只是为了明确:JS不知道类,只知道对象和自定义的构造函数,但这并不是重点。
简短回答你的问题:是的,在你发布的各种创建新对象的方式中,有一些小的甚至相当大的差异。
CoffeeScript:
实际上,这是创建自己的构造函数最清晰和传统的方法,但它已经被“优化”,因为它已经准备好使用(可选)闭包变量。
基本上,这段代码使用IIFE将构造函数定义和原型方法分配包装在它们自己的私有作用域中,并返回新构造函数的引用。这只是干净、简单的JS,与你自己编写的代码没有区别。
Knockout:
现在这让我有点困惑,因为对我来说,至少你提供的片段看起来像模块模式的一部分或者强制构造函数。但由于你没有使用严格模式,省略
new
仍然会导致危险情况,并且由于整个函数费力地创建了一个
DifferentAnimal
的新实例,只是为了然后构建第二个对象字面量,将所有属性分配给那个次要的对象,我想你可能错过了什么。因为,说实话,在这里省略最后的
return {};
语句可能根本没有任何区别。此外:正如你所看到的,你在本质上是一个构造函数中声明了一个方法(
move
)。这意味着每个实例都将被分配自己的函数对象
move
,而不是从原型中获取它。
简而言之:仔细查看你从哪里找到这个片段,并仔细检查这是否是完整版本,因为如果是,我只能看到反对这个的理由。
在构造函数内部定义变量很简单:闭包,假设你的属性有一个明显的初始状态,由传递给该构造函数的一些参数确定:
function MyConstructor(param)
{
var paramInit = param/2;
this.p = paramInit;
this.reInit = function()
{
this.p = paramInit;
};
}
var foo = new MyConstructor(10);
console.log(foo.p);
foo.p = 'hi';
console.log(foo.p);
foo.reInit();
console.log(foo.p);
console.log(foo.paramInit);
这实际上就是全部内容了。当你看到别人使用
var that = this;
或其他东西时,这通常是为了创建对主对象的引用,该引用可在任何地方使用,而不必处理
this
的问题(
this
指什么?将方法应用于原始意图以外的对象时,该方法应该执行什么操作?等等...)。
Backbone:
在这里,我们面临着另一种情况:扩展对象(即:使用现有“类”(构造函数)或特定实例的方法、属性)与简单创建对象并非相同。
正如您所知,JS对象可以随时分配新属性。这些属性也可以被删除。有时,在实例本身上重新定义原型属性(掩盖原型行为)等...因此,这完全取决于您希望生成的对象(新创建的对象,它扩展给定实例)看起来像什么:您想它从实例中获取所有属性,还是您希望两个对象在某处沿着同一原型线使用?
这两件事也可以通过使用简单的JS实现,但编写自己的代码需要更多的努力。但是,如果您编写例如:
function Animal(name)
{
this.name = name;
}
Animal.prototype.eat= function()
{
console.log(this.name + ' is eating');
};
这可以被视为等同于写作以下内容:
var Animal = Object.extend({name:'',eat:function()
{
console.log(this.name + ' is eating');
}});
这段文本很短,但缺少构造函数。
new
vs Object.create
嗯,这很容易理解: Object.create
比 new
更加强大:您可以在创建对象时定义原型方法、属性(包括它们是否可枚举、可写等),而无需编写构造函数和原型或创建对象字面量并混合使用所有这些 Object.defineProperty
行。
缺点是:一些人仍在使用不符合 ECMA5 标准的浏览器(IE8 仍未完全消失)。根据我的经验:在一段时间后,调试大型脚本变得非常困难:虽然我更倾向于使用 power-constructors 而不是常规构造函数,但我仍将它们定义在脚本的顶部,具有明显、清晰且相当描述性的名称,而对象字面量则是我“即兴创建”的东西。使用 Object.create
,我注意到我倾向于创建一些太复杂以至于不适合作为实际对象字面量的对象,尽管它们是对象字面量:
var createSomething = (function()
{
var internalMethod = function()
{
console.log(this.myProperty || '');
};
return function(basedOn)
{
var prop, returnVal= {};
returnVal.myProperty = new Date();
returnVal.getCreated = internalMethod;
if (!basedOn || !(basedOn instanceof Object))
{
return returnVal;
}
for (prop in basedOn)
{
if (basedOn.hasOwnProperty(prop) && prop !== '_extends')
{
returnVal[prop] = basedOn[prop];
}
}
returnVal._extends = basedOn;
return returnVal;
};
}());
现在这个构造函数相当冗长,但是我已经准备好了基本的构造函数,并且我也可以使用它来扩展现有的实例。可能只写以下代码似乎更简洁:
var createSomething = Object.create(someObject, {getCreated:function()
{
console.log(this.myProperty);
},
myProperty:new Date()});
但在我看来,这会使您更难跟踪哪个对象在何处创建(主要是因为
Object.create
是一个表达式,而且
不会提升。)
嗯,当然这远非定论:两者都有其优点和缺点:如果您不喜欢,我更喜欢使用模块模式、闭包和强构造函数。
希望这为您解决了一些问题。
this
,在这里你仍然可以使用原型)。 - RP Niemeyer