JavaScript編程中實(shí)現(xiàn)對(duì)象封裝特性的實(shí)例講解
來(lái)源:易賢網(wǎng) 閱讀:658 次 日期:2016-07-12 16:03:39
溫馨提示:易賢網(wǎng)小編為您整理了“JavaScript編程中實(shí)現(xiàn)對(duì)象封裝特性的實(shí)例講解”,方便廣大網(wǎng)友查閱!

JavaScript可以在一定程度上以面向?qū)ο蠓绞竭M(jìn)行編程,而封裝是面向?qū)ο笾械囊粋€(gè)重要特性,本文就來(lái)分享阮一峰老師對(duì)JavaScript編程中實(shí)現(xiàn)對(duì)象封裝特性的實(shí)例講解

1.prototype對(duì)象

1.1構(gòu)造函數(shù)的缺點(diǎn)

JavaScript通過(guò)構(gòu)造函數(shù)生成新對(duì)象,因此構(gòu)造函數(shù)可以視為對(duì)象的模板。實(shí)例對(duì)象的屬性和方法,可以定義在構(gòu)造函數(shù)內(nèi)部。

function Cat (name, color) {

 this.name = name;

 this.color = color;

}

var cat1 = new Cat('大毛', '白色');

cat1.name // '大毛'

cat1.color // '白色'

上面代碼的Cat函數(shù)是一個(gè)構(gòu)造函數(shù),函數(shù)內(nèi)部定義了name屬性和color屬性,所有實(shí)例對(duì)象都會(huì)生成這兩個(gè)屬性。但是,這樣做是對(duì)系統(tǒng)資源的浪費(fèi),因?yàn)橥粋€(gè)構(gòu)造函數(shù)的對(duì)象實(shí)例之間,無(wú)法共享屬性。

function Cat(name, color) {

 this.name = name;

 this.color = color;

 this.meow = function () {

 console.log('mew, mew, mew...');

 };

}

var cat1 = new Cat('大毛', '白色');

var cat2 = new Cat('二毛', '黑色');

cat1.meow === cat2.meow

// false

上面代碼中,cat1和cat2是同一個(gè)構(gòu)造函數(shù)的實(shí)例。但是,它們的meow方法是不一樣的,就是說(shuō)每新建一個(gè)實(shí)例,就會(huì)新建一個(gè)meow方法。這既沒有必要,又浪費(fèi)系統(tǒng)資源,因?yàn)樗衜eow方法都是同樣的行為,完全應(yīng)該共享。

1.2 prototype屬性的作用

在JavaScript語(yǔ)言中,每一個(gè)對(duì)象都有一個(gè)對(duì)應(yīng)的原型對(duì)象,被稱為prototype對(duì)象。定義在原型對(duì)象上的所有屬性和方法,都能被派生對(duì)象繼承。這就是JavaScript繼承機(jī)制的基本設(shè)計(jì)。

除了這種方法,JavaScript還提供了另一種定義實(shí)例對(duì)象的方法。我們知道,構(gòu)造函數(shù)是一個(gè)函數(shù),同時(shí)也是一個(gè)對(duì)象,也有自己的屬性和方法,其中有一個(gè)prototype屬性指向另一個(gè)對(duì)象,一般稱為prototype對(duì)象。該對(duì)象非常特別,只要定義在它上面的屬性和方法,能被所有實(shí)例對(duì)象共享。也就是說(shuō),構(gòu)造函數(shù)生成實(shí)例對(duì)象時(shí),自動(dòng)為實(shí)例對(duì)象分配了一個(gè)prototype屬性。

function Animal (name) {

 this.name = name;

}

Animal.prototype.color = "white";

var cat1 = new Animal('大毛');

var cat2 = new Animal('二毛');

cat1.color // 'white'

cat2.color // 'white'

上面代碼對(duì)構(gòu)造函數(shù)Animal的prototype對(duì)象,添加了一個(gè)color屬性。結(jié)果,實(shí)例對(duì)象cat1和cat2都帶有該屬性。

更特別的是,只要修改prototype對(duì)象,變動(dòng)就立刻會(huì)體現(xiàn)在實(shí)例對(duì)象。

Animal.prototype.color = "yellow";

