原型和原型鏈都還沒有弄明白javascript算是白學(xué)了!

極客小俊
你不用拼過聰明人,你只需要拼過那些懶人 你就一定會(huì)超越大部分人!

首先我們了解一下普通對(duì)象 與 函數(shù)對(duì)象
我們?cè)趯W(xué)習(xí)原型之前首先了解一下javascript當(dāng)中的 普通對(duì)象 和 函數(shù)對(duì)象,
如圖1

普通對(duì)象:
最普通的對(duì)象:具有_ _ proto_ _這個(gè)屬性它(指向其原型鏈),注意: 普通的對(duì)象是沒有prototype這個(gè)屬性的! 如果你調(diào)用必然返回undefined
原型對(duì)象: 我們會(huì)在下面繼續(xù)說明!
javascript中哪些情況屬于普通對(duì)象 如下代碼:
?//普通對(duì)象 ?
?function Test() {
?
?}
?var obj1 = new Test();
?var obj2 = new Object();
?var obj3 = {};
?
?
?console.log(typeof obj1); ? ?//Object
?console.log(typeof obj2); ? ?//Object
?console.log(typeof obj3); ? ?//Object
函數(shù)對(duì)象:
凡是用Function()創(chuàng)建的都是函數(shù)對(duì)象。 比如:自定義函數(shù), ?事件函數(shù) 系統(tǒng)的Object、Array、Date、String、RegEx 以上都屬于 函數(shù)對(duì)象
小提示: 以上的都是Function的實(shí)例對(duì)象, 那么也只有實(shí)例才會(huì)有_ _ proto_ _ 這個(gè)屬性
注意: ?這里的Function是比較特殊的函數(shù)對(duì)象, 因?yàn)镕unction.prototype本身它應(yīng)該指向是原型對(duì)象, 但Function的prototype卻是函數(shù)對(duì)象 ?上圖已經(jīng)有說明!
javascript中哪些情況屬于函數(shù)對(duì)象 如下代碼:
?//函數(shù)對(duì)象 ?
?
?function F1(){
? ? ?
?}
?
?var F2 = function(){
? ? ?
?}
?var F3 = function(a,b){
? ? ?
?}
?
?
?window.onload=function () {
? ? ?var div1=document.getElementById('div1');
? ? ?div1.onclick=function () {
? ? ? ? ?alert(1);
? ? ?}
? ? ?console.log(typeof div1.onclick); ? //function ?
?}
?
?
?console.log(typeof F1); ? ??//function ?
?console.log(typeof F2); ? ??//function ?
?console.log(typeof F3); ? ? ? //function ?
?
?console.log(typeof Object); ? //function ?
?console.log(typeof Array); ? ?//function ?
?console.log(typeof String); ? //function ?
?console.log(typeof Date); ? ? //function ?
?console.log(typeof RegEx); ? ?//function ?
?console.log(typeof Function); //function ?
函數(shù)對(duì)象都是Function的實(shí)例對(duì)象
就如同Array是通過Function創(chuàng)建出來的。
因?yàn)锳rray是Function的實(shí)例,是實(shí)例就會(huì)有._ _ proto_ _ ?這個(gè)屬性, 從上面的流程圖上看 一個(gè)函數(shù)對(duì)象的_ _ proto_ _屬性值是: ? () { [native code] }
而特殊的Function.prototype的值也是一個(gè): ? () { [native code] }
所以我們可以推斷出一個(gè)條件: ?函數(shù)對(duì)象._ _ proto_ _ === Function.prototype ? ? ?是成立的! ?返回true
由此引出下面判斷條件:
?Array._ _proto_ _ ?== Function.prototype ? ? //true
?
?String._ _proto_ _ == Function.prototype ? ? //true
?
?RegExp._ _proto_ _== Function.prototype ? ? //true
?
?Date._ _proto_ _ ?== Function.prototype ? ? //true
? () { [native code] } ?是什么?
native code 的意思是它是程序自帶的,是二進(jìn)制編譯的無法顯示出來代碼, ?native code是本地代碼, 這里我們就簡(jiǎn)單的解釋一下即可!
以上內(nèi)容作為學(xué)習(xí)原型之前的鋪墊了解即可!! ?接下來我們就慢慢的拆解圖1的細(xì)節(jié)內(nèi)容!!
引出兩個(gè)問題
問題1 性能方面
如果創(chuàng)建一個(gè)對(duì)象 在內(nèi)存的堆區(qū)中就會(huì)開辟一個(gè)空間來保存對(duì)象,如果每個(gè)對(duì)象里面有相同的方法也會(huì)被創(chuàng)建出來
這樣就存在一個(gè)問題,就是公共的方法或者屬性會(huì)存在內(nèi)存當(dāng)中n份.. ?, 大量的占用了內(nèi)存開銷!
如圖2
每一個(gè)對(duì)象都生成了同樣的say()方法, ?這種代碼中如果每個(gè)對(duì)象都有公共一樣的方法 就顯得很占據(jù)內(nèi)存空間!

