this 到底指向谁
了解 this 的指向
函数外的 this
- 在浏览器中运行
1 | console.log(this); // window |
结果得到的是 window 对象。
- 在
Node下运行
1 | console.log(this); // null |
结果得到的是空对象。
但发现:
1 | console.log(this === module.exports); //true |
这个空对象实际上是模块的导出对象。
- 在
Web Worker下运行
新建一个文件,名为 worker.js 代码如下:
1 | console.log(this); // self |
在另一个文件中引入:
1 | let worker = new Worker('./worker.js'); |
结果 this 指向 self 对象。
- 以
ES6模块运行:
1 | <script type="module"> |
得到的结果为 null。
在这几种情况下,即使开启严格模式,得到的结果是不变的。
小结一下:
| 环境 | 浏览器 | Node.js | Web Worker | ES6 Module |
|---|---|---|---|---|
| 非严格模式 | window | module.exports | self | undefined |
| 严格模式 | window | module.exports | self | undefined |
函数中的 this
对于一个函数来说,它的 this 指向就比较复杂了。
- 先看一个最简单的例子:
1 | function outputThis() |
这种情况下:[1]
| 环境 | 浏览器 | Node.js | Web Worker | ES6 Module |
|---|---|---|---|---|
| 非严格模式 | window | global | self | undefined |
| 严格模式 | undefined | undefined | undefined | undefined |
对于非严格模式的情况,这些 this 有一个统一的名称 globalThis,用来表示全局作用域下的 this。
- 如果把函数挂载到一个对象上:
1 | function outputThis() |
- 对于对象中的属性和方法:
1 | let obj = { |
这种情况下:
- 属性直接使用对象外的
this - 若方法挂载到了对象
obj内,那么this为所挂载的对象obj - 若方法没有挂载到对象上,那么
this则为globalThis
如果对象中又嵌套了对象:
1 | let obj = { |
这种情况下,outputThis 挂载到了 obj.foo 对象上,this 的值为 foo 对象。
再看一个更复杂的例子:
1 | let obj = { |
这种情况,将 outputThis 赋值给变量后,就直接调用,而没有挂载到哪个对象,结果便是 globalThis。
总结一下 :
- 对于函数调用,函数调用时挂载到了哪个对象上,
this就指向哪个对象,否则指向globalThis。 - 函数中的
this与函数所在作用域无关。
new 运算符与 this
当使用 new 调用函数时,this 指向一个新创建的对象。
1 | function Foo() |
箭头函数中的 this
箭头函数没有自己的 this,它使用的是箭头函数外的 this。
1 | function foo() |
小结
说简单点,谁调用函数,this 就指向谁。
说专业点,this 的指向,是在调用函数时根据执行上下文所动态确定的。[2]
修改 this 的指向
修改的方法
在 javascript 中, this 的指向过于灵活,甚至是可以修改的。
javascript 的函数对象提供了三种方法用于修改 this:
func.apply(thisArg, args)func.call(thisArg, ...args)func.bind(thisArg, ...args)
thisArg 是函数中的 this,而 args 则是函数的参数列表。
对于 apply 和 call,修改 this后,它将立即调用这个函数:
1 | function func(x,y) |
对于 bind,则返回一个新的函数,新的函数调用时,bind 函数的 args 参数将和新函数的参数一起,作为函数的参数进行调用:
1 | function func(x,y) |
对于箭头函数, thisArg 将不起任何作用,因为它没有自己的 this:
1 |
|
如果 thisArg 传递的不是对象,那么它会在内部自动转换成对象:
1 | function func() |
如果 thisArg 是 null 或 undefined, 那么:
1 | function func() |
new 和 bind 的优先级
如果我们把手动 bind 的函数使用 new 运算符、会发生什么?
1 | function Foo() |
结果,手动绑定的 this 便被丢弃了。
这意味着,new 运算符中绑定的 this 占据了更高的优先级.[5:1]
手动实现
前面说到了 bind、call、apply 这三个函数可以修改 this 的指向,那么它们具体是怎么实现的呢?
不要忘了这个结论:对于函数调用,函数调用时挂载到了哪个对象上,this 就指向哪个对象,否则指向 globalThis。
也就是说,如果我们想要修改 this,只需要把函数挂到对象上就可以了。
实现apply
下面我们来实现一个简单的 apply:
1 | Function.prototype.myApply = function(thisArg, args) |
实现call
call 和 apply 的实现是差不多的:
1 | Function.prototype.myCall = function(thisArg, ...args) |
实现bind
对于 bind,它的实现可能稍微"复杂"一点:
1 | Function.prototype.myBind = function(thisArg, ...args) |
- 标题: this 到底指向谁
- 作者: ObjectKaz
- 创建于: 2021-04-26 04:01:11
- 更新于: 2021-04-28 03:19:53
- 链接: https://www.objectkaz.cn/3e703c16f149.html
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。