简单的"类"实例化

DrS*_*ove 10 javascript oop

来自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 );
  };
}
Run Code Online (Sandbox Code Playgroud)

尤其是这条线 this.init.apply( this, args.callee ? args : arguments );

args和之间有什么区别arguments?能args.callee永远false

rua*_*akh 22

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

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

var MyClass = makeClass();
Run Code Online (Sandbox Code Playgroud)

相当于:

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 );
}
Run Code Online (Sandbox Code Playgroud)

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

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 );
}
Run Code Online (Sandbox Code Playgroud)

其中args为的标识符MyClass第一参数,并arguments一如既往地是含有类似阵列的对象的所有MyClass的论点.

只有当"类"具有init在其原型中命名的函数(它将是"构造函数")时,才会询问您要询问的行,所以让我们给它一个:

MyClass.prototype.init =
  function (prop)
  {
    this.prop = prop;
  };
Run Code Online (Sandbox Code Playgroud)

一旦我们完成了,请考虑这个:

var myInstance1 = new MyClass('value');
Run Code Online (Sandbox Code Playgroud)

在调用内部MyClass,this将引用正在构造的对象,因此this instanceof MyClass将是真实的.并且typeof this.init == "function"将是真的,因为我们MyClass.prototype.init成为了一个功能.所以我们达到了这条线:

this.init.apply( this, args.callee ? args : arguments );
Run Code Online (Sandbox Code Playgroud)

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

this.init.apply(this, arguments);
Run Code Online (Sandbox Code Playgroud)

这相当于:

this.init('value');
Run Code Online (Sandbox Code Playgroud)

(如果您还不知道apply它是如何工作的,以及它有何不同call,请参阅https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/apply).

这到目前为止有意义吗?

另一个需要考虑的情况是:

var myInstance2 = MyClass('value');
Run Code Online (Sandbox Code Playgroud)

在调用中MyClass,this将引用全局对象(通常window),因此this instanceof MyClass将为false,因此我们到达此行:

return new MyClass( arguments );
Run Code Online (Sandbox Code Playgroud)

where arguments是一个包含单个元素的类数组对象:'value'.请注意,这与... 一样new MyClass('value').

术语说明:所以调用MyClass('value')结果在第二次调用MyClass,这次用new.我将打电话给第一个电话(没有new)"外线电话",第二个电话打电话(带new)"内线电话".希望这很直观.

内部呼叫到里面MyClass,args现指外呼叫的arguments对象:不是args存在'value',它现在是包含数组状物体'value'.而不是args.callee未定义,它现在指的是MyClass,所以args.callee ? args : arguments相当于args.因此内部调用MyClass是调用this.init.apply(this, args),这相当于this.init('value').

因此,测试args.callee用于区分内部呼叫(MyClass('value')new MyClass(arguments))和普通直接呼叫(new MyClass('value')).理想情况下,我们可以通过替换此行来消除该测试:

return new MyClass( arguments );
Run Code Online (Sandbox Code Playgroud)

假设看起来像这样:

return new MyClass.apply( itself, arguments );
Run Code Online (Sandbox Code Playgroud)

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

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

  • 如果我们定义一个构造函数MyClass.prototype.init,然后我们通过编写实例化"类" var myInstance3 = new MyClass();,那么args在调用内部将是未定义的MyClass,因此测试args.callee将引发错误.我认为这只是Resig的一个错误; 无论如何,它可以通过测试轻松修复args && args.callee.
  • 如果我们的构造函数的第一个参数碰巧实际上有一个名为的属性callee,那么测试args.callee将产生一个误报,并且错误的参数将被传递给构造函数.这意味着,例如,我们不能将构造函数设计为将arguments对象作为其第一个参数.但是这个问题似乎难以解决,而且可能不值得担心.