一、類與實(shí)例
1肄梨、類的聲明
// 使用構(gòu)造函數(shù)來作類的聲明
var Me = function () {
this.name = 'pengxiaohua';
};
// es6中class的聲明
class Me2 {
constructor () {
this.name = 'xiaohuapeng';
}
}
2阻荒、生成實(shí)例
生成實(shí)例,都是用new方法众羡,如下:
// 如果沒有參數(shù)侨赡,`new Me()`中的括號(hào)是可以省掉的
new Me(); // Me {name: 'pengxiaohua'};
new Me2(); // Me2 {name: 'xiaohuapeng'};
二、類與繼承
JavaScript的繼承的基本原理還是對原型鏈的操作粱侣。
1羊壹、繼承的幾種方式
- ① 借助構(gòu)造函數(shù)實(shí)現(xiàn)繼承
function Father () {
this.name = 'father';
}
function Child () {
Father.call(this); // 將Father的this指向Child的this,此處用apply也可以
this.type = 'child';
}
console.log(new Child()); // Child {name: "father", type: "child"}
缺點(diǎn):子類只能繼承父類構(gòu)造函數(shù)里的方法齐婴,不能繼承父類原型對象上的方法油猫,如:
Father.prototype.say = 'say Hi';
new Child()實(shí)例中是沒法繼承say方法的。
- ② 借助原型鏈實(shí)現(xiàn)繼承
function Father2 () {
this.name = 'father2';
}
function Child2 () {
this.type = 'child';
}
Child2.prototype = new Father2(); // 關(guān)鍵
console.log(new Child2().__proto__); // Father2 {name: "father2"}
根據(jù)原型鏈知識(shí)可以知道柠偶,Child2 構(gòu)造函數(shù)的實(shí)例new Child2()的__proto__
屬性和 Child2 的原型prototype相等情妖,即new Child2().__proto__ === Child2.prototype
,因?yàn)?code>Child2.prototype = new Father2();诱担,Child2將其原型賦值給父類Father2的實(shí)例new Father2()
毡证,所以new Child2().__proto__
與new Father2()相等。則new Child2()
可以拿到父類 Father2 中的方法蔫仙,繼而實(shí)現(xiàn)了繼承料睛。
缺點(diǎn): 因?yàn)镃hild2 的實(shí)例對象都引用的同一個(gè)對象,即父類Father2的實(shí)例對象new Father2()匀哄,當(dāng)Child2 生成多個(gè)實(shí)例對象的時(shí)候秦效,其中一個(gè)實(shí)例對象改變,其他實(shí)例對象都會(huì)改變涎嚼,如下例子:
function Father2 () {
this.name = 'father2';
this.arr = [1,2,3];
}
function Child2 () {
this.type = 'child2';
}
Child2.prototype = new Father2(); // 關(guān)鍵
var s1 = new Child2();
var s2 = new Child2();
console.log(s1.arr);
console.log(s2.arr);
// [1,2,3]
// [1,2,3]
當(dāng)修改其中一個(gè)實(shí)例化對象時(shí)阱州,另一個(gè)也會(huì)跟著改變,如下:
// 給arr添加一個(gè)數(shù)
s1.arr.push(4);
console.log(s1.arr);
console.log(s2.arr);
// [1,2,3,4]
// [1,2,3,4]
- ③ 組合繼承
function Father3 () {
this.name = 'father3';
this.arr = [1,2,3];
}
function Child3 () {
Father3.call(this);
this.type = 'child3';
}
Child3.prototype = new Father3();
var s3 = new Child3();
var s4 = new Child3();
s3.arr.push(4);
console.log(s3.arr);
console.log(s4.arr);
// [1,2,3,4]
// [1,2,3]
組合繼承方式法梯,彌補(bǔ)了上面2中方式的缺點(diǎn)苔货。
缺點(diǎn): Father3這個(gè)父級(jí)構(gòu)造函數(shù)執(zhí)行了2次犀概,一次是子類Child3實(shí)例化new Child3()
的時(shí)候,Father3.call(this)
調(diào)用了一次Father3這個(gè)父級(jí)構(gòu)造函數(shù)夜惭,還有一次是Child3.prototype = new Father3();
姻灶,對Father3()進(jìn)行實(shí)例化的時(shí)候。
這2次是沒有必要的诈茧,會(huì)多消耗了一點(diǎn)內(nèi)存产喉。
- ④ 組合繼承優(yōu)化方案1
function Father4 () {
this.name = 'father4';
this.arr = [1,2,3];
}
function Child4 () {
Father4.call(this); // 拿到父類的構(gòu)造體里的屬性和方法
this.type = 'child4';
}
Child4.prototype = Father4.prototype; // 針對第一種方法缺點(diǎn),直接繼承父類原型對象就行了
var s5 = new Child4();
var s6 = new Child4();
s5.arr.push(4);
console.log(s5.arr);
console.log(s6.arr);
// [1,2,3,4]
// [1,2,3]
這種方式通過call方法拿到父類構(gòu)造體里的屬性和方法敢会,同時(shí)通過對prototype賦值曾沈,直接繼承父類原型對象上的方法和屬性,避免了重復(fù)鸥昏。是一種比較完美的繼承方法塞俱。
但還是有一個(gè)小缺點(diǎn)的:
s5 instanceof Child4; // true
s5 instanceof Father4; // true
s5.constructor;
/*
Father4() {
this.name = 'father4';
this.arr = [1,2,3];
*/
}
當(dāng)用instanceof來判斷s5是不是Child4和Father4的實(shí)例的時(shí)候,都顯示true吏垮,表明s5都是他們2個(gè)的實(shí)例障涯。
因?yàn)閕nstanceof有時(shí)是不準(zhǔn)確的,當(dāng)用constructor來判斷的時(shí)候膳汪,發(fā)現(xiàn)s5是父類Father4的實(shí)例化唯蝶,子類Child4沒有constructor,它的constructor是從父類的constructor中繼承的旅敷,這也造成了s5雖然是子類Child4的實(shí)例生棍,但用instanceof檢測時(shí)卻既是Child4的也是Father4的實(shí)例對象颤霎。無法判斷s5這個(gè)實(shí)例是父類創(chuàng)造的還是子類創(chuàng)造的媳谁。
- ⑤ 組合繼承優(yōu)化方案2 (寄生組合式繼承)
function Father5 () {
this.name = 'father5';
this.arr = [1,2,3];
}
function Child5 () {
Father4.call(this);
this.type = 'child5';
}
Child4.prototype = Object.create(Father5.prototype);
var s7 = new Child5();
var s8 = new Child5();
s7.arr.push(4);
console.log(s7.arr); // [1,2,3,4]
console.log(s8.arr); // [1,2,3]
s5 instanceof Child4; // true
s5 instanceof Father4; // false
s5.constructor;
/*
function Child5 () {
Father4.call(this);
this.type = 'child5';
}
*/
在上一種方法中,Child4.prototype = Father4.prototype;
友酱,子類Child4和父類Father4的構(gòu)造函數(shù)指向的是同一個(gè)晴音,所以無法區(qū)分實(shí)例s5是通過父類還是通過子類來創(chuàng)造的。
通過Object.create()就解決了這個(gè)問題缔杉,這種方法通過锤躁、創(chuàng)建一個(gè)中間對象,把父類和子類兩個(gè)原型對象區(qū)分開或详,達(dá)到了父類和子類原型對象的一個(gè)隔離系羞。