JavaScript 面向?qū)ο蟮哪切┦聝?/h2>

一、類與實例

1、類的聲明
// 使用構(gòu)造函數(shù)來作類的聲明
var Me = function () {
    this.name = 'pengxiaohua';
};

// es6中class的聲明
class Me2 {
    constructor () {
        this.name = 'xiaohuapeng';
    }
}
2、生成實例

生成實例,都是用new方法,如下:

// 如果沒有參數(shù),`new Me()`中的括號是可以省掉的
new Me();  // Me  {name: 'pengxiaohua'};
new Me2();  // Me2  {name: 'xiaohuapeng'};

二、類與繼承

JavaScript的繼承的基本原理還是對原型鏈的操作。

1、繼承的幾種方式
  • ① 借助構(gòu)造函數(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"}

缺點:子類只能繼承父類構(gòu)造函數(shù)里的方法,不能繼承父類原型對象上的方法,如:

Father.prototype.say = 'say Hi';

new Child()實例中是沒法繼承say方法的。

  • ② 借助原型鏈實現(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ù)原型鏈知識可以知道,Child2 構(gòu)造函數(shù)的實例new Child2()的__proto__屬性和 Child2 的原型prototype相等,即new Child2().__proto__ === Child2.prototype,因為Child2.prototype = new Father2();,Child2將其原型賦值給父類Father2的實例new Father2(),所以new Child2().__proto__與new Father2()相等。則new Child2()可以拿到父類 Father2 中的方法,繼而實現(xiàn)了繼承。

缺點: 因為Child2 的實例對象都引用的同一個對象,即父類Father2的實例對象new Father2(),當(dāng)Child2 生成多個實例對象的時候,其中一個實例對象改變,其他實例對象都會改變,如下例子:

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)修改其中一個實例化對象時,另一個也會跟著改變,如下:

// 給arr添加一個數(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]

組合繼承方式,彌補了上面2中方式的缺點。
缺點: Father3這個父級構(gòu)造函數(shù)執(zhí)行了2次,一次是子類Child3實例化new Child3()的時候,Father3.call(this)調(diào)用了一次Father3這個父級構(gòu)造函數(shù),還有一次是Child3.prototype = new Father3();,對Father3()進行實例化的時候。
這2次是沒有必要的,會多消耗了一點內(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; // 針對第一種方法缺點,直接繼承父類原型對象就行了
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)造體里的屬性和方法,同時通過對prototype賦值,直接繼承父類原型對象上的方法和屬性,避免了重復(fù)。是一種比較完美的繼承方法。
但還是有一個小缺點的:

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的實例的時候,都顯示true,表明s5都是他們2個的實例。
因為instanceof有時是不準確的,當(dāng)用constructor來判斷的時候,發(fā)現(xiàn)s5是父類Father4的實例化,子類Child4沒有constructor,它的constructor是從父類的constructor中繼承的,這也造成了s5雖然是子類Child4的實例,但用instanceof檢測時卻既是Child4的也是Father4的實例對象。無法判斷s5這個實例是父類創(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ù)指向的是同一個,所以無法區(qū)分實例s5是通過父類還是通過子類來創(chuàng)造的。
通過Object.create()就解決了這個問題,這種方法通過、創(chuàng)建一個中間對象,把父類和子類兩個原型對象區(qū)分開,達到了父類和子類原型對象的一個隔離。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 目錄 導(dǎo)語 1.理解對象和面向?qū)ο蟮某绦蛟O(shè)計 2.創(chuàng)建對象的方式 3.JavaScript的繼承機制 3.1 原型...
    犯迷糊的小羊閱讀 889評論 0 4
  • class的基本用法 概述 JavaScript語言的傳統(tǒng)方法是通過構(gòu)造函數(shù),定義并生成新對象。下面是一個例子: ...
    呼呼哥閱讀 4,210評論 3 11
  • 一、理解對象 1.創(chuàng)建 ①構(gòu)造函數(shù) new Object ②對象字面量 var o = {}; 2.屬性類型 ①數(shù)...
    duJing閱讀 482評論 0 0
  • 記得上中學(xué)時候,曾學(xué)過一篇課文是著名作家魏巍寫的《誰是最可愛的人》,學(xué)完后我才知道是我們?nèi)嗣褡拥鼙? 現(xiàn)在,...
    df872c2ae1f1閱讀 743評論 0 7
  • 01 剛下飛機就見蘇哩直奔而來,給我一個大大的擁抱。她眼眶微紅,不知道是激動,還是久別重逢。但我卻推開了她,眼里滿...
    420_c644閱讀 389評論 0 0

友情鏈接更多精彩內(nèi)容