问题描述
:
// 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');
(如果您还不知道如何申请
有效,以及它与调用
的区别,请参阅)。
(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>,然后
会引发错误。我认为这只是Resig的一个错误;无论如何,它可以通过测试args
将在MyClass
的调用中未定义,因此<$ c上的测试$ c> args.calleeargs&& args.callee
而不是。 - 如果我们的构造函数的第一个参数碰巧实际上有一个名为
callee
的属性,然后对args.callee
的测试将产生误报,并将错误的参数传递给构造函数。这意味着,例如,我们不能将构造函数设计为将arguments
对象作为其第一个参数。但这个问题似乎很难解决,而且可能不值得担心。
- If we define a constructor
MyClass.prototype.init
, and then we instantiate the "class" by writingvar myInstance3 = new MyClass();
, thenargs
will be undefined inside the call toMyClass
, so the test onargs.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 onargs && args.callee
instead. - If our constructor's first argument happens to actually have a property named
callee
, then the test onargs.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 anarguments
object as its first argument. But this issue seems difficult to work around, and it's probably not worth worrying about.
这篇关于简单的“类”实例化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!