Hokori 其实吧,有疑惑是很正常的,因为 JS 里的 this 虽然是个关键字,行为却不是固定的,跟个变量一样。你在一楼写的神奇代码我在刚入门研究 prototype 的时候也写过无数(逃。我觉得关键还是要当成科学实验,用好控制变量法。比如这个问题“console.log(this) 的输出为什么不是 console”,可以从不同的角度来一点点试:
第一组:我就要它输出 console
console.log(this);
console.log(console);
第二组:听说可以改变指向,改成 console 再试试
console.log(this);
console.log.bind(console)(this);
console.log.bind(console)(console);
这两组会发现只有传了 console 的才会输出 console ,传 this 的都输出了 window ,即使把 log 函数里的 this 指向改变了也没用。这个时候可能会发现是传参的问题,也可能没发现,那么在假设 console.log 没 bug 的情况下,有可能是 this 出了问题。把 this 包装下:
第三组:包装不同的 this 给 console.log 输出
function a() { return this; }
var b = () => { return this; };
var c = { foo: function () { return this; } };
var d = { foo: () => { return this; } };
console.log(a());
console.log(b());
console.log(c.foo());
console.log(d.foo());
这里控制的“变量”是同样的 console.log 函数,不同执行环境下的 this 。当然我们现在是假设 console.log 没有 bug ,但它的嫌疑也不能排除。因为直接在 F12 运行语句也能得到返回值,所以可以与 console.log 的效果进行对比:
第四组:不经过 console.log 直接输出(注意:由于直接运行只显示最后一句的返回值,所以要分开四次运行)
a();
b();
c.foo();
d.foo();
会发现第三组第四组得到的结果是一样的,都只有第三个 c.foo() 输出了 { foo: f } 也就是 c 的内容,其他都输出了 window 。再结合已知的知识:
箭头函数不绑定 this ,会捕获其所在的上下文的 this 值,作为自己的 this 值
猜测 a() 和 b() 得到 window 是因为它们是在 F12 里面执行,默认在 window 下,所以相当于 window.a() 和 window.b() 那么无论绑不绑定都只能指向 window 了。

而 c.foo() 没用箭头函数,说明它绑定了 this ,绑定了谁呢?它现在是 c ,有没有可能是绑定了 c ?
而 d.foo() 用了箭头函数,按道理它不会绑定 this ,但它却是个 window ,联想上面 window.b() 的结论,它也相当于 window.d.foo() ,有没有可能就是因为用了箭头函数,导致它跳过了 d 才绑定了 window ?
于是现在得到一个猜想: x.foo() 的形式会把 this 值绑定到 x 上。
那为什么 console.log(this) 没有绑定到 console 上呢?再次控制变量,观察它们的区别:
第五组:观察 this 绑定的异同
var c = { foo: function () { return this; } };
console.log(this);
console.log(c.foo());
this;
c.foo();
发现在输出语句中明确写了 this 的都输出 window ,没写的输出了 c (作为与输出 console 对比),有没有可能是 this 关键字在代码中位置的问题?对比发现输出了 window 的都写在了函数形参上,另一个则写在了函数体内部。有可能是传参的问题!那么验证下猜想吧。
第六组:把 this 写在 console.log 里面( 1 ~ 5 还是分开一次一次地执行 )
console.log(this); /* 1 */
// 替换掉 console.log 的实现
console.a = console.log;
console.log = function () {
// 恢复原本的行为
console.a.apply(console, arguments);
// 由于在验证 console.log ,用 console.a 输出也不可靠,直接返回出去再观察 this 值
return this;
};
console.a(this); /* 2 */
console.a.bind(console)(this); /* 3 */
console.log(); /* 4 */
console.log(this); /* 5 */
可以看到对照 1 还是原本的行为,输出了 window ;对照 2 / 3 与对照 1 一致,表明 console.a 的行为还是符合原本替换前 console.log 的;对照 4 无输出并返回了 console ,对照 5 输出 window 并返回了 console ,说明写在函数内的 this 才会绑定 console ,写在形参的 this 在传参前是什么值(这里是 window )它输出后还是什么值。
至此,我们确定了 console.log 和 this 都没有任何问题,在 console.log(this) 中不输出 console 是因为 this 在形参上时,不会被绑定到 caller (即 console )上。验证它是个通用的结论:
const a = {
sayLog: function (x) {
console.log(x);
console.log(this);
}
}
a.sayLog(this);
撒花~~
注 1 :我没接受过正经的科研训练,请不要把本文相关思想当作真正的控制变量法。
注 2 :@0x0001 话说,可以加精吗,可以开发个加精吗(逃