全面理解Javascript的面向對象(二)--創建對象與繼承

在前文全面理解Javascript的面向對象(一)中詳細介紹了面向對象的主要知識點,可以幫助大家很細緻的瞭解js面向對象的概念,本文作爲補充,主要從對象的構建和繼承的方式兩方面進行分析。

一、創建對象主要的幾種方式

1 工廠模式

工廠模式抽象了創建具體對象的過程,用函數封裝以特定接口創建對象的細節。

function createPerson(name,age,job) {
    var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName = function () {
        alert(this.name);
    }
    return o;
}

var person1 = createPerson("Nike",21,"Doctor");
var person2 = createPerson("Frank",21,"Manager");

缺點:沒有解決對象的識別問題,即怎樣知道一個對象的類別。於是,新的模式:構造函數模式出現了。

2 構造函數模式

像Object、Array這樣的原生構造函數,在運行時就會自動創建。此外也可以自定義構造函數,從而定義自定義對象類型的屬性和方法。

function Person(name,age,job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function () {
        alert(this.name);
    }
}

var person1 = new Person("Nike",21,"Doctor");
var person2 = new Person("Frank",21,"Manager");

使用構造函數方法創建對象經歷了四個過程:
- 創建新對象
- 將構造函數作用域賦給新對象(this指向了新對象)
- 執行構造函數的方法(爲新對象添加屬性)
- 返回新對象

自定義的構造函數可將它的實例標識爲特定的類型,這是勝過工廠模式的地方

alert(person1 instanceof Object);   //ture
alert(person1 instanceof Person);   //ture
alert(person2 instanceof Object);   //ture
alert(person2 instanceof Person);   //ture

缺點:每個方法在每個實例上多次創建。這個問題可以通過原型模式解決。

3 原型模式

function Person() {

}
Person.prototype.name = 'frank';
Person.prototype.age = 21;
Person.prototype.sayName = function () {
    alert(this.name);
}

var person1 = new Person();
person1.sayName();

var person2 = new Person();
person2.sayName();

alert(person1.sayName == person2.sayName);  //true

更簡單的原型語法:

function Person() {

}
Person.prototype = {
    constructor : Person,
    name:'frank',
    age:29,
    sayName:function () {
        alert(this.name);
    }
};

4 組合使用構造函數模式和原型模式

構造函數模式用於定義實例屬性,原型模式用於定義方法和共享的屬性。結果,每個實例都會有自己的一份實例屬性的副本,同時又共享着方法的引用,最大限度節省內存。

function Person(name,age,job) {
    this.name = name;
    this.age = age;
    this.job = job;
}

Person.prototype = {
    constructor : Person,
    sayName : function () {
        alert(this.name);
    }
}

var person1 = new Person("Nike",21,"Doctor");
var person2 = new Person("Frank",21,"Manager");

二、繼承的實現方式

1 原型鏈

如果對象無法相應某個請求,他會把這個請求委託給他的構造器的原型對象。
將一個原型對象等於另一個實例,這個原型對象包含一個指向另一個原型的指針,相應的,另一個原型也包括一個指向構造函數的指針,這樣層層遞進,構成了實例與原型的鏈條

function SuperType() {
    this.property = true;
}
SuperType.prototype.getSuperType = function () {
    return this.property;
}

function SubTpye() {
    this.subproperty = false;
}

SubTpye.prototype = new SuperType();

var ins = new SubTpye();
alert(ins.getSuperType());

缺點:創建子類型的實例時,不能向超類型的構造函數中傳遞參數。

2 借用構造函數

在子類型構造函數中調用超類型構造函數。

function SuperType() {
    this.colors = ['red','blue','green'];
}


function SubTpye() {
    SuperType.call(this);
}

var ins1 = new SubTpye();
ins1.colors.push("black");
alert(ins1.colors);   //'red','blue','green','black'

var ins2 = new SubTpye();
alert(ins2.colors);   //'red','blue','green'

與原型鏈模式相比,借用構造函數的最大優勢是子類可以向超類中傳遞參數。

 function SuperType(name) {
    this. name = name;
}


function SubTpye() {
    SuperType.call(this,"frank");
}

var ins1 = new SubTpye();
alert(ins1.name);   //'frank'

缺點:方法都在構造函數中定義,因此函數複用就無從談起,超類型的原型中定義的方法,對子類型而言不可見。因此借用構造函數的模式很少單獨使用。

3 組合繼承

組合繼承是將原型鏈和借用構造函數的方法結合到一起。
思路是
- 使用原型鏈實現對原型屬性和方法的繼承;
- 使用借用構造函數的方法實現對實例屬性的繼承。

這樣,通過在原型上定義方法實現函數複用,又能夠保證對每個實例有自己的屬性。

function SuperType(name){
    this.name = name;
    this.colors = ["red","blue","green"];
}
SuperType.prototype.callName = function(){
    alert(this.name);
}
function SubType(name,age){
    SuperType.call(this,name);
    this.age = age;
}
SubType.prototype = new SuperType();
SubType.prototype={
    constructor:SubType,
    callAge:function(){
        alert(this.age);
    }
}

var ins1 = new SubType("nike",29);
ins1.colors.push("black");
console.log(ins1.colors);
console.log(ins1.callName());
console.log(ins1.callAge());

組合模式是javascript中最常用的模式。

4 原型繼承

在上一篇博文全面理解Javascript的面向對象(一)中,介紹過,原型繼承的本質是基於原型鏈的委託機制。

function object(o){
    function F(){}
    F.prototype = o;
    return new F();
}

在object()函數內部,先創建一個臨時性的構造函數,然後將傳入的對象作爲這個構造函數的原型,最後返回這個臨時類型的一個新實例。object()對傳入的對象進行了一次淺複製。

var Person = {
    name:"nike",
    friends:["frank","lucy"];
}
var another = object(Person);
another.gentle = "male";
another.friends.push("mike");

在ES5中新增Object.create()方法,規範了原型繼承。這個方法又兩個傳入的參數,一個是對象,另一個是==可選==的額外定義的屬性。

var another1 = Object.create(Person);
another.gentle = "male";

var another2 = Object.create(Person,{
    name:{
        value:"Greg"
    }
});
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章