1. 程式人生 > >更適用於JavaScript的設計模式:面向委托的設計,了解一下?(下)

更適用於JavaScript的設計模式:面向委托的設計,了解一下?(下)

對象 角度 實現 log 缺陷 面向 相對 rip light

先來看一下傳統的面向類式的寫法:

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

Foo.prototype.sayName = function() {
    console.log(‘name: ‘ + this.name)
}

function Bar(name, age) {
    Foo.call(this, name);
    this.age = age;
}

Bar.prototype = Object.create(Foo.prototype);

Bar.prototype.sayAge = function() {
    console.log(‘age: ‘ + this.age)
}


var bar1 = new Bar(‘bar1‘, 13);

bar1.sayName();
bar1.sayAge();

這裏的Object.create也可以替換成Object.setPrototypeOf,但是我們這裏並不care它的constructor指向是否正確,所以從可讀性的角度我們用Object.create。(why ? 請參考上一篇)

上面是傳統的,也是最為推崇的寄生組合式繼承模式,但是es6誕生以後,這種寫法就不再流行了,更多的是利用class的語法糖,我們來看代碼:

class Foo {
    constructor(name) {
        this.name = name;
    }
    sayName() {
        console.log(‘name: ‘ + this.name);
    }
}

class Bar extends Foo {
    constructor(name, age) {
        super(name);
        this.age = age;
    }
    sayAge() {
        console.log(‘age: ‘ + this.age);
    }
}

var bar3 = new Bar(‘bar3‘, 15);

bar3.sayName();
bar3.sayAge();

class的語法優勢在於沒有了prototype的混亂,很輕松地實現繼承,利用super方法輕松實現構造函數的復制,等同於傳統的call所實現的效果,extends實現委托機制,等同於Object.create所實現的效果。

但是缺陷在於加深了人們對於類以及繼承的誤解。

我們再來看利用委托的設計模式:

Foo = {
    init(name) {
        this.name = name
    },
    sayName() {
        console.log(‘name: ‘ + this.name);
    }
}

Bar = Object.create(Foo);

Bar.inits = function(name, age) {
    Foo.init.call(this, name);
    this.age = age;
}

Bar.sayAge = function() {
    console.log(‘age: ‘ + this.age);
}

var bar = Object.create(Bar);

bar.inits(‘bar‘, 14);

bar.sayName();
bar.sayAge();

同樣,這裏沒有prototype的出現,也沒有new構造函數調用,完全依靠委托的機制,完全是對象之間的聯系。這種設計模式要求我們不再利用多態去重寫原有的函數或屬性,而是用不同的函數名或屬性名消除這種歧義。

可能存在的缺陷是之前的new構造函數被分成了兩段代碼。

var bar = Object.create(Bar);

bar.inits(‘bar‘, 14);

但是有一個好處在於我們可以關註點分離,使得創建和初始化分離。

以上三種是目前主流的實現仿類以及繼承的範式,第二種目前相對較為流行,第三種更生僻一些,但是卻最符合JavaScript的設計思想,沒有類的概念,沒有構造函數,只有對象與對象的聯系,行為委托。並不強求一定要用哪一種,還是看個人喜好吧,因為很難講三者的優勝好壞。

end

更適用於JavaScript的設計模式:面向委托的設計,了解一下?(下)