JavaScript原型之路
簡介 最近我在學習Frontend Masters 上的進階JavaScript系列教程,Kyle 帶來了他的“OLOO”(對象連結其他對象)概念。這讓我想起了Keith Peters 幾年前發表的一篇博文,關於學習沒有“new”的世界,其中解釋了使用原型繼承代替建構函式。兩者都是純粹的原型編碼。 標準方法(The Standard Way) 一直以來,我們學習的在 JavaScript 裡建立對象的方法都是建立一個建構函式,然後為函數的原型對象添加方法。 function Animal(name) { this.name = name;}Animal.prototype.getName = function() { return this.name;}; 對於子類的解決方案是,建立一個新的建構函式,並且設定其原型為其父類的原型。調用父類的建構函式,並將this設定為其內容物件。 複製代碼function Dog(name) { Animal.call(this, name);}Dog.prototype = Object.create(Animal.prototype);Dog.prototype.speak = function() { return "woof";}; var dog = new Dog("Scamp");console.log(dog.getName() + ' says ' + dog.speak());複製代碼 原型方法(The Prototypal Way) 如果你接觸過任何原型語言,你會覺得上面的例子看起來很奇怪。我嘗試過 IO 語言——一門基於原型的語言。在原型語言中,可以通過複製對象並添加屬性和方法的方式建立一個原型。然後你能複製剛才建立的原型,從而建立一個可以使用的執行個體,或者複製它來建立另一個原型。上面的例子在 IO 裡,看起來像下面這樣: 複製代碼Animal := Object cloneAnimal getName := method(name) Dog := Animal cloneDog speak := method("woof") dog := Dog clonedog name := "Scamp"writeln(dog getName(), " says ", dog speak())複製代碼 好訊息(The Good News) 在JavaScript中,也可以使用這種編碼方式!Object.create 函數和 IO 裡的 clone 類似。下面是在JavaScript中,純原型的實現。除了文法不同之外,和 IO 版本一樣。 複製代碼Animal = Object.create(Object);Animal.getName = function() { return this.name;}; Dog = Object.create(Animal);Dog.speak = function() { return "woof";}; var dog = Object.create(Dog);dog.name = "Scamp";console.log(dog.getName() + ' says ' + dog.speak());複製代碼 壞訊息(The Bad News) 當使用建構函式時,JavaScript 引擎會進行最佳化。在 JSPerf 上測試兩個不同的操作,顯示基於原型的實現比使用建構函式的方式最多慢90多倍。 Perf Graph 另外,如果你使用類似 Angular 的架構,當建立控制器和服務時,必須使用建構函式。 引入類(Enter Classes) ES6帶來了新的 class 文法。但其只是標準建構函式方法的文法糖。新的文法看起來更像 Java 或 c#,但其幕後仍然是建立原型對象。這會讓來自基於類語言的人感到迷惑,因為當建立原型時,他們希望類和他們的語言有相同的屬性。 複製代碼class Animal { constructor(name) { this.name = name; } getName() { return this.name; }} class Dog extends Animal { constructor(name) { super(name); } speak() { return "woof"; }} var dog = new Dog("Scamp");console.log(dog.getName() + ' says ' + dog.speak());