前言
相信你一定被 javaScript 中 this 指向迷惑过。为什么这里 this 是这样,为什么这里的 this 是那样?
小小的脑袋,大大的疑问。
指向运行时判定
javaScript 一开始并不知道当前的 this 指向,必须在运行时才能得知当前的 this 指向。
所以也就有了这样一句话,谁调用就指向谁。
这样一句话并不能我们对 this 指向问题有一个深刻的认识,那我们进行举例一个个认识。
html 篇
直接打印
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<button type="button" onclick="console.log(this)">查看 this</button>
</body>
</html>
这时候打印的是 button 元素。onclick 事件由 button DOM 元素进行调用。
借助函数打印
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<button type="button" onclick="sayThis()">查看 this</button>
</body>
<script>
function sayThis(){
console.log(this)
}
</script>
</html>
这时候打印的是 window 对象。
注意:这里的 onclick 调用的是 sayThis(),JS 应该是在外层包了一层 function。故这里真正执行的是 window.sayThis()。如果不能理解,可见下方例子:
let temp = {
test:function(){
test()
}
}
function test(){
console.log(this)
}
temp.test()
temp 里的 test 函数执行是通过当前的 global 环境进行执行的(为什么不说 window,因为这里还考虑到了 node.js 平台)。
利用 DOM 操作去打印
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<button type="button">查看 this</button>
</body>
<script>
var button = document.getElementsByTagName('button')[0]
button.onclick = sayThis
function sayThis(){
console.log(this)
}
</script>
</html>
这时候打印的是 button 元素。
这个不难理解,由 button DOM 元素进行调用。
构造函数篇
let name = '张三'
function Person(name) {
this.name = name
}
let people = new Person('李四')
console.log(people.name)
这里打印的为李四。
对于构造函数来说,this 将被绑定到被实例化的对象上。
箭头函数篇
箭头函数并不具有 this,而是根据定义的环境自动绑定上 this。
let a = 0
let temp = {
a: 1,
b: 2,
test() {
console.log(this.a)
},
}
temp.test()
这里打印的为 1,因为由 temp 调用的 test 函数。
let a = 0
let temp = {
a: 1,
b: 2,
test: () => {
console.log(this.a)
},
}
temp.test()
这里打印的为 0(注意这里只是 web 端,Node 端为 undefined),因为箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this。
函数回调篇
在对一些原生方法传递函数时,this 指向并不会像我们期望那样发生变化。例如:setTimeout
var name='window'
function hello(){
setTimeout(function(){
console.log(this.name)
}, 100);
}
let obj={
name:'obj',
hello
}
// widow
obj.hello()
setTimeout等传递函数方法是一种特殊情况,setTimeout中的this都指向window(同样这里只是适用于 web 端)。
同理数组里的 map、filter等也是如此。
Node 篇
直接打印 this
// {}
console.log(this)
在 Node 中直接打印 this,我们只会得到一个空对象
map、filter等函数回调
let arr = [1]
arr.filter(function(item){
// global 对象
console.log(this)
})
这里比较特殊返回的是 global 对象
setTimeout、setInterval
setTimeout(function(){
// Timeout 对象
console.log(this)
})
这里更加特殊返回的是 Timeout 对象。
总结
在 web 端中 this 指向,其实我们只需奉行:
- 谁调用 this 指向谁
- 注意箭头函数的上下文绑定
- 注意 JS 方法里的回调函数的 this
而在 node 端我们需注意更多:
- 谁调用 this 指向谁
- 注意箭头函数的上下文绑定
- 注意 this 的直接打印
- 注意 JS 方法里的回调函数的 this(不包括setTimeout、setInterval)
- 注意 setTimeout、setInterval