上圖的代碼如下
?function Person(name,age){
? ? ?this.name=name;
? ? ?this,age=age;
? ? ?this.say=function () {
? ? ? ? ?console('輸出結(jié)果');
? ? ?}
?}
?
?var obj1=new Person();
?var obj2=new Person();
?var obj3=new Person();
?var obj4=new Person();
?var obj5=new Person();
問題2 屬性 方法 不能共享!
有時(shí)候我們希望一個(gè)方法能夠被多個(gè)相同對(duì)象類型都可以公共的進(jìn)行使用!
例如: 定義一個(gè)數(shù)組私有方法, 而另外一個(gè)數(shù)組對(duì)象是不能訪問這個(gè)私有方法的
代碼案例:
?var arr=[5,5,10];
?//arr數(shù)組對(duì)象的sum方法
?arr.sum=function () {
? ? ?var result=0;
? ? ?for(var i=0;i<this.length;i++){
? ? ? ? ?result+=this[i];
? ? ?}
? ? ?return result;
?}
?
?console.log(arr.sum()); ?//結(jié)果: 20
?
?var arr2=[10,10,10];
?console.log(arr2.sum()); //結(jié)果: Uncaught TypeError: arr2.sum is not a function
解答: 這里報(bào)錯(cuò)是因?yàn)?arr2 這個(gè)數(shù)組對(duì)象 根本就不存在sum() 這個(gè)方法, ?它是屬于arr數(shù)組對(duì)象私有的一個(gè)方法!
所以有時(shí)候我們希望一個(gè)方法能夠被多個(gè)相同對(duì)象類型都可以公共的來使用!
以上兩個(gè)問題 , 問題1是性能優(yōu)化不足,問題2是私有方法不能被相同類型的對(duì)象調(diào)用 , ?所以解決上述問題方法或?qū)傩圆荒芄蚕淼霓k法 就要用到: 原型 ?[提高性能] 也就是通常說的: 原型模式
接下來我們就來探討一下原型是什么!
原型對(duì)象是什么!
根據(jù)圖1 函數(shù)對(duì)象都有一個(gè)prototype屬性指向的是一個(gè)原型對(duì)象, 那么我們可以推出以下概念:
每創(chuàng)建一個(gè)函數(shù)都會(huì)有一個(gè)prototype屬性,這個(gè)屬性是一個(gè)指針,指向一個(gè)對(duì)象(原型對(duì)象)
原型對(duì)象,也就是(構(gòu)造函數(shù).prototype), ?當(dāng)中含有一個(gè)constructor 屬性 這個(gè)屬性(指向的就是當(dāng)前原型對(duì)象的構(gòu)造函數(shù))
如下圖:

