我在浏览器控制台中做了一个JavaScript实验
首先,我创建了一个新的对象foo,如下
var foo = {
bar: function() { return this.baz; },
baz: 1
};
现在,当我在控制台中再次运行以下代码时,它会返回"number"
(function(){
return typeof foo.bar();
})();
但当我运行以下匿名函数时,它会返回"undefined"
(function(){
return typeof arguments[0]();
})(foo.bar);
据我所知,上面函数中的arguments[0]
返回foo.bar
(下面的例子也证明了这一点),那么为什么上面的代码返回"undefined"
而不是"number"
呢?
当我运行时
(function(){
return arguments[0];
})(foo.bar);
返回function () { return this.baz; }
还有
(function(){
return typeof arguments[0];
})(foo.bar());
返回"number"
,为什么不返回
(function(){
return typeof arguments[0]();
})(foo.bar);
返回相同的?JavaScript中有什么基本定律在这里起作用吗?
this
取决于如何调用函数。当使用点表示法时,JavaScript将this
的上下文设置为接收器。在您的情况下,没有接收器,而是对函数的引用,因此您丢失了上下文。你必须明确通过:
arguments[0].call(foo);
arguments[0]
是与foo.bar
相同的函数对象,但this
的值是动态的。在foo.bar()
中,this
被分配给foo
,因为您使用了foo.
(点表示法)。但在arguments[0]()
中没有点(没有接收器),因此this
是默认值,或window
。
它是相同的函数,但调用不同。
让我简化这个问题。。。
var foo = {
bar: function() { return this.baz; },
baz: 1
};
var ref = foo.bar;
console.log(typeof ref()); // undefined
console.log(typeof foo.bar()); // number
这是因为函数对象"bar"中的"this"并不总是引用"foo"。它会改变你调用它的方式。如果你从一个对象而不是foo调用它,它将显示未定义。注意,ref不是foo,而是foo.bar。
现在,如果您将foo更改为以下内容,它将在这两种情况下都给出"number"输出。。。
var foo = {
bar: function() { return foo.baz; },
baz: 1
};
注意
console.log(ref === foo.bar); // true
但是foo.bar()不等于ref()(在第一种情况下),因为当您调用foo.bar)时,javascript引擎将foo传递为"this",但当您调用最初是window.ref()的ref()时,它将窗口对象(或非浏览器环境下的其他全局对象)传递为"this"
关于elclans答案的精化。
CCD_ 21是一种方法;关于如何做某事的指示。在面向对象编程语言中,唯一可以使用该指令的对象是foo
或与foo
相关的对象;但在JavaScript中,如果代码要求,任何人都可以尝试运行它。
在第二个匿名函数中,您将获得一个要运行的方法,运行它,并返回结果的类型。然而,运行此功能的不是foo
;它是匿名的,所以window
是运行它的对象。window
运行foo
,它试图返回this.baz
;但是,window
没有baz
,所以这就是为什么您没有定义。
要进一步测试它,请尝试设置window.baz
,看看是否得到了正确的结果,还可以尝试elclans建议的使用call()
方法,以确保从foo
的作用域调用它。
编辑
你是对的,arguments[0]
和foo.bar
的类型是相同的;它们都是"功能"。我认为混淆的地方在于JavaScript将函数视为一级对象。
如果你熟悉Java或C++等面向对象语言,那么当你调用bar
时,它总是由具有baz
属性的foo
调用;然而,JavaScript中的情况并非如此。任何函数都可以由Any对象调用,这意味着结果可能有意义,也可能没有意义。
正如elclans所说,两者之间的区别在于.
。假设我制作了自己的对象phew = {baz: 44}
。我可以有效地从foo
"窃取"一个方法,如下所示:phew.myMethod = foo.bar
。现在,phew
知道bar
中包含的指令,如果我调用phew.myMethod()
,我会得到44作为我的结果,因为phew
正在调用该方法,而phew
的baz
是44;方法定义在哪里并不重要!所有重要的是方法说要做什么,bar
说返回调用它的人的baz
。
现在,回到您的代码,您正在调用arguments[0]()
。看起来应该是一样的,但因为函数是一级对象,所以当您将其作为参数传递时,您真正传递的只是一个名为bar
的函数。当您调用arguments[0]()
时,它就像调用bar()
,这与调用foo.bar()
不同,就像它与调用phew.myMethod()
不同一样,尽管它们都是完全相同的函数。
有关更多信息,请尝试此SO post