新聞中心
1.寫在前面
對(duì)于初學(xué)js的繼承機(jī)制--”原型“和”原型鏈“這兩個(gè)概念的理論時(shí),總是忘了記、記了忘。所以死記硬背真的是沒(méi)得用的,得深入理解其背后的設(shè)計(jì)思想,得理解加記憶,如虎添翼。

至于為什么這樣說(shuō),就隨著這篇文章去揭開(kāi)珍妮的面紗,如剝洋蔥般去探究它的本質(zhì)。
來(lái)不及解釋,快上車。
2.JS繼承的設(shè)計(jì)思想
我們知道創(chuàng)建對(duì)象有兩種方式:一種是最常見(jiàn)的對(duì)象字面量,一種就是常說(shuō)的通過(guò)new來(lái)創(chuàng)建對(duì)象實(shí)例。其實(shí)這兩種方式描述的對(duì)象都是等價(jià)的,屬性和方法都是一致的。
// 字面量對(duì)象
let obj = {
name:"yichuan",
age:18,
sayName(){
console.log("name: ", this.name);
}
}
// new創(chuàng)建對(duì)象實(shí)例
let obj2 = new Object();
obj2.name = "pingping";
obj2.sayName = function(){
console.log("name: ", this.name);
}
使用對(duì)象字面量或者Object構(gòu)造函數(shù)可以輕松創(chuàng)建對(duì)象,但是在創(chuàng)建具有同樣接口的多個(gè)對(duì)象時(shí),會(huì)重復(fù)編寫很多代碼。那么,我們想可不可以創(chuàng)建一個(gè)容器,將共享的屬性和方法存在里面,這樣就可以在多個(gè)對(duì)象中使用。
在es6之前沒(méi)有正式支持類和繼承的結(jié)構(gòu),但是能夠通過(guò)原型鏈繼承進(jìn)行模仿實(shí)現(xiàn)類和繼承。事實(shí)上,es6的類也的確是封裝了構(gòu)造函數(shù)和原型繼承的語(yǔ)法糖。
工廠模式
工廠模式可以用于抽象創(chuàng)建特定對(duì)象過(guò)程,解決創(chuàng)建多個(gè)類似對(duì)象的問(wèn)題,但是沒(méi)有解決對(duì)象標(biāo)識(shí)的問(wèn)題,不能設(shè)置新創(chuàng)建對(duì)象的類型。
// 工廠模式
function createPerson(name, age, city){
let obj = new Object();
obj.name = name;
obj.age = age;
obj.city = city;
obj.sayName = function(){
console.log("my name is : ", this.name);
}
return obj;
}
let preson1 = createPerson("yichuan",18,"BeiJing");
let preson2 = createPerson("onechuan",28,"GuangZhou");
構(gòu)造函數(shù)模式
在js中構(gòu)造函數(shù)是用于創(chuàng)建特定類型對(duì)象的,在前面使用了Object原生構(gòu)造函數(shù)創(chuàng)建對(duì)象,運(yùn)行時(shí)可以直接在執(zhí)行環(huán)境中使用。但其實(shí),我們也可以進(jìn)行自定義構(gòu)造函數(shù),用函數(shù)的形式為自己的對(duì)象定義屬性和方法。
// 構(gòu)造函數(shù)模式
function Person(name, age, city){
this.name = name;
this.age = age;
this.city = city;
this.sayName = function(){
console.log(this.name);
}
}
let p1 = new Person("yichuan",18,"Beijing")
let p2 = new Person("onechuan",19,"Guangzhou")
p1.sayName()//yichuan
p2.sayName()//onechuan
構(gòu)造函數(shù)模式相比于工廠模式而言,沒(méi)有顯式創(chuàng)建對(duì)象,屬性和方法都是直接賦給this,也沒(méi)有return返回任何值。
那么使用new構(gòu)造函數(shù)的方式創(chuàng)建對(duì)象,具體會(huì)發(fā)生什么?
會(huì)有如下操作:
1)在內(nèi)存中開(kāi)辟新的空間創(chuàng)建新對(duì)象。
2)這個(gè)新對(duì)象內(nèi)部的_proto_特性被賦值為構(gòu)造函數(shù)的prototype屬性。
3)構(gòu)造函數(shù)內(nèi)部的this指向新對(duì)象。
4)給新對(duì)象添加屬性。
5)如果構(gòu)造函數(shù)返回非空對(duì)象,則返回該對(duì)象;否則,返回剛創(chuàng)建的新對(duì)象。
構(gòu)造函數(shù)是什么?
其實(shí)構(gòu)造函數(shù)也是函數(shù),和普通函數(shù)沒(méi)啥區(qū)別,只是調(diào)用的方式不同而已,通過(guò)new調(diào)用的函數(shù)是構(gòu)造函數(shù)。構(gòu)造函數(shù)定義的方法會(huì)在每個(gè)實(shí)例上都創(chuàng)建一遍,每次定義函數(shù)時(shí),都會(huì)初始化一個(gè)對(duì)象。
function Person(name, age, city){
this.name = name;
this.age = age;
this.city = city;
this.sayName = new Function("console.log(this.name);");
}原型模式
每個(gè)函數(shù)都會(huì)創(chuàng)建一個(gè)prototype屬性,這個(gè)屬性是一個(gè)對(duì)象,包含應(yīng)該由特定引用類型的實(shí)例共享的屬性和方法,而這個(gè)對(duì)象就是通過(guò)調(diào)用構(gòu)造函數(shù)創(chuàng)建的實(shí)例對(duì)象的原型,那么這個(gè)對(duì)象就叫做原型對(duì)象。
原型對(duì)象的作用是:在原型對(duì)象上定義的屬性和方法可以被對(duì)象實(shí)例所共享,即對(duì)象原型相當(dāng)于一個(gè)存儲(chǔ)公共屬性和方法的容器。
等等,這不就是前面所說(shuō)的構(gòu)造器中直接賦值給對(duì)象實(shí)例的值嗎?
其實(shí)不是,其實(shí)在進(jìn)行構(gòu)造函數(shù)Person定義時(shí),構(gòu)造函數(shù)內(nèi)部是個(gè)空對(duì)象,沒(méi)有任何屬性和方法。然而,可以通過(guò)在Person的prototype上直接定義屬性和方法,來(lái)掛載到Person對(duì)象的原型上。這樣通過(guò)new Person()得到的對(duì)象實(shí)例是可以共享Person.prototype上的屬性和方法。如下所示:
function Person(){}
Person.prototype.name = "yichuan";
Person.prototype.age = 18;
Person.prototype.city = "Beijing";
Person.prototype.sayName = function(){
console.log(this.name);
}
let p1 = new Person();
p1.sayName();
let p2 = new Person();
p2.sayName();3.原型和原型鏈
上面的原型模式中,已經(jīng)將原型和原型對(duì)象的概念引出來(lái)了,那么我們重新整理下思路:
構(gòu)造函數(shù)的prototype屬性指向的原型對(duì)象中,定義了所有實(shí)例對(duì)象都能夠共享的屬性和方法,而不需要共享的屬性和方法則直接定義在構(gòu)造函數(shù)上。
通過(guò)構(gòu)造函數(shù)創(chuàng)建的實(shí)例對(duì)象,會(huì)自動(dòng)擁有原型對(duì)象上共享的屬性或方法。
function Person(name){
this.name = name;
}
Person.prototype.address = "earth";
const p = new Person("yichuan");//{name:age}
const p1 = new Person("onechuan");在上面代碼中,在構(gòu)造函數(shù)Person的prototype原型上定義了一個(gè)公共屬性 Person.prototype.address="earth";,那么通過(guò)new出來(lái)的實(shí)例對(duì)象p和p1都會(huì)天生繼承屬性address,而p和p1各自的name值分別為"yichuan"和"onechuan"。
函數(shù)與對(duì)象的關(guān)系
- 函數(shù)是對(duì)象,對(duì)象都是通過(guò)函數(shù)創(chuàng)建的
- 函數(shù)與對(duì)象并不是簡(jiǎn)單的包含與被包含的關(guān)系
原型的類別
- 顯式原型:prototype,是每個(gè)函數(shù)function獨(dú)有的屬性
- 隱式原型:_proto_,是每個(gè)對(duì)象都具有的屬性
原型和原型鏈
- 原型:一個(gè)函數(shù)可以看做一個(gè)類,原型是所有類都有的一個(gè)屬性,prototype原型的作用就是給這個(gè)類的每個(gè)對(duì)象都添加一個(gè)統(tǒng)一的方法。
- 原型鏈:每個(gè)對(duì)象都有一個(gè)_proto_,它指向它的prototype原型對(duì)象;它的prototype原型對(duì)象又有一個(gè)_proto_,指向它的prototype原型對(duì)象,就這樣向上查找原型,直到頂級(jí)對(duì)象Object.prototype,最終指向是null。
用圖片描述原型鏈:
我們看到原型鏈的最終歸屬都是對(duì)象,而Object.prototype的_proto_指向的是null,這是為了避免死循環(huán)而設(shè)置的,所以一切皆空。
4.參考文章
【重點(diǎn)】圖解:告訴面試官什么是 JS 原型和原型鏈?
面不面試的,你都得懂原型和原型鏈
《Javascript高級(jí)程序設(shè)計(jì)》
5.寫在最后
所有實(shí)例對(duì)象的_proto_都指向該構(gòu)造函數(shù)的prototype原型對(duì)象 (即:p._proto_ === Person.prototype)。
所有函數(shù)(包括構(gòu)造函數(shù))都是Function的實(shí)例,所有函數(shù)的_proto_都指向Function的原型對(duì)象。
所有的原型對(duì)象(包括 Function的原型對(duì)象)都是Object的實(shí)例,所以_proto_都指向 Object(構(gòu)造函數(shù))的原型對(duì)象。而Object構(gòu)造函數(shù)的 _proto_指向 null。
Function構(gòu)造函數(shù)本身就是Function的實(shí)例,所以_proto_指向Function的原型對(duì)象。
網(wǎng)頁(yè)題目:快醒醒,帶你穿過(guò)原型和原型鏈的迷霧
網(wǎng)站地址:http://www.dlmjj.cn/article/dpigjcp.html


咨詢
建站咨詢
