题目 23 / 31

链式调用末尾的 .call() 绑定的是外层调用还是内层调用?

知识点: this 绑定难度: 高级

call 只会附着在它直接调用的那个具体函数值上,且只在那一次调用生效。

使用执行可视化器解题在下方逐步观察每个绑定、函数调用和控制台输出。

预测输出结果

预测三组共六行输出。每一行都先调用 foo,再立即调用 foo 返回的函数——请对每一行分别推演。

var obj = {
  a: 'obj',
  foo: function () {
    console.log('foo:', this.a);
    return function () {
      console.log('inner:', this.a);
    };
  }
};
var a = 'window';
var obj2 = { a: 'obj2' };

obj.foo()();
obj.foo.call(obj2)();
obj.foo().call(obj2);
查看答案与完整解析

正确输出

foo: obj
inner: window
foo: obj2
inner: window
foo: obj
inner: obj2

为什么会这样

obj.foo()() 先以 obj.foo() 的形式调用 foo——隐式绑定,因此 foo: obj。这次调用返回一个普通的内部函数,随后它被裸调用,没有接收者,于是 inner: window(默认绑定)。

obj.foo.call(obj2)() 在第一次调用时就显式把 foo 自己的 this 绑定为 obj2,所以 foo: obj2。但返回的内部函数依然是作为一次独立的、无接收者的调用被执行的,所以还是 inner: window——前面的 .call(obj2) 对它没有任何影响。

obj.foo().call(obj2) 先以隐式绑定调用 foo(foo: obj,不受后面任何操作影响),然后才把 .call(obj2) 链接到返回的内部函数上,只对这一次具体的调用强制 inner: obj2。

这道题考察什么

  • call/apply/bind 只对它们被调用的那个具体函数值的这一次调用生效
  • 返回的闭包是一个独立的函数值,拥有自己单独决定的 this
  • 从左到右阅读调用链,就能准确看出每个 .call() 附着在哪次调用上

运行环境前提

假设运行在经典的非严格模式脚本中;返回的内部函数是普通函数,裸调用时依然遵循默认绑定。

规范参考

JavaScript 代码编辑器支持直接编辑或粘贴自定义代码
第 0 步 / 共 0 步
单步Space播放
按 → 单步 或 Space 自动播放
阶段 1: 编译预解析(变量提升与内存分配)
阶段 2: 代码执行(逐行赋值与调用求值)
单步执行原理解析

按键盘右方向键 “→” 进行单步调试,或按空格键 “Space” / 点击 “▶ 自动播放” 进行连续执行。