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.
所需的代码量非常相似,但对于我来说,原型更重要的是做得更多,而且它也更灵活,并且没有一个典型的继承关系式的行李.