原型對(duì)象作用:是包含特定類型的所有實(shí)例共享的屬性和方法, 就是說你把屬性和方法定義在原型對(duì)象里面之后,那么這個(gè)類型的實(shí)例就都會(huì)共享這些屬性和方法!
原型對(duì)象優(yōu)點(diǎn): 就是可以讓所有實(shí)例對(duì)象共享它所包含的屬性和方法。
原型對(duì)象的語法基礎(chǔ)
要特定類型的所有實(shí)例都共享的屬性和方法, 就要把它們定義在原型對(duì)象的下面!
原型: 要使用prototype這個(gè)關(guān)鍵字, ?要寫在構(gòu)造函數(shù)的下面:
?//語法如下
?
?構(gòu)造函數(shù)名.prototype.屬性=值;
?
?構(gòu)造函數(shù)名.prototype.方法=function(){
? ?..代碼段..
?}
如下圖: 所以用: 構(gòu)造函數(shù)名.prototype 你就可以把屬性和方法定義在原型對(duì)象當(dāng)中

案例代碼:
?function createPerson(name,age) {
? ? ?this.name=name;
? ? ?this,age=age;
?}
?
?createPerson.prototype.say=function () {
? ? ?console.log('我的名字叫'+this.name);
?}
?
?var a=new createPerson('張三','33');
?var b=new createPerson('李四','55');
?var c=new createPerson('王武','66');
?
?a.say();
?b.say();
?c.say();
為了方便理解 我畫了一張圖, 以上代碼的流程圖分析如下圖:

特殊的Function.prototype
Function.prototype是個(gè)例外,為什么說它是一個(gè)例外呢? ? 按道理來說它這里獲取出來的應(yīng)該是一個(gè)原型對(duì)象,但卻是一個(gè)函數(shù)對(duì)象,
作為一個(gè)函數(shù)對(duì)象,它又沒有prototype屬性。 ?從圖1中就可以看出來這個(gè)道理!
你用Function.prototype 獲取出來是一個(gè): ? () { [native code] } ?這個(gè)東西是什么上面已經(jīng)解釋過了!
如下圖:

原型知識(shí)點(diǎn)
為了節(jié)省內(nèi)存,我們把對(duì)象的方法都放在原型里面。為什么呢?
因?yàn)楦膶憣?duì)象下面公用的方法或者屬性、讓公用的方法或者屬性在內(nèi)存中只存在一份
在我們通過new實(shí)例化對(duì)象的時(shí)候,在內(nèi)存中,會(huì)自動(dòng)拷貝構(gòu)造函數(shù)中的所有屬性和方法,用來給實(shí)例對(duì)象賦值,而不管我們實(shí)例化多少次,原型里面的屬性和方法只生成一次,所以會(huì)節(jié)省內(nèi)存。
普通定義方式與原型定義的優(yōu)先級(jí)高低
如下代碼:
?function createPerson(name,age) {
? ? ? ? ? ? ?this.name=name;
? ? ? ? ? ? ?this,age=age;
? ? ? ? ?}
?
?createPerson.prototype.say=function () {
? ? ?console.log('我的名字叫'+this.name);
?}
?
?var a=new createPerson('張三','33');
?var b=new createPerson('李四','55');
?var c=new createPerson('王武','66');
?
?a.say();
?b.say();
?c.say();
?
?//普通定義的優(yōu)先級(jí)高于原型prototype
?c.say=function(){
? ? ?console.log('輸出ok');
?}
? ? ? ? ?
?c.say();
?
所以以上的普通定義的方式要比原型定義的方式的優(yōu)先級(jí)高!,但這并不是把原型覆蓋了 只是優(yōu)先調(diào)用普通定義的方法
如圖:

