这三种“阶级"模式有什么区别?JavaScript 中的定义?
在选择使用这四种模式中的一种而不是其他模式时,是否存在任何重要/微妙/显着的差异?而且,当通过 Object.create()
与 new
运算符实例化"时,它们之间有什么区别吗?
Are there any important/subtle/significant differences under the hood when choosing to use one of these four patterns over the others? And, are there any differences between the them when "instantiated" via Object.create()
vs the new
operator?
1) CoffeeScript
在翻译类"定义时使用的模式:
1) The pattern that CoffeeScript
uses when translating "class" definitions:
Animal = (function() {
function Animal(name) {
this.name = name;
}
Animal.prototype.move = function(meters) {
return alert(this.name + (" moved " + meters + "m."));
};
return Animal;
})();
和
2) Knockout
似乎提倡的模式:
2) The pattern that Knockout
seems to promote:
var DifferentAnimal = function(name){
var self = this;
self.name = name;
self.move = function(meters){
return alert(this.name + (" moved " + meters + "m."));
};
}
和
3) 我经常看到的类似的简单模式:
3) a similar, simple pattern I've often seen:
var DifferentAnimalWithClosure = function(name){
var name = name;
var move = function(meters){
};
return {name:name, move:move};
}
和
4) Backbone
提倡的模式:
var OneMoreAnimal= ClassThatAlreadyExists.extend({
name:'',
move:function(){}
});
更新 1: 更改模式 #2 并添加模式 #3 以响应 Elias 的响应//次要格式
Update 1: Changed pattern #2 and added pattern #3 in response to Elias' response // minor formatting
推荐答案
要明确一点:JS 不知道类,只知道对象和自定义的、自定义的构造函数,但这不是重点.
简而言之,回答您的问题:是的,创建您在此处发布的新对象的各种方式之间存在一些小的甚至相当大的差异.
Just to be clear: JS doesn't know of classes, just objects and custom, self-defined constructor functions, but that's besides the point.
To answer your question in short: yes, there are some small and even some fairly large differences between the various ways of creating a new object you're posting here.
CoffeeScript:
这实际上是创建自己的构造函数的最清晰和最传统的方式,但它已经优化",因为它已经准备好使用(可选)闭包变量.
基本上,这段代码的作用是使用 IIFE,将构造函数定义 和 都包装在它们自己的私有范围内的 proptotype 方法分配,返回对新构造函数的引用.它只是干净、简单的 JS,和你自己写的没什么不同.
CoffeeScript:
This is actually the most clear-cut and traditional way to create your own constructor, but it has been "optimized" in the sense that it's been ready set-up to use (optional) closure variables.
Basically, what this code does, is use an IIFE, to wrap both the constructor definition and the proptotype method assignments in their own, private scope, that returns a reference to the new constructor. It's just clean, simple JS, no different from what you might write yourself.
淘汰赛:
现在这让我有点吃惊,因为至少对我来说,您提供的代码片段看起来像是模块模式的一部分,或者是一个强大的构造函数.但是由于您没有使用 strict 模式
,省略 new
仍然会导致危险情况,并且由于整个函数都遇到了创建 DifferentAnimal
,只是为了 然后 构造第二个对象文字,将 DifferentAnimal
的所有属性分配给该辅助对象,我会说你错过了一些东西.因为,说实话,在这里省略最后一个 return {};
语句可能根本没有区别.另外:如您所见,您在本质上是构造函数中声明了一个方法(move
).这意味着每个实例都将被分配它自己的函数对象move
,而不是从原型中获取它.
简而言之:再仔细看看你从哪里得到这个片段,并仔细检查这是否是完整版本,因为如果是,我只能看到反对这个的参数.
Knockout:
Now this threw me a little, because to me, at least, the snippet you provide looks either like part of a module pattern, or a power constructor. But since you're not using strict mode
, omitting the new
would still make for dangerous situations, and since the entire function goes trough the trouble of creating a new instance of DifferentAnimal
, only to then construct a second object literal, assigning all properties of DifferentAnimal
to that secondary object, I'd say you're missing something. Because, truth be told, omitting the last return {};
statement here, would probably make no difference at all. Plus: as you can see, you're declaring a method (move
) in what is, in essence, a constructor. This means that every instance will be assigned its own function object move
, rather then getting it from the prototype.
In short: have another close look at where you got this snippet from, and double-check if this is the full version, because if it is, I can only see arguments against this.
使用一个变量,在构造函数中定义很简单:一个闭包,假设你的属性有一个不同的初始状态,由一些参数决定,传递给构造函数:
Using a variable, defined inside the constructor is simply: a closure, suppose your properties have a distinct initial state, determined by some arguments, passed to that constructor:
function MyConstructor(param)
{
var paramInit = param/2;//or something
this.p = paramInit;//this property can change later on, so:
this.reInit = function()
{//this method HAS to be inside constructor, every instance needs its own method
this.p = paramInit;//var paramInit can't, it's local to this scope
};
}
var foo = new MyConstructor(10);
console.log(foo.p);//5
foo.p = 'hi';
console.log(foo.p);//hi
foo.reInit();
console.log(foo.p);//5
console.log(foo.paramInit);//undefined, not available outside object: it's a pseudo-private property
仅此而已,真的.当你看到 ppl 使用 var that = this;
或其他东西时,这通常是为了创建对在任何地方都可用的主对象的引用,而不必处理 this
(this
引用了什么?当应用于对象其他而不是它最初打算用于的对象时,该方法应该做什么?等等......)
That's all there is too it, really. When you see ppl using var that = this;
or something, that's often to create a reference to the main object that is available anywhere, without having to deal with the headaches of this
(what does this
reference? What should the method do when applied to an object other than the one it was originally intended for? etcetera...)
主干:
在这里,我们正在处理另一种情况:扩展对象(IE:使用现有 "class"(构造函数)或特定实例的方法、属性)与简单地创建对象不同.
众所周知,JS 对象可以在任何给定时间分配新属性.这些属性也可以删除.有时,原型属性可以在实例本身上重新定义(掩盖原型行为)等......所以这完全取决于您希望结果对象(新创建的对象,扩展给定实例)看起来像:你希望它从实例中获取所有属性,还是希望两个对象在某个地方使用相同的原型?
这两件事都可以通过使用简单的 JS 来实现,但是他们需要自己编写更多的努力.但是,如果你写,例如:
Backbone:
Here, we're dealing with another case: extending objects (IE: using methods, properties of either an existing "class" (constructor) or a particular instance) is not the same as simply creating an object.
As you well know, JS objects can be assigned new properties at any given time. Those properties can be removed, too. Sometimes, prototype properties can be redefined on the instance itself (masking the prototypal behaviour) etc... So it all depends on what you want the resulting object (the newly created object, that extends the given instance) to look like: do you want it to take all properties from the instance, or do you want both objects to use the same prototype somewhere down the line?
Both of these things can be achieved by using simple JS, too, but they just take a bit more effort to write yourself. However, if you write, for example:
function Animal(name)
{
this.name = name;
}
Animal.prototype.eat= function()
{
console.log(this.name + ' is eating');
};
这可以看作是写作的等价物:
That could be deemed the equivalent of writing:
var Animal = Object.extend({name:'',eat:function()
{
console.log(this.name + ' is eating');
}});
短很多,但缺少构造函数.
A lot shorter, but lacking the constructor.
new
vs Object.create
嗯,这很简单:Object.create
只是比 new
强大得多:您可以定义原型方法、属性(包括天气与否,它们是否可枚举、可写)等等...)就在你需要创建一个对象的时候,而不是必须编写一个构造函数和一个原型,或者创建一个对象文字并弄乱所有那些 Object.defineProperty
行.
缺点:有些人仍然没有使用兼容 ECMA5 的浏览器(IE8 还没有完全死掉).以我的经验:一段时间后调试相当大的脚本确实变得相当困难:虽然我倾向于使用强力构造函数而不是常规构造函数,但我仍然在脚本的最顶部定义了它们,具有独特、清晰和非常具有描述性的名称,而对象文字是我刚刚创建的东西on-the-fly".使用 Object.create
,我注意到我倾向于创建实际上有点过于复杂而无法作为实际对象字面量的对象,就好像它们是对象字面量一样:
new
vs Object.create
Well, that's an easy one: Object.create
just is a lot more powerful that new
: you can define prototype methods, properties (including weather or not they are enumerable, writeable etc...) right at the time you need to create an object, instead of having to write a constructor and a prototype, or create an object literal and mess around with all those Object.defineProperty
lines.
The downsides: Some people still aren't using ECMA5 compliant browsers (IE8 is still not quite dead). In my experience: it does become quite hard to debug sizeable scripts after a while: though I tend to use power-constructors more than I do regular constructors, I still have them defined at the very top of my script, with distinct, clear and quite descriptive names, whereas object-literals are things I just create "on-the-fly". Using Object.create
, I noticed I tend to create objects that are really a little too complex to qualify as actual object literals, as though they are object literals:
//fictional example, old:
var createSomething = (function()
{
var internalMethod = function()
{//method for new object
console.log(this.myProperty || '');
};
return function(basedOn)
{
var prop, returnVal= {};
returnVal.myProperty = new Date();
returnVal.getCreated = internalMethod;//<--shared by all instances, thx to closure
if (!basedOn || !(basedOn instanceof Object))
{//no argument, or argument is not an object:
return returnVal;
}
for (prop in basedOn)
{//extend instance, passed as argument
if (basedOn.hasOwnProperty(prop) && prop !== '_extends')
{
returnVal[prop] = basedOn[prop];
}
}
returnVal._extends = basedOn;//<-- ref as sort-of-prototype
return returnVal;
};
}());
现在这很冗长,但我已经准备好基本的构造函数,我也可以使用它来扩展现有实例.简单地写起来可能看起来不那么冗长:
Now this is pretty verbose, but I've got my basic constructor ready, and I can use it to extend an existing instance, too. It might seem less verbose to simply write:
var createSomething = Object.create(someObject, {getCreated:function()
{
console.log(this.myProperty);
},
myProperty:new Date()});
但是 IMO,这让你更难跟踪在哪里创建了哪个对象(主要是因为 Object.create
是一个表达式,不会被提升.
嗯,当然,这远不是一个结论性的论点:两者都有其优点和缺点:我更喜欢使用模块模式、闭包和强大的构造函数,如果你不这样做就好了.
But IMO, this makes it harder on you do keep track of what object is created where (mainly because Object.create
is an expression, and will not be hoisted.
Ah well, that's far from a conclusive argument of course: both have their pro's and con's: I prefer using module patters, closures and power constructors, if you don't that's just fine.
希望这能为您解决一两件事.
Hope this cleared up a thing or 2 for you.
相关文章