先来看一下传统的面向类式的写法:
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