简单的“类”实例化 [英] Simple “Class” Instantiation

查看:85
本文介绍了简单的“类”实例化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

来自John Resig博客

// makeClass - By John Resig (MIT Licensed)
function makeClass(){
  return function(args){
    if ( this instanceof arguments.callee ) {
      if ( typeof this.init == "function" )
        this.init.apply( this, args.callee ? args : arguments );
    } else
      return new arguments.callee( arguments );
  };
}

特别是这行 this.init.apply(这个,args.callee?args:arguments);

args 之间的区别是什么?和参数 args.callee 可以 false

What's the difference between args and arguments? Can args.callee ever be false?

推荐答案

你写的是现有的答案没有足够的细节,但即使在阅读了你的具体问题之后,我也不能完全确定代码的哪些方面会让你陷入困境 - 它有一些棘手的部分 - 所以如果这个答案过于详细了解你已经理解的事情,我会提前道歉!

You write that the existing answers don't have enough detail, but even after reading your specific questions, I'm not completely sure exactly which aspects of the code are throwing you for a loop — it has a number of tricky parts — so I apologize in advance if this answer goes overboard with details about things you've already understood!

因为 makeClass 始终意味着以相同的方式调用,如果我们删除一个级别的间接,则更容易推理它。这:

Since makeClass is always meant to be called the same way, it's a bit easier to reason about it if we remove one level of indirection. This:

var MyClass = makeClass();

相当于:

function MyClass(args)
{
  if ( this instanceof arguments.callee )
  {
    if ( typeof this.init == "function" )
      this.init.apply( this, args.callee ? args : arguments );
  }
  else
    return new arguments.callee( arguments );
}

由于我们不再处理匿名函数,我们不再需要 arguments.callee 表示法:它必然是指 MyClass ,因此我们可以用<$ c替换它的所有实例$ c> MyClass ,给出:

Since we're no longer dealing with an anonymous function, we no longer need the arguments.callee notation: it necessarily refers to MyClass, so we can replace all instances of it with MyClass, giving this:

function MyClass(args)
{
  if ( this instanceof MyClass )
  {
    if ( typeof this.init == "function" )
      this.init.apply( this, args.callee ? args : arguments );
  }
  else
    return new MyClass( arguments );
}

其中 args MyClass 第一个参数的标识符,以及参数,一如既往,是一个标识符类似于数组的对象,包含所有 MyClass 的参数。

where args is an identifier for MyClass's first argument, and arguments, as always, is an array-like object containing all of MyClass's arguments.

只有当class在其原型中有一个名为 init 的函数(它将是构造函数)时才会询问,所以让我们给它一个: / p>

The line you're asking about is only reached if the "class" has a function named init in its prototype (which will be the "constructor"), so let's give it one:

MyClass.prototype.init =
  function (prop)
  {
    this.prop = prop;
  };

一旦我们完成了这一点,请考虑以下事项:

Once we've done that, consider this:

var myInstance1 = new MyClass('value');

MyClass 的调用中,这个将引用正在构造的对象,因此此实例的MyClass 将为true。并且 typeof this.init ==function将为true,因为我们将 MyClass.prototype.init 设为a功能。所以我们到达这一行:

Inside the call to MyClass, this will refer to the object being constructed, so this instanceof MyClass will be true. And typeof this.init == "function" will be true, because we made MyClass.prototype.init be a function. So we reach this line:

this.init.apply( this, args.callee ? args : arguments );

此处 args 等于'value'(第一个参数),所以它是一个字符串,所以它没有被调用者属性;所以 args.callee 是未定义的,在布尔上下文中意味着它是假的,所以 args.callee? args:arguments 等同于参数。因此,上述行等同于:

Here args is equal to 'value' (the first argument), so it's a string, so it doesn't have the callee property; so args.callee is undefined, which in a Boolean context means it's false, so args.callee ? args : arguments is equivalent to arguments. Therefore, the above line is equivalent to this:

this.init.apply(this, arguments);

相当于:

this.init('value');

