开发者问题收集

通过参数调用时的函数上下文

2020-10-21
529

我有这道面试题,但我真的不明白 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 。但这是怎么发生的呢?

3个回答

考虑以下代码:

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 对象,就像上面两个例子一样。

Nick Parsons
2020-10-21

执行上下文使用的 this 值取决于函数的调用方式。

1.  fn() // TypeError: Cannot read property 'length' of undefined
2.  arguments[0]() // 2

第 1 行显示直接函数调用。对于此样式, this 值在严格模式下未定义,在非严格模式下为全局对象。

第 2 行显示函数作为方法调用。这是一个有点棘手的问题,因为我们更习惯于看到与方法一起使用的点运算符,但 括号属性访问器 表示法的工作方式类似。当您将函数作为方法调用时, this 值将设置为调用该方法的对象。此行为旨在支持面向对象风格的编程。

这类似于编写 arguments.0() ,但 JS 语法不允许这种序列(大概是由于小数位语法存在歧义)。

狡猾的面试问题!

Ben Aston
2020-10-21

此处 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
Bhavesh Ajani
2021-05-29