原型的中的 _ _ proto_ _
首先回顧一下, 實(shí)例化new的時(shí)候,系統(tǒng)會(huì)在內(nèi)存中創(chuàng)建了一個(gè)空的對(duì)象,就像是這樣 var p = {} , 拷貝構(gòu)造函數(shù)中的屬性和方法到空對(duì)象中。
重點(diǎn)的是: ?每個(gè)實(shí)例化對(duì)象都會(huì)有一個(gè) _ _ proto_ _ 屬性, ?這個(gè)屬性是自動(dòng)生成的, ?_ _ proto_ _ 屬性指向類的原型對(duì)象。
構(gòu)造函數(shù)、實(shí)例化對(duì)象、原型對(duì)象的之間的關(guān)系
先來看一段代碼案例
?function createPerson(name,age) {
? ? ?this.name=name;
? ? ?this,age=age;
?}
?
?createPerson.prototype.say=function () {
? ? ?console.log('我的名字叫'+this.name);
?}
?
?var obj=new createPerson('張三','33');
?
?console.log(obj.__proto__); ? ? ? ? ? ? ?//實(shí)例化obj的__proto__屬性 可以獲取到原型對(duì)象
?console.log(createPerson.prototype); ? ? //構(gòu)造函數(shù)的prototype屬性 ?也可以獲取到原型對(duì)象
?console.log(obj.__proto__.constructor); ?//原型對(duì)象中的constructor又可以獲取到構(gòu)造函數(shù)
?
構(gòu)造函數(shù)、實(shí)例化對(duì)象、原型對(duì)象的基本關(guān)系圖分析

當(dāng)然你也可以通過 實(shí)例化對(duì)象的_ _ proto_ _屬性 和 構(gòu)造函數(shù)的prototype屬性進(jìn)行比較可以驗(yàn)證結(jié)果, ?我們可以通過打印來驗(yàn)證
?console.log(實(shí)例化對(duì)象._ _proto_ _ === 構(gòu)造函數(shù).prototype); ? ? //true
定義在實(shí)例和定義在原型下的區(qū)別總結(jié):
先看一段代碼案例:
?function Test(){
? ? ?
?}
?//定義屬性
?Test.prototype.name = "張三"; ?
?Test.prototype.age = ?33;
?//定義方法
?Test.prototype.getAge = function(){ ?
? ? ?return this.age; ?
?} ?
? ?
?var t1 = new Test(); ?
?var t2 = new Test();
?var t3 = new Test(); ?
?t3.name = "李四"; ?
?
?console.log(t1.name); // 張三 來自原型 ?
?console.log(t2.name); // 張三 來自原型 ?
?console.log(t3.name); // 李四 來自實(shí)例 ?
?
?//打印實(shí)例看下圖結(jié)果
?console.log(t1);
?console.log(t2);
?console.log(t3);
?

