JavaScript 的 this 原理

对 this 的定义

this 指的是函数运行时所在的环境

案例解析

来看一个经典案例:

1
2
3
4
5
6
7
8
9
10
11
12
var obj = {
foo: function () {
console.log(this.bar);
},
bar: 1,
};

var foo = obj.foo;
var bar = 2;

obj.foo(); // 1
foo(); // 2

我们平时经常用到的:

1
2
3
4
5
6
7
function name() {
console.log(this);
}
// 由于在最外层调用,一个网页的最外层就是 Window 对象
name(); // Window
// 由于这里的代码块由 元素#id 所调用,所以这里的this,会输出调用它的dom
document.querySelector("#id").onclick = name;

js 是如何定义变量的?

分两种情况,首先是栈类型
var obj = { foo: 5 }; JavaScript 引擎会先在内存里面,生成一个对象{ foo: 5 },然后把这个对象的内存地址赋值给变量 obj

也就是说 js 使用了两块内存空间 ‘obj’ 和 ‘{ foo: 5 }’

那么使用时,想要读取 obj.foo 拿到 5,引擎先从 obj 拿到内存地址,然后再从该地址读出原始的对象,返回它的 foo 属性

1
2
3
4
5
6
7
8
{
foo: {
[[value]]: 5
[[writable]]: true
[[enumerable]]: true
[[configurable]]: true
}
}

堆类型
var obj = { foo: function(){} }; 函数单独保存在内存中,然后再将函数的地址赋值给 foo

也就是说 js 使用了三块内存空间 ‘obj’, ‘{ foo }’ 和 function(){}

由于函数是一块单独的空间,所以可以在不同的上下文执行,这也能够解释了上面的案例

call, apply, bind 对 this 的影响

都是改变 this 的运行环境. 比如说我们平常用到的一些方法,查找数组中最大值

1
2
3
4
5
6
7
8
9
10
11
12
// 用上面的案例举例
var obj = {
foo: function () {
console.log(this);
},
};
obj.foo.call(window); // 本应输出 obj 的函数,改到了 Window
obj.foo.bind(window)(); // Window

// 查找数组最大值,这里对 this 没有用到影响,把null换成 window 或 Math 输出也是一样的结果
// 因为 apply 把参数转化为了 max 方法可接受的样子 Math.max(1, 2, 3), 这就是 和 call的区别
Math.max.apply(null, [1, 2, 3]);

ps:只是调用时会改变,不会永久改变

匿名函数

只单纯作为函数执行,没有本身的 this, 在 匿名函数中的 this 会向上层作用域查找 👆, 不支持 call, apply, bind 的改变

1
2
3
4
5
6
7
8
var obj = {
foo: () => {
console.log(this);
},
};
obj.foo(); // Window
obj.foo.call(obj); // Window
obj.foo.call(Array); // Window

总结

我们可以简单理解为,谁调用了一块 function,function 中的 this 就指向调用它的对象

作者

Huasun47

发布于

2020-06-07

更新于

2020-06-07

许可协议