原型鏈這個話題算是老生常談了,隨意一搜疾渣,網(wǎng)上大把文章來論述這個話題⊙缕看懂是一回事,理解是一回事杈女,自己寫出自己的理解又是另外一回事兒了朱浴。本文僅作為我在漫漫JS之路上的一點小理解和學習記錄吊圾,以備來日翻閱和深入更新。
知識準備
先給個經(jīng)典的person構(gòu)造函數(shù):
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
console.log(this.name);
};
}
var person1 = new Person("liyang", 26, "programmer");
var person2 = new Person("xiaoming", 24, "designer");
- 在js中翰蠢,萬物皆對象项乒,原型
Person.prototype
也是對象。 - 理解
構(gòu)造函數(shù)
梁沧、實例
和原型
的概念檀何。
構(gòu)造函數(shù)
: 構(gòu)造函數(shù)其實就是函數(shù),但任何函數(shù)如果使用 new
操作符來調(diào)用廷支,那它就可以作為構(gòu)造函數(shù)频鉴;而任何函數(shù),如果不通過 new
操作符來調(diào)用恋拍,那它跟普通函數(shù)也不會有什么兩樣垛孔。按照慣例,構(gòu)造函數(shù)始終都應(yīng)該以一個大寫字母開頭施敢,而非構(gòu)造函數(shù)則應(yīng)該使用一個小寫字母開頭周荐。
// 當作構(gòu)造函數(shù)使用
var person = new Person("liyang", 26, "programmer");
person.sayName(); // "liyang"
// 作為普通函數(shù)調(diào)用
Person("xiaoming", 24, "designer); // 添加到 window
window.sayName(); // "xiaoming"
// 在另一個對象的作用域中調(diào)用
var o = new Object();
Person.call(o, "Tommy", 3, "Baby");
o.sayName(); // "Tommy"
實例
:使用 new
操作符來創(chuàng)建Person的新實例 person1
和 person2
。
var person1 = new Person("liyang", 26, "programmer");
var person2 = new Person("xiaoming", 24, "designer");
原型
: 簡單來說僵娃, Person.prototype
就是Person原型概作。注意,原型也是對象默怨,且 所有函數(shù)的默認原型都是Object的實例
讯榕,所以 Person.prototype
同時也是Object的一個實例。
理解原型對象
先來捋一遍最基礎(chǔ)的流程:函數(shù)一創(chuàng)建先壕,就會按照一組特定的規(guī)則(暫時搞不清)來給這個函數(shù)創(chuàng)建一個 prototype
屬性瘩扼,其實就是函數(shù) Person()
創(chuàng)建了,它的原型 Person.prototype
就有了垃僚。我們再來new一個Person的實例 person1:
var person1 = new Person("liyang", 26, "programmer");
實例person1的內(nèi)部會包含一個官方ECMA5稱為 [[Prototype]]
集绰,F(xiàn)irefox、Safari和Chrome稱為 __proto__
的指針谆棺,而這個指針 __proto__
就會指向構(gòu)造函數(shù)的原型對象 Person.prototype
栽燕,注意:這個指向是存在于實例 person1
和 原型對象 Person.prototype
之間的,跟構(gòu)造函數(shù) Person()
是沒半毛錢關(guān)系的改淑。
其實到現(xiàn)在碍岔,原型鏈的基礎(chǔ)知識已經(jīng)搞定1/3了,好朵夏,現(xiàn)在我們擴展一下:實例 person1
有一個 __proto__
的指針指向了原型對象 Person.prototype
蔼啦,前面說過原型也是個實例,那它同樣也會有個 __proto__
的指針仰猖,指向了誰呢捏肢?指向了 Object.prototype
這個原型奈籽。最后 Object.prototype
原型的 __proto__
指向了原型鏈的終點 null
。 網(wǎng)上盜圖一波:
why原型?
為什么要用原型鸵赫?因為使用原型可以讓所有對象的實例共享它所包含的屬性和方法衣屏,先來個代碼:
function Person(){}
Person.prototype.name = "xiaohong";
Person.prototype.age = 25;
Person.prototype.job = "teacher";
Person.prototype.sayName = function(){
console.log(this.name);
};
var person1 = new Person();
person1.sayName(); // "xiaohong"
var person2 = new Person();
person2.sayName(); // "xiaohong"
console.log(person1.sayName == person2.sayName); // true
'
當調(diào)用 person1.sayName()
的時候,解析器會先在person1里找辩棒,找不到的話就接著在上面的 Person.prototype
里找狼忱,找到就返回,找不到就接著往上一睁,在 Object.prototype
里面找钻弄,這樣一級一級的找下去。
上面說的這些卖局,是為了簡單讓你入個門斧蜕,讓你有原型鏈的一個感覺,它就像是門縫里透出來的一絲光亮砚偶,能引導你推開原型鏈這個知識點的大門批销。
雜七雜八
Person.prototype.construtor === Person; //ture
- 使用
hasOwnProperty()
檢測一個屬性是存在于實例中,還是存在于原型中染坯。
Person.name = "xiaohong"; //ture name屬性可訪問到
Person.hasOwnProperty("name"); //false name屬性不在實例中
- 實例重新定義屬性會屏蔽原型屬性均芽,可使用
delete
操作符刪除實例屬性,但不可刪除原型上的屬性单鹿。 - 若以對象字面量的方式來封裝原型的功能掀宋,那么就相當于重寫了整個原型對象,constructor屬性指向?qū)淖冎俪梢酝ㄟ^屬性設(shè)置將constructor屬性重新指向劲妙,此時constructor屬性將會[[Enumerable]]可枚舉(默認原生constructor不可枚舉)。
function Person(){}
var person1 = new Person(); //重寫原型對象之前實例化
Person.prototype = {
construtor: Person, //重新指向Person
name: 'xiaowang',
age:'30',
sayName: function(){
alert(this.name);
}
};
var person2 = new Person(); //重寫原型對象之后實例化
- 若如上重寫原型對象儒喊,會切斷現(xiàn)有原型與任何之前已經(jīng)存在的對象實例之間的聯(lián)系镣奋,所以必須在其之后再創(chuàng)建實例。
console.log(person1.name); //undefined
console.log(person2.name); //xiaowang
- 讓原型對象等于另一個類型的實例怀愧,來實現(xiàn)原型鏈的
繼承
侨颈。
son.prototype = new Father()
//將son的原型作為Father()的實例