以上圖就解釋了為什么定義在原型中 屬性和方法是公用的, 而單獨(dú)定義在實(shí)例中是屬于獨(dú)立的屬性和方法不共有!
所以 我們也推斷出 在實(shí)例中定義屬性和方法 會(huì)覆蓋 或者說 會(huì)實(shí)現(xiàn)調(diào)用實(shí)例中定義的屬性和方法 如果沒有才會(huì)到原型中去尋找! ?這里其實(shí)就是我們一會(huì)要講到的原型鏈!
_ _ proto_ _與 prototype的詳細(xì)認(rèn)識(shí)
1.所有的引用類型,比如數(shù)組、對(duì)象、都有一個(gè)_ _ proto_ _屬性(也叫隱式原型,它來指向自己的原型對(duì)象)
重點(diǎn)再次提醒: 所有的對(duì)象引用 都有_ _ proto_ _ 這個(gè)屬性! 記住了!
通過下面的測(cè)試我們不難發(fā)現(xiàn),其中它們賦值的引用對(duì)象中 打印出來看到都有一個(gè) _ _ proto_ _的屬性 都是指向自己的原型對(duì)象
代碼如下:
?function createPerson(name,age) {
? ? ? this.name=name;
? ? ? this,age=age;
?}
?createPerson.prototype.say=function () {
? ? ?console.log('我的名字叫'+this.name);
?}
?var obj=new createPerson('測(cè)試','33');
?
?//對(duì)象引用打印
?console.log(obj);
?
?
?var arr=[1,2,3];
?//數(shù)組引用的打印
?console.log(arr);
?
?var arr2=new Array(2,2,2);
?//數(shù)組引用的打印
?console.log(arr2);
?
?
2.再一次重點(diǎn)注意: 所有引用類型,它的_ _ proto_ _屬性指向這個(gè)引用本身的原型對(duì)象 ?而構(gòu)造函數(shù)的prototype屬性的值也就是指向的原型對(duì)象
所以在各自相應(yīng)引用類型的_ _ proto_ _屬性 和 構(gòu)造函數(shù)的 prototype屬性 彼此它們兩個(gè)是相等的! 上面的圖中也可以表明這一點(diǎn)!
_ _ proto_ _屬性 和 構(gòu)造函數(shù)的 prototype屬性比較, 案例代碼如下:
?//案例1
?function createPerson(name,age) {
? ? ?this.name=name;
? ? ?this,age=age;
?}
?createPerson.prototype.say=function () {
? ? ?console.log('我的名字叫'+this.name);
?}
?
?var obj=new createPerson('張三','33');
?
?console.log(obj.__proto__); ? ? ?? //打印出obj對(duì)象引用的原型對(duì)象 ?
?console.log(createPerson.prototype); ? //打印出createPerson構(gòu)造函數(shù)的原型對(duì)象
?console.log(obj.__proto__===createPerson.prototype); ?//而且它們是相等的,指向同一個(gè)原型對(duì)象
?
?//案例2
?var arr=new Array(2,2,2);
?console.log(arr.__proto__);
?console.log(Array.prototype);
?console.log(arr.__proto__ === Array.prototype);
3.所有的構(gòu)造函數(shù) 或者 普通函數(shù)都有一個(gè)prototype屬性 (這也叫顯式原型,它也指向自己的原型對(duì)象)。
案例代碼:
?//普通函數(shù)
?function Test() {
?
?}
?//打印普通函數(shù)的prototype屬性
?console.log(Test.prototype);
?
?
?//構(gòu)造函數(shù)
?function createPerson(name,age) {
? ? ? this.name=name;
? ? ? this,age=age;
?}
?createPerson.prototype.say=function () {
? ? ?console.log('我的名字叫'+this.name);
?}
?
?console.log(createPerson.prototype);
?
圖解如下:

_ _proto _ _和 prototype區(qū)別
prototype是每個(gè)函數(shù)都會(huì)具備的一個(gè)屬性,它是一個(gè)指針,指向原型對(duì)象,只有普通函數(shù)或 構(gòu)造函數(shù)才有。
_ _ proto_ _屬性 是主流瀏覽器上在除null對(duì)象以外的每個(gè)引用對(duì)象上都支持存在的一個(gè)屬性,它能夠指向當(dāng)前該引用對(duì)象的:原型對(duì)象 其實(shí)_ _ proto_ _ 就是用來將引用對(duì)象與原型相連的屬性
小結(jié): 一個(gè)只有函數(shù)才有的屬性(prototype),一個(gè)是引用對(duì)象才有的屬性(_ _ proto_ _ ), ?
注意: 你用一個(gè)函數(shù)去調(diào)用屬性(_ _ proto_ _ ), 會(huì)得到一個(gè): ? () { [native code] }
原型中批量添加屬性與方法
使用prototype這個(gè)關(guān)鍵字, ?要批量的把屬性和方法寫入原型 就在構(gòu)造函數(shù)的下面寫一個(gè)JSON格式 如下代碼, 這樣比單一的一個(gè)個(gè)寫方便!
?//語法
?構(gòu)造函數(shù).prototype={
? ? ?屬性名: 值,
? ? ?方法名:function(){
? ? ? ? ?//方法體... ? 這里的this是什么要看執(zhí)行的時(shí)候誰調(diào)用了這個(gè)函數(shù)
? ? ?},
? ? ?方法名:function(){
? ? ? ? //方法體... ? 這里的this是什么要看執(zhí)行的時(shí)候誰調(diào)用了這個(gè)函數(shù)
? ? ?}
?}
案例代碼:
? createPerson.prototype={
? ? ? aaa:123,
? ? ? // prototype對(duì)象里面又有其他的屬性
? ? ? showName:function(){
? ? ? ? ? //this是什么要看執(zhí)行的時(shí)候誰調(diào)用了這個(gè)函數(shù)
? ? ? ? ? console.log("我的名字叫:"+this.name);
? ? ? },
? ? ? showAge:function(){
? ? ? ? ? //this是什么要看執(zhí)行的時(shí)候誰調(diào)用了這個(gè)函數(shù)
? ? ? ? ? console.log("我的年齡是:"+this.age);
? ? ? }
? }
?
?function createPerson(name,age) {
? ? ?this.name=name;
? ? ?this.age=age;
?}
?var obj= new createPerson('張三','33');
?console.log(obj);
?obj.showName();
?obj.showAge();
?console.log(obj.aaa);
以上原型代碼 圖解

