Stop Using Constructor Functions in JavaScript来自埃里克·埃利奥特(Eric Elliott),我认为我理解了所有的观点,在理论上.但实际上我看不到这种模式的真正优势.
我们来看看两个代码片段中的两个实现来继承.
>第一个使用augment.js它是Aadit M Shah的脚本
>在这个例子中,我们将使用this script.由Aadit M Shah也是这样做的.
实施1:
var AugmentPerson = Object.augment(function() {
this.constructor = function(name) {
this.name = name;
};
this.setAddress = function(country,city,street) {
this.country = country;
this.city = city;
this.street = street;
};
});
var AugmentfrenchGuy = AugmentPerson.augment(function(base) {
this.constructor = function(name) {
base.constructor.call(this,name);
};
this.setAddress = function(city,street) {
base.setAddress.call(this,"France",street);
};
});
var AugmentParislover = AugmentfrenchGuy.augment(function(base) {
this.constructor = function(name) {
base.constructor.call(this,name);
};
this.setAddress = function(street) {
base.setAddress.call(this,"Paris",street);
};
});
var t = new AugmentParislover("Mary");
t.setAddress("CH");
console.log(t.name,t.country,t.city,t.street); //Mary France Paris CH
在这个例子中,我们使用函数构造函数,而不是直接从对象继承.
实施2:
var CreatePerson = {
create: function (name) {
this.name = name;
return this.extend();
},setAddress: function(country,street) {
this.country = country;
this.city = city;
this.street = street;
}
};
var CreatefrenchGuy = CreatePerson.extend({
create: function (name) {
return CreatePerson.create.call(this,name);
},setAddress: function(city,street) {
CreatePerson.setAddress('France',street);
}
});
var CreateParislover = CreatefrenchGuy.extend({
create: function (name) {
return CreatefrenchGuy.create.call(this,setAddress: function(street) {
CreatefrenchGuy.setAddress('Paris',street);
}
});
var t = CreateParislover.create("Mary");
t.setAddress("CH");
console.log(t.name,t.street); //Mary France Paris CH
说实话,我试图看到第二个实现的好处.但我不能.我唯一看到的是更灵活的是因为我们可以使用apply来创建实例:
var t = CreateParislover.create.apply(CreateParislover,[“Mary”]);
这给了我们更多的灵活性,这是真的.但是我们可以对this做同样的事情:
Function.prototype.new = function () {
function functor() { return constructor.apply(this,args); }
var args = Array.prototype.slice.call(arguments);
functor.prototype = this.prototype;
var constructor = this;
return new functor;
};
然后我们可以:
var t = AugmentParislover.new.apply(AugmentParislover,["Mary"]);
在灵活性,可重用性,难度方面真正的好处是什么?
因为如果你检查两种情况的表现. Object.create()几乎比new:http://jsperf.com/inheritance-using-create-vs-new慢得多
我很混乱
解决方法
Constructor function vs Factory functions
Classical Vs prototypal inheritance
更多学习:
https://medium.com/javascript-scene/3-different-kinds-of-prototypal-inheritance-es6-edition-32d777fa16c9#.s0r3i5w6t
http://vimeo.com/69255635
TL;博士
>构建者打破/封闭的原则
>构造函数将对象创建与对象初始化相结合 – 有时会妨碍代码的可重用性
>构造函数看起来有点像类,这是令人困惑的. JavaScript不需要类(我建议避免ES6中的类关键字). JavaScript有一些比类更好的东西.
>原型委托和动态对象扩展(连接继承)的组合比传统的继承更强大和灵活.
>构造函数.prototype和实例之间的连接在JavaScript中是虚弱的和不可信的.使用构造函数可以提供一个工作实例的幻觉,当它不能在执行上下文中工作时,这可能会令人困惑,或者如果构造函数原型被替换,则不起作用.
使用构造函数更换原型更加困难.您可能想要这样做来启用多态对象构造.使用工厂,热插拔原型很容易,可以使用.call()和.apply()完成.
编辑 – 响应OP发布的“答案”:
Object.create最好的一点是,它是一个专用的低级工具,可以让您创建一个新的对象,并分配您想要的任何原型,而不使用构造函数.有很多理由来避免构建者,这里深入讨论:Constructor function vs Factory functions
>用于演示“较少代码”的代码并不能真正显示古典和原型继承之间的区别.一个更典型的例子可能是:
古典
var Animal = function Animal(name) {
this.name = name;
};
Animal.prototype.walk = function walk() {
console.log(this.name + ' goes for a walk.');
};
var Rabbit = function Rabbit(/* name */) {
// Because construction and instantiation are conflated,you must call super().
Animal.prototype.constructor.apply(this,arguments);
};
// Classical inheritance is really built on top of prototypal inheritance:
Rabbit.prototype = Object.create(Animal.prototype);
// Fix the .constructor property:
Rabbit.prototype.constructor = Rabbit;
Rabbit.prototype.jump = function jump() {
console.log(this.name + ' hops around a bit.');
};
var myRabbit = new Rabbit('Bunny George');
myRabbit.walk();
// Bunny George goes for a walk.
原型
var animalMethods = {
walk: function walk() {
console.log(this.name + ' goes for a walk.');
}
};
var animal = function animal(name) {
var instance = Object.create(animalMethods);
instance.name = name;
return instance;
};
var rabbitMethods = {
jump: function jump() {
console.log(this.name + ' hops around a bit.');
}
};
var rabbit = function rabbit(name) {
var proto = rabbitMethods;
// This is more commonly done like mixin({},animalMethods,rabbitMethods);
// where mixin = $.extend,_.extend,mout.object.mixIn,etc... It just copies
// source properties to the destination object (first arg),where properties from
// the last argument override properties from prevIoUs source arguments.
proto.walk = animalMethods.walk;
var instance = Object.create(rabbitMethods);
// This Could just as easily be a functional mixin,// shared with both animal and rabbit.
instance.name = name;
return instance;
};
var rabbit2 = rabbit('Bunny Bob');
rabbit2.walk();
// Bunny Bob goes for a walk.
所需的代码量非常相似,但对于我来说,原型更重要的是做得更多,而且它也更灵活,并且没有一个典型的继承关系式的行李.