最近我在學(xué)習(xí)Frontend Masters 上的高級JavaScript系列教程,Kyle 帶來了他的“OLOO”(對象鏈接其他對象)概念。這讓我想起了Keith Peters 幾年前發(fā)表的一篇博文,關(guān)于學(xué)習(xí)沒有“new”的世界,其中解釋了使用原型繼承代替構(gòu)造函數(shù)。兩者都是純粹的原型編碼。
一直以來,我們學(xué)習(xí)的在 JavaScript 里創(chuàng)建對象的方法都是創(chuàng)建一個(gè)構(gòu)造函數(shù),然后為函數(shù)的原型對象添加方法。
function Animal(name) {
this.name = name;
}
Animal.prototype.getName = function() {
return this.name;
};
對于子類的解決方案是,創(chuàng)建一個(gè)新的構(gòu)造函數(shù),并且設(shè)置其原型為其父類的原型。調(diào)用父類的構(gòu)造函數(shù),并將this設(shè)置為其上下文對象。
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());
如果你接觸過任何原型語言,你會(huì)覺得上面的例子看起來很奇怪。我嘗試過 IO 語言——一門基于原型的語言。在原型語言中,可以通過克隆對象并添加屬性和方法的方式創(chuàng)建一個(gè)原型。然后你能克隆剛才創(chuàng)建的原型,從而創(chuàng)建一個(gè)可以使用的實(shí)例,或者克隆它來創(chuàng)建另一個(gè)原型。上面的例子在 IO 里,看起來像下面這樣:
Animal := Object clone
Animal getName := method(name)
Dog := Animal clone
Dog speak := method("woof")
dog := Dog clone
dog name := "Scamp"
writeln(dog getName(), " says ", dog speak())
在JavaScript中,也可以使用這種編碼方式!Object.create 函數(shù)和 IO 里的 clone 類似。下面是在JavaScript中,純原型的實(shí)現(xiàn)。除了語法不同之外,和 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());
當(dāng)使用構(gòu)造函數(shù)時(shí),JavaScript 引擎會(huì)進(jìn)行優(yōu)化。在 JSPerf 上測試兩個(gè)不同的操作,顯示基于原型的實(shí)現(xiàn)比使用構(gòu)造函數(shù)的方式最多慢90多倍。
另外,如果你使用類似 Angular 的框架,當(dāng)創(chuàng)建控制器和服務(wù)時(shí),必須使用構(gòu)造函數(shù)。
ES6帶來了新的 class 語法。但其只是標(biāo)準(zhǔn)構(gòu)造函數(shù)方法的語法糖。新的語法看起來更像 Java 或 c#,但其幕后仍然是創(chuàng)建原型對象。這會(huì)讓來自基于類語言的人感到迷惑,因?yàn)楫?dāng)創(chuàng)建原型時(shí),他們希望類和他們的語言有相同的屬性。
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());
如果讓我選擇,我會(huì)用純原型的風(fēng)格。這更具有表現(xiàn)力,動(dòng)態(tài)和有趣。由于虛擬機(jī)會(huì)對構(gòu)造函數(shù)方法進(jìn)行優(yōu)化,所有框架都會(huì)選擇構(gòu)造函數(shù)方法,在產(chǎn)品代碼中,我會(huì)繼續(xù)使用構(gòu)造函數(shù)。一旦 ES6 變得流行,我希望使用新的類語法代替古老的構(gòu)造函數(shù)方法。
英文:http://jurberg.github.io/blog/2014/07/12/javascript-prototype/
更多建議: