原型鏈繼承
原型鏈是實現(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對象!!!;
// 特別注意:要想子類構(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年,一起加油。