前端基本知識(二):JS的原型鏈的理解


      之前一直對於前端的基本知識不是了解很詳細,基本功不扎實,但是前端開發中的基本知識才是以后職業發展的根基,雖然自己總是以一種實踐是檢驗真理的唯一標准,寫代碼實踐項目才是唯一,但是經常遇到知道怎么去解決這個問題,但是不知道使用的是什么一種方法,方法的原理是什么,現在覺得大學里學習的基本知識還是很重要的,一定有自己的理解才能走的更遠。

 

無論以后自己的學習新的技術,但是萬變不離其宗,基本扎實,學習效率高。

 

廢話這么多,開始了今天理解的四部分部分。

一、JS的原型鏈理解方式

二、原型理解

三、規則

四、js常見的創建對象的各種方法

 

一、原始鏈理解方式

每一次原型鏈理解起來很費勁,而且經常容易出錯,或者解釋的不到位。

1、什么是對象實例,什么是原型(對象)?

2、什么是構造函數?

3、通過原型對象找到構造函數

4、原型的唯一性

5、原型鏈

6、原型鏈最終指向為null

7、繼承

8、原型鏈的繼承

9、原型鏈的向上搜索

10、對象的屬性可以自定義

11、對象實例不能改動原型屬性

12、原型屬性的共享

13、原型的動態性

14、原型的重寫

我去,這也太多了,不想看了,我已經蒙圈了。等等,學習本身就是一個痛苦的過程,但是當你通過自己的理解的東西,這樣運行機制就更容易理解。

你可以這樣理解:

1、”人是人生的,妖是妖生的。“人和妖都是對象實例,人他媽和妖他媽都是原型,也叫原型對象。

2、“人出生和妖出生”都是構造一個函數,從無到有的過程

3、“人可以通過人他媽找到人他爸是誰”,也就是通過原型找到構造函數。

4、“人他媽可以生很多小寶寶,但是這些寶寶只有一個媽媽”,這就是原型的唯一性。

5、“人他媽的媽媽,和人他媽的媽媽的媽媽,。。。。”,這就是原型鏈

6、原型鏈並不是無限長,通過繼承可以不斷的往上找,最終原型鏈指向null

7、“人繼承了人他媽的屬性,妖繼承了妖他媽的屬性。”

8、“人繼承了人他媽的膚色等等,人他媽繼承人他媽他媽的膚色等等。。。”這就是原型繼承。

9、“你沒有家,你家指的就是你媽家;你媽沒有家,那你家指向的就是你媽媽的媽媽的家“,這就是原型鏈的向上搜索。

10、“你會繼承你媽的樣子,但是你可以染發剪頭發等等”,也就是說對象的屬性可以的自定義。

11、"雖然你改變了自己的頭發顏色等等,但是你不能改變你媽的樣子",這就是對象實例不能改變原型的屬性。

12、“你家玩火被你說了話,那就是說你家,你媽家,你弟弟們家,都被少了,這就是原型共享”

13、“你媽外號叫"小翠",鄰居都叫你“小翠兒”,但是你媽做了一個帥氣的發型,外號改成了“金毛獅王”,鄰居都叫你,“金毛獅王子””,這就是原型的動態性。

14、“你媽愛美,整容了,沒有人認識,然后又整回去了”,這就是叫原型的整體重寫。

 

在用代碼說明原型鏈的之前,我們先弄清楚,函數和function有什么關系,構造函數,原型,實例之間有什么關系?

答:1、所有的函數都是 Function 的實例。

2、在構造函數上都有一個原型屬性 prototype,該屬性也是一個對象;

3、那么在原型對象上有一個 constructor 屬性,該屬性指向的就是構造函數;

4、而實例對象上有一個 _proto_ 屬性,該屬性也指向原型對象,並且該屬性不是標准屬性,不可以用在編程中,該屬性用於瀏覽器內部使用。

function person(name){this.name=name}

function mother(){}

mother.prototype={//mother原型自帶屬性prototype
    age:20,
    home:['Beijing','Shanghai']
};

person.prototype=new mother();   //person的原型為mother

var p1=new person('Tom');     //p1:'Tom';_proto_:20,['Beijing','Shanghai']
var p2=new person('Mark')     //p2:'Mark';_proto_:20,['Beijing','Shanghai']
p1.age=24;     //實例不能改變原型的基本屬性值,在p1實例下增加一個age的屬性,與原型無關
//p1:'Tom',24;_proto_:20,['Beijing','Shanghai']

p1.home[0]='Shenzhen';  
//原型中引用類型屬相的共享,,正如你燒了你家,就是燒了你全家的家
//p1:'Tom',24;_proto_:20,['Shenzhen','Shanghai']
//p2:'Mark';_proto_:20,['Shenzhen','Shanghai']

p1.home=['Hangzhou','Guangzhou'];
//其實與p1.age=20的操作一樣
//p1:'Tom',24,['Hangzhou','Guangzhou'];_proto_:20,['Shenzhen','Shanghai']
//p2:'Mark';_proto_:20,['Shenzhen','Shanghai']

delete p1.age;
//刪除自定義的屬性之后,原本覆蓋的原型就可以重見天日,這就是搜索機制
//p1:'Tom',['Hangzhou','Guangzhou'];_proto_:20,['Shenzhen','Shanghai']
//p2:'Mark';_proto_:20,['Shenzhen','Shanghai']

