我們創(chuàng)建的每個(gè)函數(shù)都有一個(gè) prototype (原型)屬性,這個(gè)屬性是一個(gè)指針,指向一個(gè)原型對(duì)象,而這個(gè)原型對(duì)象中擁有的屬性和方法可以被所以實(shí)例共享。
function Person(){}Person.prototype.name = "Nicholas";Person.prototype.age = 29;Person.prototype.sayName = function(){alert(this.name);};var person1 = new Person();person1.sayName(); //"Nicholas"var person2 = new Person();person2.sayName(); //"Nicholas"alert(person1.sayName == person2.sayName); //true一、理解原型對(duì)象
無(wú)論什么時(shí)候,只要?jiǎng)?chuàng)建了一個(gè)新函數(shù),就會(huì)根據(jù)一組特定的規(guī)則為該函數(shù)創(chuàng)建一個(gè) prototype屬性,這個(gè)屬性指向函數(shù)的原型對(duì)象。
在默認(rèn)情況下,所有原型對(duì)象都會(huì)自動(dòng)獲得一個(gè) constructor(構(gòu)造函數(shù))屬性,這個(gè)屬性包含一個(gè)指向 prototype 屬性所在函數(shù)的指針。
當(dāng)調(diào)用構(gòu)造函數(shù)創(chuàng)建一個(gè)新實(shí)例后,該實(shí)例的內(nèi)部將包含一個(gè)指針(內(nèi)部屬性),指向構(gòu)造函數(shù)的原型對(duì)象。ECMA-262 第 5 版中管這個(gè)指針叫 [[Prototype]] 。
雖然在腳本中沒(méi)有標(biāo)準(zhǔn)的方式訪(fǎng)問(wèn) [[Prototype]] ,但 Firefox、Safari 和 Chrome 在每個(gè)對(duì)象上都支持一個(gè)屬性__proto__ ;而在其他實(shí)現(xiàn)中,這個(gè)屬性對(duì)腳本則是完全不可見(jiàn)的。
不過(guò),要明確的真正重要的一點(diǎn)就是,這個(gè)連接存在于實(shí)例與構(gòu)造函數(shù)的原型對(duì)象之間,而不是存在于實(shí)例與構(gòu)造函數(shù)之間。
以前面使用 Person 構(gòu)造函數(shù)和 Person.prototype 創(chuàng)建實(shí)例的代碼為例,圖 6-1 展示了各個(gè)對(duì)象之間的關(guān)系。

在此, Person.prototype 指向了原型對(duì)象,而 Person.prototype.constructor 又指回了 Person 。
person1 和 person2 都包含一個(gè)內(nèi)部屬性,該屬性?xún)H僅指向了 Person.prototype ;換句話(huà)說(shuō),它們與構(gòu)造函數(shù)沒(méi)有直接的關(guān)系。
可以調(diào)用 person1.sayName() 。這是通過(guò)查找對(duì)象屬性的過(guò)程來(lái)實(shí)現(xiàn)的。(會(huì)先在實(shí)例上搜索,如果搜索不到就會(huì)繼續(xù)搜索原型。)
用isPrototypeOf()方法判斷實(shí)例與原型對(duì)象之間的關(guān)系alert(Person.prototype.isPrototypeOf(person1)); //truealert(Person.prototype.isPrototypeOf(person2)); //true用Object.getPrototypeOf() 方法返回實(shí)例的原型對(duì)象alert(Object.getPrototypeOf(person1) == Person.prototype); //true使用 hasOwnProperty() 方法可以檢測(cè)一個(gè)屬性是存在于實(shí)例中,還是存在于原型中。alert(person1.hasOwnProperty("name")); //false 來(lái)著原型person1.name = "Greg";alert(person1.name); //"Greg"——來(lái)自實(shí)例alert(person1.hasOwnProperty("name")); //true二、更簡(jiǎn)單的原型語(yǔ)法
前面例子中每添加一個(gè)屬性和方法就要敲一遍 Person.prototype 。為減少不必要的輸入,也為了從視覺(jué)上更好地封裝原型的功能,更常見(jiàn)的做法是用一個(gè)包含所有屬性和方法的對(duì)象字面量來(lái)重寫(xiě)整個(gè)原型對(duì)象。
function Person(){}Person.prototype = { name : "Nicholas", age : 29, job: "Software Engineer", sayName : function () { alert(this.name); }};在上面的代碼中,我們將 Person.prototype 設(shè)置為等于一個(gè)以對(duì)象字面量形式創(chuàng)建的新對(duì)象。最終結(jié)果相同,但有一個(gè)例外: constructor 屬性不再指向 Person 了。
前面曾經(jīng)介紹過(guò),每創(chuàng)建一個(gè)函數(shù),就會(huì)同時(shí)創(chuàng)建它的 prototype 對(duì)象,這個(gè)對(duì)象也會(huì)自動(dòng)獲得 constructor 屬性。
var friend = new Person();alert(friend instanceof Object); //truealert(friend instanceof Person); //truealert(friend.constructor == Person); //falsealert(friend.constructor == Object); //true
在此,用 instanceof 操作符測(cè)試 Object 和 Person 仍然返回 true ,但 constructor 屬性則等于 Object 而不等于 Person 了。
如果 constructor 的值真的很重要,可以像下面這樣特意將它設(shè)置回適當(dāng)?shù)闹怠?/p>
function Person(){}Person.prototype = { constructor : Person, name : "Nicholas", age : 29, job: "Software Engineer", sayName : function () { alert(this.name); }}; 三、原生對(duì)象的原型
所有原生引用類(lèi)型( Object 、 Array 、 String ,等等)都在其構(gòu)造函數(shù)的原型上定義了方法。
例如,在 Array.prototype 中可以找到 sort() 方法,而在 String.prototype 中可以找到substring() 方法。盡管可以這樣做,但不推薦修改原生對(duì)象的原型。
四、原型對(duì)象的問(wèn)題
原型模式的最大問(wèn)題是由其共享的本性所導(dǎo)致的。 修改其中的一個(gè),另一個(gè)也會(huì)受影響。
function Person(){}Person.prototype = {constructor: Person,name : "Nicholas",age : 29,job : "Software Engineer",friends : ["Shelby", "Court"],sayName : function () {alert(this.name);}};var person1 = new Person();var person2 = new Person();person1.friends.push("Van");alert(person1.friends); //"Shelby,Court,Van"alert(person2.friends); //"Shelby,Court,Van"alert(person1.friends === person2.friends); //true五、原型鏈
其基本思想是利用原型讓一個(gè)引用類(lèi)型繼承另一個(gè)引用類(lèi)型的屬性和方法。然后層層遞進(jìn),就構(gòu)成了實(shí)例與原型的鏈條,這就是所謂原型鏈的基本概念。
function SuperType(){ this.property = true;}SuperType.prototype.getSuperValue = function(){ return this.property;};function SubType(){ this.subproperty = false;}//繼承了 SuperTypeSubType.prototype = new SuperType();SubType.prototype.getSubValue = function (){ return this.subproperty;};var instance = new SubType();alert(instance.getSuperValue()); //true一張圖說(shuō)明:

property 則位于 SubType.prototype 中。這是因?yàn)?property 是一個(gè)實(shí)例屬性,而 getSuperValue() 則是一個(gè)原型方法。既然 SubType.prototype 現(xiàn)在是 SuperType的實(shí)例,那么 property 當(dāng)然就位于該實(shí)例中了。
新聞熱點(diǎn)
疑難解答