原型注意事項(xiàng)
重點(diǎn)注意
如果是自定義構(gòu)造函數(shù),并且使用{ }這種方式批量的在prototype中定義屬性和方法, 會(huì)改變?cè)椭衏onstructor對(duì)構(gòu)造函數(shù)的指向!
也就是說使用{ }這種方式批量在prototype中定義屬性和方法, 那么constructor的指向就是一個(gè)函數(shù)對(duì)象
測(cè)試代碼如下
?function createPerson(name,age) {
? ? ?this.name=name;
? ? ?this,age=age;
?}
?
?/* createPerson.prototype.say=function(){
? ? ? ?console.log('我的名字叫'+this.name);
?}*/
?
?createPerson.prototype={
? ? ?say:function () {
? ? ? ? ?console.log('我的名字叫'+this.name);
? ? ?}
?}
?
?var obj=new createPerson('張三','33');
?console.log(obj.__proto__);
在控制臺(tái)輸出會(huì)看到 _ _proto _ _的值, constructor這個(gè)屬性就沒有了!

?console.log(createPerson.prototype.constructor); ? //返回 ? ? Object() { [native code] }
原型基本小結(jié)
讓相同方法在內(nèi)存中存在一份
原型定義方式要比普通定義方式的優(yōu)先級(jí)要低
在項(xiàng)目當(dāng)中公共相同的屬性和方法可以加載在原型上
原型鏈 核心原理
首先這里要提出一點(diǎn)的是 在JS中實(shí)現(xiàn)繼承主要是依靠原型鏈來實(shí)現(xiàn)! ,所以我們才需要學(xué)習(xí)原型鏈的原理!
原型鏈核心概念
原型鏈: 當(dāng)試圖調(diào)用或想得到一個(gè)對(duì)象實(shí)例中的屬性 或 方法時(shí),如果這個(gè)對(duì)象本身不存在這個(gè)屬性 或 方法 也就是說構(gòu)造函數(shù)中沒有定義你想要的屬性或方法,那么就會(huì)通過構(gòu)造函數(shù)的prototype屬性到原型中去 尋找這個(gè)屬性 或者 方法 (也就是它的構(gòu)造函數(shù)的’prototype’屬性會(huì)到原型對(duì)象中去尋找) ?, ?如果有就返回,如果沒有就會(huì)到頂層的Object去找 , 如果有就返回, 如果還是找不到就返回undefined!
原型鏈流程圖