cat1.color // 'yellow'

cat2.color // 'yellow'

上面代碼將prototype對(duì)象的color屬性的值改為yellow,兩個(gè)實(shí)例對(duì)象的color屬性的值立刻就跟著變了。這是因?yàn)閷?shí)例對(duì)象其實(shí)沒有color屬性,都是讀取prototype對(duì)象的color屬性。也就是說(shuō),當(dāng)實(shí)例對(duì)象本身沒有某個(gè)屬性或方法的時(shí)候,它會(huì)到構(gòu)造函數(shù)的prototype對(duì)象去尋找該屬性或方法。這就是prototype對(duì)象的特殊之處。

如果實(shí)例對(duì)象自身就有某個(gè)屬性或方法,它就不會(huì)再去prototype對(duì)象尋找這個(gè)屬性或方法。

cat1.color = 'black';

cat2.color // 'yellow'

Animal.prototype.color // "yellow";

上面代碼將實(shí)例對(duì)象cat1的color屬性改為black,就使得它不用再去prototype對(duì)象讀取color屬性,后者的值依然為yellow。

總而言之,prototype對(duì)象的作用,就是定義所有實(shí)例對(duì)象共享的屬性和方法,所以它也被稱為實(shí)例對(duì)象的原型,而實(shí)例對(duì)象可以視作從prototype對(duì)象衍生出來(lái)的。

Animal.prototype.walk = function () {

 console.log(this.name + ' is walking.');

};

上面代碼在Animal.protype對(duì)象上面定義了一個(gè)walk方法,這個(gè)方法將可以在所有Animal實(shí)例對(duì)象上面調(diào)用。

1.3原型鏈

由于JavaScript的所有對(duì)象都有構(gòu)造函數(shù),而所有構(gòu)造函數(shù)都有prototype屬性(其實(shí)是所有函數(shù)都有prototype屬性),所以所有對(duì)象都有自己的prototype原型對(duì)象。

因此,一個(gè)對(duì)象的屬性和方法,有可能是定義它自身上面,也有可能定義在它的原型對(duì)象上面(就像上面代碼中的walk方法)。由于原型本身也是對(duì)象,又有自己的原型,所以形成了一條原型鏈(prototype chain)。比如,a對(duì)象是b對(duì)象的原型,b對(duì)象是c對(duì)象的原型,以此類推。因?yàn)樽犯菰?,最源頭的對(duì)象都是從Object構(gòu)造函數(shù)生成(使用new Object()命令),所以如果一層層地上溯,所有對(duì)象的原型最終都可以上溯到Object.prototype。那么,Object.prototype有沒有原型呢?回答可以是有,也可以是沒有,因?yàn)镺bject.prototype的原型是沒有任何屬性和方法的null。

Object.getPrototypeOf(Object.prototype)

// null

上面代碼表示Object.prototype對(duì)象的原型是null,由于null沒有任何屬性,所以原型鏈到此為止。

“原型鏈”的作用在于,當(dāng)讀取對(duì)象的某個(gè)屬性時(shí),JavaScript引擎先尋找對(duì)象本身的屬性,如果找不到,就到它的原型去找,如果還是找不到,就到原型的原型去找。以此類推,如果直到最頂層的Object.prototype還是找不到,則返回undefined。

舉例來(lái)說(shuō),如果讓某個(gè)函數(shù)的prototype屬性指向一個(gè)數(shù)組,就意味著該函數(shù)可以用作數(shù)組的構(gòu)造函數(shù),因?yàn)樗傻膶?shí)例對(duì)象都可以通過(guò)prototype屬性調(diào)用數(shù)組方法。

function MyArray (){}

MyArray.prototype = new Array();

MyArray.prototype.constructor = MyArray;

var mine = new MyArray();

mine.push(1, 2, 3);

mine.length // 3

mine instanceof Array // true

上面代碼的mine是MyArray的實(shí)例對(duì)象,由于MyArray的prototype屬性指向一個(gè)數(shù)組,使得mine可以調(diào)用數(shù)組方法(這些方法其實(shí)定義在數(shù)組的prototype對(duì)象上面)。至于最后那行instanceof表達(dá)式,我們知道instanceof運(yùn)算符用來(lái)比較一個(gè)對(duì)象是否為某個(gè)構(gòu)造函數(shù)的實(shí)例,最后一行表示mine為Array的實(shí)例。