(如果您还不知道如何申请有效,以及它与调用的区别,请参阅 https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/apply )。

(if you don't already know how apply works, and how it differs from call, see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/apply).

这是否有意义所以远吗?

Does that make sense so far?

另一个需要考虑的案例是:

The other case to consider is this:

var myInstance2 = MyClass('value');

MyClass 的调用中,这个将引用全局对象(通常是窗口),所以此实例的MyClass 将为false,所以我们到达这一行:

Inside the call to MyClass, this will refer to the global object (typically window), so this instanceof MyClass will be false, so we reach this line:

return new MyClass( arguments );

其中参数是一个类似数组的对象包含单个元素:'value'。请注意,这新MyClass('value')相同。

where arguments is an array-like object containing a single element: 'value'. Note that this is not the same as new MyClass('value').

术语说明:因此,对 MyClass('value')的调用导致第二次调用 MyClass ,这次是 new 。我要拨打第一个电话(没有)外线电话和第二个电话()内部呼唤。希望这很直观。

Terminological note: So the call to MyClass('value') results in a second call to MyClass, this time with new. I'm going to call the first call (without new) the "outer call", and the second call (with new) the "inner call". Hopefully that's intuitive.

内部调用 MyClass args 现在引用外部调用的参数对象:而不是 args '的值',它现在是一个类似于数组的对象,包含'value'。而不是 args.callee 未定义,它现在引用 MyClass ,所以 args。被叫? args:arguments 相当于 args 。所以对 MyClass 的内部调用是调用 this.init.apply(this,args),这相当于 this.init('value')

Inside the inner call to MyClass, args now refers to the outer call's arguments object: instead of args being 'value', it's now an array-like object containing 'value'. And instead of args.callee being undefined, it now refers to MyClass, so args.callee ? args : arguments is equivalent to args. So the inner call to MyClass is calling this.init.apply(this, args), which is equivalent to this.init('value').

所以上的测试args.callee 用于区分内部调用( MyClass('value') new MyClass(arguments))来自正常的直接调用( new MyClass('value'))。理想情况下,我们可以通过替换此行来消除该测试:

So the test on args.callee is intended to distinguish an inner call (MyClass('value')new MyClass(arguments)) from a normal direct call (new MyClass('value')). Ideally we could eliminate that test by replacing this line:

return new MyClass( arguments );

假设有这样的假设:

return new MyClass.apply( itself, arguments );

但JavaScript不允许使用该表示法(也没有任何等效表示法)。

but JavaScript doesn't allow that notation (nor any equivalent notation).

顺便提一下,你可以看到Resig代码存在一些小问题:

You can see, by the way, that there are a few small problems with Resig's code:


  • 如果我们定义一个构造函数 MyClass.prototype.init ,然后我们通过编写 var myInstance3 = new MyClass();来实例化class code>,然后 args 将在 MyClass 的调用中未定义,因此<$ c上的测试$ c> args.callee 会引发错误。我认为这只是Resig的一个错误;无论如何,它可以通过测试 args&& args.callee 而不是。

  • 如果我们的构造函数的第一个参数碰巧实际上有一个名为 callee 的属性,然后对 args.callee 的测试将产生误报,并将错误的参数传递给构造函数。这意味着,例如,我们不能将构造函数设计为将 arguments 对象作为其第一个参数。但这个问题似乎很难解决,而且可能不值得担心。

  • If we define a constructor MyClass.prototype.init, and then we instantiate the "class" by writing var myInstance3 = new MyClass();, then args will be undefined inside the call to MyClass, so the test on args.callee will raise an error. I think this is simply a bug on Resig's part; at any rate, it's easily fixed by testing on args && args.callee instead.
  • If our constructor's first argument happens to actually have a property named callee, then the test on args.callee will produce a false positive, and the wrong arguments will be passed into the constructor. This means that, for example, we cannot design the constructor to take an arguments object as its first argument. But this issue seems difficult to work around, and it's probably not worth worrying about.

这篇关于简单的“类”实例化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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