通过参数调用时的函数上下文
我有这道面试题,但我真的不明白
fn()
和
arguments[0]()
有什么区别,因为它们都引用同一个函数。
var length = 10;
function foo() {
console.log(this.length);
}
var obj = {
length: 5,
method: function(fn) {
fn(); // TypeError: Cannot read property 'length' of undefined
arguments[0](); // 2
}
};
obj.method(foo, 1);
好的,所以当调用
arguments[0]()
时,在
foo
中,它的上下文 (this) 将等于函数参数 -
[Arguments] { '0': [Function: foo], '1': 1
。但这是怎么发生的呢?
考虑以下代码:
function foo() {
console.log(this.bar);
}
const obj = {
bar: "foobar",
myFunction: foo
}
obj.myFunction(); // foobar
obj["myFunction"](); // also `foobar`
在上面的代码中,函数
foo
中的
this
绑定到 foo 的调用上下文,在上面的例子中,该上下文是对象
obj
,因为函数
myFunction
就是从该对象调用的,因此函数
foo
内的
this
将引用
obj
。
在您的情况下,
arguments
是一个
对象
,类似于上面的
obj
是一个对象。它具有一系列属性,例如
length
以及数字属性,用于表示传递给函数的参数的索引。例如,您可以(粗略地)像这样创建自己的参数对象:
function foo() {
console.log(this.length);
}
const myArguments = {
0: foo,
length: 2
};
// myArguments.0() is syntactically invalid, so we can only use bracket-notation like below
myArguments[0](); // 2
请注意,上面的
myArguments[0]()
使用与从第一个代码片段(
obj.myFunction()
&
obj["myFunction"]()
)调用函数相同的想法,因为我们使用的是
括号表示法
(
arguments[0]
)(而不是
点符号
(
arguments.0
)) 来访问和调用存储在
0
属性中的函数,该函数来自对象
myArguments
。因此,本例中
foo
的调用上下文是
myArguments
,因此
this
在函数
foo
中绑定到的内容就是它。
上述相同想法也适用于您的代码。当您在没有明确上下文的情况下调用
fn()
时(因为您没有通过以
someObject.
为前缀从对象调用它,或者使用诸如
.bind()
、
.call()
或
.apply()
之类的方法),您的
foo()
函数内的
this
将为
undefined
(如果处于
strict-mode
),从而导致错误:
TypeError: Cannot read property 'length' of undefined
但是,当使用
arguments[0]
时,
this
会绑定到
arguments
对象,就像上面两个例子一样。
执行上下文使用的
this
值取决于函数的调用方式。
1. fn() // TypeError: Cannot read property 'length' of undefined
2. arguments[0]() // 2
第 1 行显示直接函数调用。对于此样式,
this
值在严格模式下未定义,在非严格模式下为全局对象。
第 2 行显示函数作为方法调用。这是一个有点棘手的问题,因为我们更习惯于看到与方法一起使用的点运算符,但
括号属性访问器
表示法的工作方式类似。当您将函数作为方法调用时,
this
值将设置为调用该方法的对象。此行为旨在支持面向对象风格的编程。
这类似于编写
arguments.0()
,但 JS 语法不允许这种序列(大概是由于小数位语法存在歧义)。
狡猾的面试问题!
此处 obj 的工作方式类似于数组,获取给定位置的值并将其作为函数调用
var length = 10;
function foo1() {
console.log(length);
}
function foo2() {
console.log(this.length);
}
const obj = {
length: 5,
0: foo1,
1: foo2,
};
obj[0](); // 10
obj[1](); // 5