mine instanceof Array

// 等同于

(Array === MyArray.prototype.constructor) ||

(Array === Array.prototype.constructor) ||

(Array === Object.prototype.constructor )

上面代碼說(shuō)明了instanceof運(yùn)算符的實(shí)質(zhì),它依次與實(shí)例對(duì)象的所有原型對(duì)象的constructor屬性(關(guān)于該屬性的介紹,請(qǐng)看下一節(jié))進(jìn)行比較,只要有一個(gè)符合就返回true,否則返回false。

1.4 constructor屬性

prototype對(duì)象有一個(gè)constructor屬性,默認(rèn)指向prototype對(duì)象所在的構(gòu)造函數(shù)。

function P() {}

P.prototype.constructor === P

// true

由于constructor屬性定義在prototype對(duì)象上面,意味著可以被所有實(shí)例對(duì)象繼承。

function P() {}

var p = new P();

p.constructor

// function P() {}

p.constructor === P.prototype.constructor

// true

p.hasOwnProperty('constructor')

// false

上面代碼表示p是構(gòu)造函數(shù)P的實(shí)例對(duì)象,但是p自身沒有contructor屬性,該屬性其實(shí)是讀取原型鏈上面的P.prototype.constructor屬性。

constructor屬性的作用是分辨prototype對(duì)象到底定義在哪個(gè)構(gòu)造函數(shù)上面。

function F(){};

var f = new F();

f.constructor === F // true

f.constructor === RegExp // false

上面代碼表示,使用constructor屬性,確定變量f的構(gòu)造函數(shù)是F,而不是RegExp。

2.Object.getPrototypeOf方法

Object.getPrototypeOf方法返回一個(gè)對(duì)象的原型。

// 空對(duì)象的原型是Object.prototype

Object.getPrototypeOf({}) === Object.prototype

// true

// 函數(shù)的原型是Function.prototype

function f() {}

Object.getPrototypeOf(f) === Function.prototype

// true

// 假定F為構(gòu)造函數(shù),f為F的實(shí)例對(duì)象

// 那么,f的原型是F.prototype

var f = new F();

Object.getPrototypeOf(f) === F.prototype

// true

3.Object.create方法

Object.create方法用于生成新的對(duì)象,可以替代new命令。它接受一個(gè)對(duì)象作為參數(shù),返回一個(gè)新對(duì)象,后者完全繼承前者的屬性,即前者成為后者的原型。

var o1 = { p: 1 };

var o2 = Object.create(o1);

o2.p // 1

上面代碼中,Object.create方法在o1的基礎(chǔ)上生成了o2。此時(shí),o1成了o2的原型,也就是說(shuō),o2繼承了o1所有的屬性的方法。

Object.create方法基本等同于下面的代碼,如果老式瀏覽器不支持Object.create方法,可以用下面代碼自己部署。

if (typeof Object.create !== "function") {

 Object.create = function (o) {

 function F() {}

 F.prototype = o;

 return new F();

 };

}

上面代碼表示,Object.create方法實(shí)質(zhì)是新建一個(gè)構(gòu)造函數(shù)F,然后讓F的prototype屬性指向作為原型的對(duì)象o,最后返回一個(gè)F的實(shí)例,從而實(shí)現(xiàn)讓實(shí)例繼承o的屬性。

下面三種方式生成的新對(duì)象是等價(jià)的。

var o1 = Object.create({});

var o2 = Object.create(Object.prototype);

var o3 = new Object();

如果想要生成一個(gè)不繼承任何屬性(比如toString和valueOf方法)的對(duì)象,可以將Object.create的參數(shù)設(shè)為null。

var o = Object.create(null);

o.valueOf()

// TypeError: Object [object Object] has no method 'valueOf'

上面代碼表示,如果對(duì)象o的原型是null,它就不具備一些定義在Object.prototype對(duì)象上面的屬性,比如valueOf方法。

使用Object.create方法的時(shí)候,必須提供對(duì)象原型,否則會(huì)報(bào)錯(cuò)。

