原型鏈繼承
原型鏈是實現(xiàn)繼承的主要方法纹份,其基本思想是利用原型讓一個引用類型繼承另外一個引用類型的屬性和方法。
在介紹原型鏈之前廷痘,需要了解掌握以下幾點內(nèi)容:
構(gòu)造函數(shù)蔓涧、原型對象、實例對象之間的關(guān)系:
1. 每一個構(gòu)造函數(shù)都有一個原型對象笋额,原型對象都包含有一個指向其構(gòu)造函數(shù)的指針(constructor)元暴;
2. 實例對象包含一個指向原型對象的內(nèi)部指針(_ proto _);
3. 每一個構(gòu)造函數(shù)都有一個prototype屬性兄猩,用來指向它的原型對象
如果讓原型對象等于另一個類型的實例枢冤,此時鸠姨,原型對象將包含一個指向另一個原型的指針,相應(yīng)地淹真,另一個原型中也包含著一個指向另一個構(gòu)造函數(shù)的指針讶迁。如此下去,每一個原型對象都是下一個構(gòu)造函數(shù)的實例對象核蘸,這樣就構(gòu)成了實例與原型的鏈條巍糯,就是所謂的原型鏈。
function Animal() {
this.name = "animal";
this.age = 18;
this.say = function() {
console.log("hellow");
}
}
//給原型對象添加方法
Animal.prototype.run = function() {
console.log("running");
}
function Person() {
this.name = "person";
}
/*******************未構(gòu)建原型鏈之前實例的per對象*****************************************/
// var per = new Person;
// console.log(per);
// per.say();// is not a function
//因為沒有構(gòu)建原型鏈客扎,所以Person實例的per對象中沒有say()方法
/*******************未構(gòu)建原型鏈之前實例的per對象*****************************************/
//解決方法:
// 1.利用原型鏈的繼承關(guān)系祟峦,構(gòu)建原型鏈
// console.log(per.constructor);//Person
// console.log(Person.prototype.constructor);//Person
Person.prototype = new Animal();//原型鏈的核心
//此時Person的原型對象的constructor屬性指向的是Animal了
// console.log(per.constructor);//Person
// console.log(Person.prototype.constructor);//Aniaml
// 為了保證原型對象的constructor指向不變
Person.prototype.constructor = Person;//(可以不寫徙鱼,不會對代碼運行造成影響)
// 構(gòu)建好原型鏈之后再創(chuàng)建per對象2罄ⅰ!疆偿!;
// 特別注意:要想子類構(gòu)造函數(shù)實例的對象擁有父級的方法(say()),必須先搭建原型鏈關(guān)系即:Person.prototype = new Animal();,再實例對象搓幌。
var per = new Person();
console.log(per.name);//animal
per.say();//hellow 對比未構(gòu)建原型鏈之前的per.say()//pre is not defined
per.run();//running;
總結(jié):
優(yōu)點:可以繼承父級的所有方法;
缺點:
1.創(chuàng)建子類型的實例時不能向超類型的構(gòu)造函數(shù)傳遞參數(shù)
2.子類實例共享父類引用屬性的問題杆故。
借用構(gòu)造函數(shù)法實現(xiàn)繼承
基本思想就是在子類型構(gòu)造函數(shù)的內(nèi)部調(diào)用超類型構(gòu)造函數(shù)。
介紹借用構(gòu)造函數(shù)之前需要掌握以下幾點
每個函數(shù)都有call()和apply()方法
Function.apply(obj,[參數(shù)2溉愁,參數(shù)3处铛,參數(shù)4...])
等同于Function.apply(obj,arguments)
obj:這個對象將代替Function類里this對象
[]:這個是數(shù)組饲趋,它將作為參數(shù)傳給Function
Function.call([obj[,參數(shù)2[, 參數(shù)3[,參數(shù)4 [,.argN]]]]])
(我個人看著這個很別扭,簡單說就是撤蟆,將上面的數(shù)組集合拆為單個的參數(shù)書寫)
Function.call(obj,參數(shù)2奕塑,參數(shù)3,參數(shù)4家肯,...)
function Animal(name,age) {
this.name = name;
this.age = age ;
this.say = function() {
console.log("hellow");
}
}
//給原型對象添加方法
Animal.prototype.run = function() {
console.log("running");
}
function Person(name,age) {
Animal.call(this,name,age);//核心關(guān)鍵
// Animal.apply(this,[name,age]);
// Animal.apply(this,arguments);
}
var per = new Person();
animal.say.call(per);//只傳遞了一個對象參數(shù)
Animal.call(per,"李明",18);//傳遞了三個參數(shù):參數(shù)1:對象龄砰;參數(shù)2:“李明”;參數(shù)3:18
console.log(per);
Animal.call(per,"王磊",21)
console.log(per);
總結(jié):
call()與apply()沒有太大區(qū)別讨衣,只是參數(shù)傳遞方式不同
優(yōu)點:
1.解決了子類實例共享父類引用屬性的問題
2.創(chuàng)建子類實例時换棚,可以向父類構(gòu)造函數(shù)傳參,
缺點:
無法實現(xiàn)函數(shù)復(fù)用反镇,每個子類實例都持有一個新的fun函數(shù)固蚤,太多了就會影響性能,內(nèi)存爆炸(參考別人的歹茶,自己也不太清楚)夕玩。
補充:
實際經(jīng)常將原型鏈和借用構(gòu)造函數(shù)結(jié)合起來使用,即組合繼承惊豺。其背后的思想是使用原型鏈實現(xiàn)原型屬性和方法的繼承燎孟,而通過借用構(gòu)造函數(shù)來實現(xiàn)實例屬性的繼承。這樣扮叨,即通過在原型上定義方法實現(xiàn)了函數(shù)復(fù)用缤弦,用能夠保證每個實例都有自己的屬性。
關(guān)于apply()還有一些其他巧妙用法
apply的一些其他巧妙用法
在調(diào)用apply方法的時候彻磁,第一個參數(shù)是對象宗苍,第二個參數(shù)是一個數(shù)組集合舆绎,但是在調(diào)用父級構(gòu)造函數(shù)的時候,它需要的不是一個數(shù)組,但是跃惫,為什么他給我一個數(shù)組我仍然可以解析成一個個參數(shù)呢?這就是apply的一個巧妙的用法:apply()可以將一個數(shù)組默認的轉(zhuǎn)換成為一個參數(shù)列表
這個如果讓我們用程序來實現(xiàn)將數(shù)組的每一項轉(zhuǎn)換成參數(shù)列表可能會費點功夫求冷,借助apply的這點特性侠草,就方便多了
var max = Math.max.apply(null,[1,2,3,4,6,8,0]); console.log(max);
更多關(guān)于apply的詳細介紹
后記:
這是自己第一次使用markdown寫的筆記,你能想象我寫這篇文章用一天的時間么置吓?把腦袋里的想法用文字表達出來无虚,對于我這個不善表達的人是多么不容易呀。17年衍锚,一起加油友题。