JavaScript建立對象與繼承方法(筆記)

一.建立對象
雖然Object構造函數或對象字面量能夠用來建立單個對象,但有個明顯缺點:使用同一個接口建立不少對象會產生大量重複代碼。於是你們開始探索其餘方式。
1.工廠模式es6

function createPerson (name, age) {
    var o = new Object():
    o.name = name;
    o.age = age;
    o.sayName = function () {
        console.log(this.name);
    }
    return o;
}
var person = createPerson('Lily', 12);

工廠模式特色:雖然解決了建立多個類似對象的問題,但卻沒法識別一個對象的類型。(instance of)
2.構造函數模式數組

function Person (name, age) {
    this.name = name;
    this.age = age;
    this.sayName = function () {
        console.log(this.name);
    }
}
var person = new Person('Lily', 12); //使用構造函數模式建立實例,必須使用new操做符。

構造函數模式特色:能夠將它的實例標識爲一種特定類型,但每一個方法都要在實力上從新建立一遍。
3.原型模式函數

function Person () {}
Person.prototype.name = 'Lily';
Person.prototype.age = 12;
Person.prototype.sayName = function () {
    console.log(this.name);
}
var person = new Person(); //使用hasOwnProperty方法能夠檢測一個屬性存在於實例仍是原型。
console.log(person.hasOwnProperty('name')); //false
person.name = 'Tom'; //來自實例
console.log(person.hasOwnProperty('name')); //true

更簡單的原型語法:this

function Person () {}
Person.prototype = {
    name: 'Lily',
    age: 12,
    sayName: function () {
        console.log(this.name);
    }
} //這樣寫會致使constructor屬性再也不指向Person了。(不管什麼時候建立一個新函數A,都會爲它建立一個prototype屬性,指向函數的原型對象,默認狀況下,全部原型對象都會得到一個constructor屬性,包含一個指向A的指針。)

於是可更改成:prototype

function Person () {}
Person.prototype = {
    constructor: Person, //增長constructor屬性
    name: 'Lily',
    age: 12,
    sayName: function () {
        console.log(this.name);
    }
} //這種寫法要注意,建立實例必定要在定義原型以後,由於重寫原型對象就切斷了構造函數與最初原型的聯繫。

原型模式的特色:實現了讓全部實例共享原型對象所包含的屬性和方法。但缺點也在於這種共享對於包含引用類型值的屬性而言,存在一些問題,即全部實例會共享一個數組或者對象。
4.組合構造函數模式和原型模式指針

function Person (name, age) {
    this.name = name;
    this.age = age;
    this.friends = ['Tom', 'Bob'];
}
Person.prototype = {
    constructor: Person,
    sayName: function () {
        console.log(this.name);
    }
}

構造函數模式用於定義實例屬性,而原型模式用於定義方法和共享的屬性。這是目前最普遍,認同度最高的方法。
5.動態原型模式
有其餘OO語言經驗的開發人員看到獨立的構造函數和原型,可能會困惑。動態原型模式則是致力於解決此問題的一個方案,它把全部信息都封裝在了構造函數中,在必要狀況下,經過在構造函數中初始化原型,保持了組合使用構造函數和原型的優勢。code

function Person (name, age) {
    this.name = name;
    this.age = age;
    this.friends = ['Tom', 'Bob'];
    if (type of this.sayName != 'function') {
        Person.prototype.sayName = function () {
            console.log(this.name);
        }
    } //if語句只需檢查一個初始化應該存在的共享屬性或方法便可。
}

使用動態原型模式要記得不能用對象字面量重寫原型,由於若是在建立了實例的狀況下重寫原型。那麼就會切斷現有實例與新原型的聯繫。
關於寄生構造模式和穩妥構造模式,在工程實踐用的很少且稍顯過期,就不贅述了。有時間能夠了解es6的class。
二.繼承
1.原型鏈對象

function SuperType(){
    this.property = true;
}
SuperType.prototype.getSuperValue = function () {
    return this.property;
}
function SubType(){
    this.property = false;
}
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function () {
    return this.property;
}

須要注意的是,在經過原型鏈實現繼承時,不能用對象字面量建立原型方法,由於這樣會重寫原型鏈。以下所示:繼承

function SuperType(){
    this.property = true;
}
SuperType.prototype.getSuperValue = function () {
    return this.property;
}
function SubType(){
    this.property = false;
}
SubType.prototype = new SuperType();
SubType.prototype = {
    getSubValue: function () {
        return this.subproperty;
    },
    someOtherMethod: function () {
        return false;
    }
} //這樣會致使SubType的實例沒法訪問到getSuperValue()方法了。

原型鏈繼承的問題:
1.共享實例屬性,若是SupeType中定義一個數組colors,當subType經過原型鏈繼承SuperType後,它也會擁有一個colors屬性(就像專門建立了一個subType.prototype.colors屬性同樣),結果是SubType全部實例都會共享這個屬性。對其中一個實例.colors屬性會影響到全部其餘實例。
2.在建立子類型的實例時,沒辦法在不影響全部對象實例的狀況下,向超類型的構造函數傳遞參數。
所以實踐中不多單獨使用原型鏈繼承。
2.借用構造函數接口

function SuperType (name) {
    this.name = name;
    this.colors = ['red', 'blue', 'yellow'];
}
function SubType () 
    SuperType.call(this, 'Lily');
    this.age = 12;
} //使用這種方式就能夠向超類型的構造函數傳參啦。

借用構造函數的問題:仍是和構造函數建立對象同樣,方法都在構造函數定義,函數複用就無從談起了。
3.組合繼承
使用原型鏈實現對原型屬性和方法的繼承,而經過構造函數實現對實例屬性的繼承。

function SuperType (name) {
    this.name = name;
    this.colors = ['red', 'blue', 'yellow'];
}
SuperType.prototype.sayName = function () {
    console.log(this.name);
};
function SubType (name, age) 
    // 繼承實例屬性
    SuperType.call(this, name); //第二次調用SuperType()
    this.age = age;
}
//繼承方法
SubType.prototype = new SuperType(); //第一次調用SuperType()
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function () {
    console.log(this.age);
}
var instance1 = new SubType('Lily', 12);
instance1.colors.push('green');
console.log(instance1.colors); // "red,blue,yellow,green"
var instance2 = new SubType('Lucy', 22);
console.log(instance1.colors); // "red,blue,yellow"

組合繼承避免了原型鏈和借用構造函數的缺陷,是一種較爲流行的繼承方式。可是它還存在一個缺點:在第一次調用superType時,SubType.prototype會獲得兩個屬性:name和colors,當調用SubType的構造函數時又會再調用一次SuperType構造函數,在對象實例上建立了實例屬性name和colors屏蔽了SubType原型中的同名屬性。
寄生組合式繼承
寄生組合式是組合式繼承的改進,思路是沒必要爲了指定子類的原型而調用超類的構造函數,僅複製超類的原型便可。

function SuperType (name) {
    this.name = name;
    this.colors = ['red', 'blue', 'yellow'];
}
SuperType.prototype.sayName = function () {
    console.log(this.name);
};
function SubType (name, age) 
    // 繼承實例屬性
    SuperType.call(this, name); 
    this.age = age;
}
//繼承方法
SubType.prototype = Object.create(SuperType.prototype);
//Object.create(obj)至關於 function F(){}; F.prototype = obj;return new F();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function () {
    console.log(this.age);
}

開發人員廣泛認爲寄生組合式繼承是引用類型最理想的繼承方法。

相關文章
相關標籤/搜索