Object.create()

// TypeError: Object prototype may only be an Object or null

Object.create方法生成的新對(duì)象,動(dòng)態(tài)繼承了原型。在原型上添加或修改任何方法,會(huì)立刻反映在新對(duì)象之上。

var o1 = { p: 1 };

var o2 = Object.create(o1);

o1.p = 2;

o2.p

// 2

上面代碼表示,修改對(duì)象原型會(huì)影響到新生成的對(duì)象。

除了對(duì)象的原型,Object.create方法還可以接受第二個(gè)參數(shù),表示描述屬性的attributes對(duì)象,跟用在Object.defineProperties方法的格式是一樣的。它所描述的對(duì)象屬性,會(huì)添加到新對(duì)象。

var o = Object.create(Object.prototype, {

 p1: { value: 123, enumerable: true },

 p2: { value: "abc", enumerable: true }

});

o.p1 // 123

o.p2 // "abc"

由于Object.create方法不使用構(gòu)造函數(shù),所以不能用instanceof運(yùn)算符判斷,對(duì)象是哪一個(gè)構(gòu)造函數(shù)的實(shí)例。這時(shí),可以使用下面的isPrototypeOf方法,判讀原型是哪一個(gè)對(duì)象。

4.isPrototypeOf方法

isPrototypeOf方法用來(lái)判斷一個(gè)對(duì)象是否是另一個(gè)對(duì)象的原型。

var o1 = {};

var o2 = Object.create(o1);

var o3 = Object.create(o2);

o2.isPrototypeOf(o3) // true

o1.isPrototypeOf(o3) // true

上面代碼表明,只要某個(gè)對(duì)象處在原型鏈上,isProtypeOf都返回true。

5.一個(gè)簡(jiǎn)單示例

var ClassDemo = function () {

 //靜態(tài)private變量

 var private_static_var = 'aaaa';

 //靜態(tài)private方法

 var private_static_func = function (key) { 

  return key + private_static_var;

 }

 //private方法,關(guān)鍵就是第一參數(shù)self要傳入this

 var private_func = function (self, key) { 

  return private_static_func(key + self.id);

 }

 var _class = function (id) { //構(gòu)造函數(shù)

  this.id = id; //public變量

 }

 //public方法

 _class.prototype.public_func = function (key) { 

  return private_func(this, key);

 }

 return _class;

}();

var a = new ClassDemo('hello world');

alert(a.public_func('world hello'));

沒有簡(jiǎn)單的辦法實(shí)現(xiàn)private變量和public靜態(tài)變量/方法,不過(guò)封裝到這個(gè)程度夠用了

更多信息請(qǐng)查看網(wǎng)絡(luò)編程
由于各方面情況的不斷調(diào)整與變化,易賢網(wǎng)提供的所有考試信息和咨詢回復(fù)僅供參考,敬請(qǐng)考生以權(quán)威部門公布的正式信息和咨詢?yōu)闇?zhǔn)!

2025國(guó)考·省考課程試聽報(bào)名

  • 報(bào)班類型
  • 姓名
  • 手機(jī)號(hào)
  • 驗(yàn)證碼
關(guān)于我們 | 聯(lián)系我們 | 人才招聘 | 網(wǎng)站聲明 | 網(wǎng)站幫助 | 非正式的簡(jiǎn)要咨詢 | 簡(jiǎn)要咨詢須知 | 新媒體/短視頻平臺(tái) | 手機(jī)站點(diǎn) | 投訴建議
工業(yè)和信息化部備案號(hào):滇ICP備2023014141號(hào)-1 云南省教育廳備案號(hào):云教ICP備0901021 滇公網(wǎng)安備53010202001879號(hào) 人力資源服務(wù)許可證:(云)人服證字(2023)第0102001523號(hào)
云南網(wǎng)警備案專用圖標(biāo)
聯(lián)系電話:0871-65099533/13759567129 獲取招聘考試信息及咨詢關(guān)注公眾號(hào):hfpxwx
咨詢QQ:1093837350(9:00—18:00)版權(quán)所有:易賢網(wǎng)
云南網(wǎng)警報(bào)警專用圖標(biāo)