这三种“类"模式有什么区别?JavaScript 中的定义? [英] What are the differences between these three patterns of "class" definitions in JavaScript?

查看:23
本文介绍了这三种“类"模式有什么区别?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 仍然会导致危险的情况,并且由于整个函数经历了创建 <的新实例的麻烦code>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...)

主干:
在这里,我们处理另一种情况:扩展对象(即:使用现有类"(构造函数)或特定实例的方法、属性)与简单地创建对象不同.
众所周知,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 还没有完全消亡).根据我的经验:一段时间后,调试相当大的脚本确实变得非常困难:尽管与常规构造函数相比,我更倾向于使用 power-constructors,但我仍然在脚本的最顶部定义了它们,清晰、清晰、非常具有描述性的名称,而对象字面量只是我即时"创建的东西.使用 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.

这篇关于这三种“类"模式有什么区别?JavaScript 中的定义?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