person.prototype.lastname='Cheng';
//改寫原型,動態反應在實例中。我們改寫的是person的原型,往mother里加一個last那么屬性
//等同於mother.lastname='Cheng'
//這里並不是mother。prototype,改動不同的層次,效果會有很大的差異
//p1:'Tom',['Hangzhou','Guangzhou'];_proto_:'Cheng';_proto_:20,['Shenzhen','Shanghai']
//p2:'Mark',['Hangzhou','Guangzhou'];_proto_:'Cheng';_proto_:20,['Shenzhen','Shanghai]

person.prototype={
    age:28,
    address:{country:'USA',city:'Washington'}
};
var p3=new person('obama');
//重寫原型,這個時候person的原型完全改變
//p1:'Tom',['Hangzhou','Guangzhou'];_proto_:'Cheng';_proto_:20,['Shenzhen','Shanghai']
//p2:'Mark',['Hangzhou','Guangzhou'];_proto_:'Cheng';_proto_:20,['Shenzhen','Shanghai']
//p3:'obma';_proto_:28{country:'USA',city:'Washington'}

mother.prototype.no=20110408;
//改寫原型的原型,動態反應在實例中,
//p1和p2會改變,但是p3不會改變,p3與mother無關
//p1:'Tom',['Hangzhou','Guangzhou'];_proto_:'Cheng';_proto_:20,['Shenzhen','Shanghai'],20110408
//p2:'Mark',['Hangzhou','Guangzhou'];_proto_:'Cheng';_proto_:20,['Shenzhen','Shanghai'],20110408
//p3:'obama';_proto_:28{country:'USA',city:'Washington'}

mother.prototype={
    car:2,
    hobby:['run','walk']
};
var p4=new person('Tony');
//重寫原型的原型,這時候mother的原型已經發生完全的變化
//上面的person和mother已經斷開聯系了,mother怎么變都不會影響person
//p4:'Tony';_proto_:28{country:'USA',city:'Washington'}


person.prototype=new mother();   //再次綁定
var p5=new person('Luccy');
//這個時候如果需要應用這邊改動的話,需要重新將person原型綁定在mother上
//p5:'Luccy';_proto_:2,['run','walk']


p1.__proto__.__proto__.__proto__.__proto__    //null,原始鏈終點是null
mother.__proto__.__proto__.__proto__     //null ,原型鏈的終點事null

 

二、原型理解

javascript中,原型也是一個對象,通過原型可以實現對象的屬性繼承,javascript的對象中都包含了一個protype內部屬性,這個屬性對應的就是該對象的原型。javascript的原型對象中還包含一個constructor屬性,這個屬性對應創建所有指向該原型的實例的構造函數。

 

注意:protype作為對象的內部屬性是不可以直接訪問的,但是谷歌瀏覽器提供了一個_proto_這個非標准的訪問器。

 

三、規則

javascript中,每一個函數都有一個prototype屬性,當一個函數被用作構造函數來創建實例,這個prototype屬性值會被作為原型賦值給所有的對象實例(也就是所有實例設置“_proto_”屬性)。

對象:prototype屬性

原型對象:protype屬性,construction屬性

對象實例:_proto_屬性

 

new的過程分為三步:

var p=new person('張三',20);

1、var p={};初始化一個對象p

2、p._proto_=person.prototype;將對象p的_proto_屬性設置為person.prototype

3、person.call('張三',20);調用構造函數person來初始化p。

 

原型鏈繼承的主要問題在於屬性的共享。原型繼承的改良方法:

(1)組合繼承

function mother(age){
    this.age=age;
    this.hobby=['running','football']
}
mother.prototype.showAge=function(){
    console.log(this.age);
}

function person(name,age){
    mother.call(this,age);   //第二次執行
    this.name=name;
}
person.prototype=new mother();   //第一次執行
person.protype.constructor=person;
person.prototype.showName=function(){
    console.log(this.name);
}
var p1=new person('jack',20);
p1.hobby.push('basketball');     //p1:'jack';_proto_:20,['running','football']
var p2=new person('mark',18);    //p2:'mark';_proto_:18,['running','football']

執行結果如圖所示:

 

 

 

四、js常見的創建對象的各種方法

(1)原始模式

//1、原始模式。對象字面量方式
var person={
name:'Jack',
age:18,
sayName:function(){alert(this.name);}
};
//1、原始模式,object構造函數方式
var person=new object();
person.name='Jack';
person.age=18;
person.sayName=function(){alert(this.name);};

代碼重用量大,所以產生了工廠模式。

(2)工廠模式

//2、工廠模式,定義一個函數創建對象
function createPerson(name,age){
var temp=new Object();
person.name=name;
person.age=age;
person.sayName=function(){alert(this.name);};
return temp;
}

工廠模式就是批量成產,效率

(3)構造函數

//3、構造函數模式,為對象頂一個構造函數
function person(name,age){
this.name=name;
this.age=age;
this.sayName=function(){alert(this.name);};
}
var p1=new person('Jack',18);   //創建一個p1對象
person('Jack',18);    //屬性方法都給window對象,window.name='Jack'

構造函數與c++,、java構造函數類似,易於理解。

(4)原型方式

//4、原型模式,直接定義prottype
function person(){
person.prototype.name='Jack';
person.prototype.age=18;
person.prototype.sayName=function(){alert(this.name);}
}

//4、原型模式,字面量定義的方式
function person(){
person.prototype={
name:'Jack',
age:18,
sayName:function(){alert(this.name);}
};
var p1=new person();  //name='Jack'
var p2=new person();  //name='Jack'
}

這里需要注意的是原型屬性和方法的共享,即所有實例中都只是引用原型中的屬性方法,任何一個地方差生的改動都會引起其他實例的變化。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM