原型鏈
原型鏈?zhǔn)且环N關(guān)系抖僵,實例對象和原型對象之間的關(guān)系,關(guān)系是通過原型(proto)來聯(lián)系的镰绎;
實例對象中有proto,是對象,叫原型,不是標(biāo)準(zhǔn)的屬性,瀏覽器使用,并且有的游覽器不支持
構(gòu)造函數(shù)中有prototype屬性,也是對象,叫原型脓斩;
注意: 原型中的方法是可以互相訪問的
實例代碼
function Animal(name,age){
this.name=name;
thia.age=age;
}
//在原型中添加方法
Animal.prototype.eat=function(){
console.log("動物吃草")
this.play()
}
Animal.prototype.play=function(){
console.log("玩啥呢")
}
原型的簡單語法
利用原型共享數(shù)據(jù)
- 第一種 寫法
function Student(name,age,sex){
this.name=name;
this.age=age;
this.sex=sex;
}
Student.prototype.height="188"
Student.prototype.weight="55kg"
Student.prototype.study=function(){
console.log("好好學(xué)習(xí)i")
}
var stu=new Student("小紅",20,"男")
console.dir(stu)
結(jié)果:
- 第二種 寫法
function Student(name,age,sex){
this.name=name;
this.age=age;
this.sex=sex;
}
Student.prototype={
height:"188",
weight:"55kg",
study:function(){
console.log("好好學(xué)習(xí)i")
}
}
var stu=new Student("小紅",20,"男")
console.dir(stu)
結(jié)果:
我們會發(fā)現(xiàn) 兩種寫法還是有差別的 ,第二種寫法會導(dǎo)致constructor構(gòu)造器屬性消失 所以我們得手動修改構(gòu)造器指向
最終代碼
function Student(name,age,sex){
this.name=name;
this.age=age;
this.sex=sex;
}
Student.prototype={
constructor:Student,
height:"188",
weight:"55kg",
study:function(){
console.log("好好學(xué)習(xí)i")
}
}
var stu=new Student("小紅",20,"男")
console.dir(stu)
好了,這回有了
實例對象使用屬性或方法的規(guī)則
- 實例對象使用的屬性或方法,現(xiàn)在實例中查找,如果有則使用自身的屬性或方法畴栖,
- 如果沒有随静,則通過proto指向的原型對象 查找方法,找到則使用,
- 如果找不到則繼續(xù)向proto尋找吗讶,直到未找到時報錯
構(gòu)造函數(shù)和實例對象和原型對象之間的關(guān)系
- 構(gòu)造函數(shù)可以實例化對象
- 構(gòu)造函數(shù)中有一個屬性叫prototype,是構(gòu)造函數(shù)的原型對象
- 構(gòu)造函數(shù)的原型對象(prototype)中有一個constructor 構(gòu)造器燎猛,這個構(gòu)造器指向的就是自己所在的原型對象所在的構(gòu)造函數(shù)
- 實例對象的原型對象(proto) 指向的是該構(gòu)造函數(shù)的原型對象(prototype)
- 構(gòu)造函數(shù)的原型對象(prototype)中的方法是可以被實例對象直接訪問
改變原型是否可以改變?
首先我們得知道構(gòu)造函數(shù)和實例對象中的this 指向的是什么?
這里我創(chuàng)建了自定義構(gòu)造函數(shù) Person ,并在內(nèi)部輸出了this ,并且在Person 的原型對象上添加了一個eat 方法,也輸出了一個this照皆,接著我實例化了一個對象重绷,并調(diào)用eat方法,
我們執(zhí)行一下膜毁,查看結(jié)果如何:
輸出結(jié)果
由此得出
- 原型對象中方法中的this 就是實例對象
- 構(gòu)造函數(shù)中的this就是實例對象.
接下來我們嘗試改變一下原型的指向
這段代碼中昭卓,首先我定義了一個Person自定義構(gòu)造函數(shù),并且在原型上添加了一個eat方法,
定義了一個Student 函數(shù)瘟滨,在原型上定義了一個sayHi方法候醒,
然后我將 Student的原型指向 了一個 Person的實例對象,
接著實例化一個Student,接著分別在stu 實例上 嘗試著調(diào)用 eat方法 和 sayHi 方法杂瘸,
運行結(jié)果:
到此我們可以確定倒淫,stu實例對象的原型指向被下面這條代碼改變了
Student.prototype=new Person(10);
總結(jié):
- 原型指向可以被改變的;
- 實例對象的原型proto指向的是該對象所在的構(gòu)造函數(shù)的原型對象;
- 構(gòu)造函數(shù)的原型對象(prototype)指向如果改變了,實例對象的原型(proto)指向也會發(fā)生改變;
- 實例對象和原型對象之間的關(guān)系是通過proto 原型來聯(lián)系起來的败玉,這個關(guān)系就是原型鏈;
- 如果原型指向改變了敌土,那么就應(yīng)該再原型改變指向之后添加原型方法, 那么sayHi方法則會創(chuàng)建在 new Person(10) 這個實例對象上
原型最終指向了哪里?
實例對象中的proto指向的是構(gòu)造函數(shù)的prototype
以此代碼為例:
測試一下
所以
per實例對象的proto ---指向---> Person.prototype的proto ---指向---> Object.prototype的proto 是Null
查看了一下html的dom對象,這有很有意思的原型鏈
這里祭出祖?zhèn)鱆PG
實現(xiàn)繼承
小知識---->instanceof的判斷方法:
從左邊操作數(shù)的proto路線出發(fā)绒怨,從右邊操作數(shù)的prototype出發(fā)纯赎,如果兩條路線最終指向一個引用就是true了
- 利用 call 借用構(gòu)造函數(shù)繼承
優(yōu)點:實現(xiàn)了繼承屬性,但值都不相同
缺點: 無法繼承父級類別中原型上的方法
function Person(name,age,sex,weight){
this.name=name;
this.age=age;
this.sex=sex;
this.weight=weight;
}
Person.prototype.sayHi=function(){
console.log("您好")
}
function Student(name,age,sex,weight,score){
//將當(dāng)前實例對象傳入Person 借過來使用一次來達(dá)到繼承效果
Person.call(this,name,age,sex,weight);
this.score=score;
}
var stu1=new Student("小明",10,"男","10kg","100")
- prototype 實現(xiàn)繼承: 利用prototype南蹂,將Student 的prototype 指向 Person 來達(dá)到繼承效果;
優(yōu)點:繼承了父級原型上的方法
缺點: 實例化多個Student 都必須共用相同的name 和 age
Student.prototype.constructor=Student
注意: 使用原型繼承時犬金,需要將構(gòu)造器的指向更改回正確的指向
function Person(name,age){
this.name=name;
this.age=age;
}
Person.prototype.eat=function(){
console.log("Person 吃飯")
}
function Student(num,score){
this.num=num
this.score=score
}
//繼承
Student.prototype=new Person("小紅",10)
Student.prototype.constructor=Student
var stu =new Student(2016002288,80)
stu.eat()//Person 吃飯
- 組合繼承: 組合繼承其實就是結(jié)合了上述的兩種方法來實現(xiàn)繼承,擁有兩種方法的優(yōu)點
function Person(name,age,sex){
this.name=name;
this.age=age;
this.sex=sex;
}
Person.prototype.sayHi=function(){
console.log("你好")
}
function Student(name,age,sex,score){
//借用構(gòu)造函數(shù)
Person.call(this,name,age,sex)
this.score=score
}
// 改變了原型指向
Student.prototype=new Person();//不傳值
Student.prototype.eat=function(){
console.log("吃東西");
}
var stu=new Student("小黑",20,"男","100分")
console.log(stu.name,stu.age,stu.sex,stu.score);
stu.sayHi()//你好
stu.eat()//吃東西
- 拷貝繼承: 類似于復(fù)制六剥,把一個對象中的屬性和方法直接復(fù)制到另一個對象中
function Person(){
}
Person.prototype.name="小紅"
Person.prototype.age=18
function Student(){
}
var p=Person.prototype;
var s=Student.prototype;
for(key in p){
s[key]=p[key]
}
console.dir(Student)
console結(jié)果:
每次都要for in 好累 , 可以進(jìn)行優(yōu)化封裝一下
function extend(Child,Parent) {
var p = Parent.prototype;
var c = Child.prototype;
for (var i in p) {
c[i] = p[i];
}
//這個屬性直接指向父對象的prototype屬性,可以直接調(diào)用父對象的方法,為了實現(xiàn)繼承的完備性晚顷,純屬備用性質(zhì)
c.par = p;
}
- 直接繼承prototype
優(yōu)點 : 效率比較高
缺點 : 因為相當(dāng)于是個傳址過程 所以修改Student的屬性 Person 的也會被更改
function Person(){};
Person.prototype.name="小紅";
Person.prototype.age=18;
function Student(){};
Student.prototype=Person.prototype;
console.dir(Student);
console.dir(Person);
Student.prototype.age=25;
console結(jié)果:
- 利用空對象作中介實現(xiàn)繼承: 用這種方式修改 Student 的prototype 不會影響到 Person的prototype
function Person(){};
Person.prototype.name="小紅";
Person.prototype.age=11;
function Student(){};
var F=function(){};
F.prototype=Person.prototype;
Student.prototype=new F();
Student.prototype.constructor=Student;
Student.prototype.age=25;
console.dir(Person)
console.dir(Student)
console結(jié)果:
封裝一下
function extend(Child,Parent) {
var F = function(){};
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
Child.par = Parent.prototype;
}
————————————————
原文鏈接:https://blog.csdn.net/m0_37846579/article/details/80278092