上圖可以用以下例子來說明:當(dāng)構(gòu)造函數(shù) Test
存在 getName
這個(gè)方法時(shí),就不用到構(gòu)造函數(shù)的原型當(dāng)中去找 getName
這個(gè)方法;反之,就到構(gòu)造函數(shù)的原型當(dāng)中去找 getName
這個(gè)方法;如果構(gòu)造函數(shù)的原型中也不存在 getName
這個(gè)方法時(shí),就要到頂層對(duì)象的原型中去找 getName
這個(gè)方法。
上圖測(cè)試案例代碼如下:
?function Test(name){
? ? ?this.name=name;
? ? ?this.getName=function(){
? ? ? ? ?return this.name+"我在構(gòu)造函數(shù)中";
? ? ?}
?}
?Test.prototype.getName=function(){
? ? ?return this.name+"我在原型對(duì)象中";
?}
?Object.prototype.getName=function(){
? ? ?return this.name+"我在頂層對(duì)象中";
?}
?var t1=new Test('小紅');
?
?console.log(t1.getName());
原型鏈案例代碼2 如下:
?Array.prototype.aaa=123; ? ?//把這個(gè)自定義屬性定義到原型對(duì)象下
?var arr=new Array(1,2,3);
?console.log(arr.aaa);
?console.log(Array.prototype);
?
?var arr2=['重慶','上海','北京'];
?console.log(arr2.aaa);
如下圖:

小結(jié): arr 和 arr2都能夠找到aaa這個(gè)屬性, 并且這個(gè)屬性是數(shù)組原型對(duì)象下的屬性,是公共的
只要是數(shù)組就可以調(diào)用這個(gè)屬性, 同理方法也是一樣,
所以創(chuàng)建很多很多個(gè)相同類型對(duì)象的時(shí)候, 創(chuàng)建出來的每一個(gè)對(duì)象,如果里面都有一些公共的方法,這樣就會(huì)占用很多的資源,
而通過原型來實(shí)現(xiàn)的話,只需要在構(gòu)造函數(shù)里面給屬性賦值,而把方法寫在prototype屬性,當(dāng)然屬性也是可以寫在原型當(dāng)中的,
這樣每個(gè)對(duì)象引用都可以使用prototype屬性里面的方法 或 屬性,并且節(jié)省了不少的資源
這就是我們?yōu)槭裁匆褂迷偷脑?
原型鏈總體結(jié)構(gòu)圖解小結(jié)
當(dāng)試圖調(diào)用或想得到一個(gè)對(duì)象實(shí)例中的屬性 或 方法時(shí),如果這個(gè)對(duì)象本身不存在這個(gè)屬性 或 方法 那么就會(huì)通過構(gòu)造函數(shù)的prototype屬性到原型中去 尋找這個(gè)屬性 或者 方法 如果有就返回,如果沒有就會(huì)到頂層的Object去找 , 如果有就返回, 如果還是找不到就返回undefined!
但又因?yàn)闃?gòu)造函數(shù)中的prototype屬性值本身又是一個(gè)對(duì)象(原型對(duì)象), 所以這里它也有一個(gè)_ _ proto_ _ 屬性 , 就可以向上繼續(xù)獲取_ _ _ proto_ _屬性 ?, 那么這里獲取出來的就是頂層的Object對(duì)象
如下圖:

小結(jié):
當(dāng)obj調(diào)用test()方法,JS發(fā)現(xiàn)Fn中沒有這個(gè)方法,于是它就去Fn.prototype中去找,發(fā)現(xiàn)還是沒有這個(gè)方法,然后就去Object.prototype中去找,找到了,就調(diào)用Object.prototype中的test()方法。
這就是原型鏈查找,而則一層一層的鏈接的關(guān)系就是:原型鏈。
obj能夠調(diào)用Object.prototype中的方法正是因?yàn)榇嬖谠玩滉P(guān)系的機(jī)制!
另外,在使用原型的時(shí)候,一般推薦將需要擴(kuò)展的方法寫在 構(gòu)造函數(shù).prototype屬性中,
而不要寫在: 構(gòu)造函數(shù).prototype._ _ proto _ _中, 因?yàn)檫@里也就是Object頂層對(duì)象,定義到這里的屬性和方法 所有JS對(duì)象都可以調(diào)用,在這里面定義多了就會(huì)影響整體性能,所以不建議定義到這里!
如果喜歡話請(qǐng) 點(diǎn)贊 ?投幣 ?收藏 一鍵三連 ?
大家的支持就是我堅(jiān)持下去的動(dòng)力!
