文章目录
js 中 this 的指向非常绕, 在此记录下自己实践时总结的规律, 方便复习。
this 指向
总结
this 具体的指向是分情况的, 不同情况有不同的规律
非箭头函数场合:
- 普通函数、立即执行函数、定时器函数中 this 指向 window 对象
- 构造函数、对象的方法中 this 指向当前实例对象
- 事件绑定函数中 this 指向调用者
箭头函数场合:
- 箭头函数声明在对象内, 那么箭头函数中的 this 就指向该对象声明时代码所在作用域中的 this
- 箭头函数声明在实参处(既回调函数), 那么箭头函数中的 this 就指向宿主函数调用代码所在作用域中的 this
试验
1. 普通函数中 this 指向 window 对象
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>this 指向</title>
</head>
<body>
<script>
function pointer() {
console.dir(this)
}
pointer() // 结果: window对象
</script>
</body>
</html>
2. 立即执行函数中 this 指向 window 对象
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>this 指向</title>
</head>
<body>
<script>
// 立即执行函数, 方式 1
(function pointer() {
console.dir(this)
})() // 结果: window对象
// 立即执行函数, 方式 2
;(function pointer() {
console.dir(this)
}()) // 结果: window对象
</script>
</body>
</html>
3. 定时器函数中 this 指向 window 对象
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>this 指向</title>
</head>
<body>
<script>
// 延迟执行一次
window.setTimeout(function () {
console.dir(this)
}, 1000) // 结果: window对象
// 延迟后间隔执行
window.setInterval(function () {
console.dir(this)
}, 1000) // 结果: window对象
</script>
</body>
</html>
4. 构造函数中 this 指向当前实例对象
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>this 指向</title>
</head>
<body>
<script>
function Person(name, age) {
this.name = name
this.age = age
console.log(this)
}
new Person('ares5k', 27) // 结果:Person {name: "ares5k", age: 27}
new Person('3s', 27) // 结果:Person {name: "3s", age: 27}
</script>
</body>
</html>
5. 对象的方法中 this 指向当前实例对象
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>this 指向</title>
</head>
<body>
<script>
// 创建对象方式 1
function Person(name, age) {
this.name = name
this.age = age
this.pointer = function () {
console.log(this)
}
}
new Person('ares5k', 27).pointer() // 结果:Person {name: "ares5k", age: 27}
new Person('3s', 27).pointer() // 结果:Person {name: "3s", age: 27}
// 创建对象方式 2
let obj = {
sex: 0,
pointer() {
console.log(this)
}
}
obj.pointer() // 结果:{sex: 0, pointer: ƒ}
</script>
</body>
</html>
6. 事件绑定函数中 this 指向调用者
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>this 指向</title>
</head>
<body>
<input type="button" value="点我">
<script>
let btn = document.querySelector('input')
btn.addEventListener('click', function () {
console.dir(this)
})
</script>
</body>
</html>
7. 箭头函数 - 对象方法中 this 指向该对象声明时代码所在作用域中的 this
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>this 指向</title>
</head>
<body>
<script>
/*
* 场景 1
*/
// 箭头函数声明在对象中
let obj1 = {
fn: () => {
console.log(this) // 结果: window 对象
}
}
// obj1 对象声明代码所在作用域中的 this 是 window 对象
console.log(this)
// 调用箭头函数
obj1.fn()
/*
* 场景 2
*/
let obj2 = {
fn: function () {
// return 对象声明代码所在作用域中的 this 是 obj2 对象
console.log(this)
// 箭头函数声明在对象中
return {
innerFn: () => {
console.log(this) // 结果: obj2 对象
}
}
}
}
// 调用箭头函数
obj2.fn().innerFn()
</script>
</body>
</html>
8. 箭头函数 - 实参(既回调函数)this 指向宿主函数调用代码所在作用域中的 this
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>this 指向</title>
</head>
<body>
<script>
let obj1 = {
fn: function () {
// 宿主函数调用代码所在作用域, 该作用域中的 this是 obj1 对象
console.log(this) // 结果: obj1 对象
// 箭头函数声明在宿主函数的实参中
window.setTimeout(() => {
console.log(this) // 结果: obj1 对象
})
}
}
// 调用箭头函数
obj1.fn()
</script>
</body>
</html>
改变 this 指向
箭头函数中的 this 指向无法修改。
修改 this 指向 - call
call 函数的第一个参数, 就是用来替换原 this 的, 后面的参数是可选的, 参数个数取决于目标函数的参数列表, 每个参数用逗号隔开, call 函数调用后, 会立即执行目标函数。
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>this 指向</title>
</head>
<body>
<script>
// 声明一个对象
let person = {
name: 'ares5k',
age: 27
}
// 声明一个函数
function changePointer(param1, param2) {
console.log(`参数列表: ${param1}、${param2}`)
console.log(`当前对象: ${this}`)
}
// 直接调用函数, this 指向 window
changePointer('参数1', '参数2')
// 使用 call 调用函数, 可以将 this 的指向修改
changePointer.call(person, '参数1', '参数2')
</script>
</body>
</html>
修改 this 指向 - apply
apply 函数的第一个参数, 就是用来替换原 this 的, 第二个是可选数组, 数组长度取决于目标函数的参数列表, 每个参数都是数组中的一项, apply 函数调用后, 会立即执行目标函数。
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>this 指向</title>
</head>
<body>
<script>
// 声明一个对象
let person = {
name: 'ares5k',
age: 27
}
// 声明一个函数
function changePointer(param1, param2) {
console.log(`参数列表: ${param1}、${param2}`)
console.log(`当前对象: ${this}`)
}
// 直接调用函数, this 指向 window
changePointer('参数1', '参数2')
// 使用 apply 调用函数, 可以将 this 的指向修改
changePointer.apply(person, ['参数1', '参数2'])
</script>
</body>
</html>
修改 this 指向 - bind
call 和 apply 都是调用后立即执行目标函数, 这样有个缺点就是没法当作回调函数使用。下面例子中, 虽然确实改变了 this 指向,但是本来想用 setTimeout 延迟 5 秒执行, 而因为使用了 call,所以变成了立即执行。
使用 bind 的方式来修改 this 指向就可以很好的解决这个问题, bind 调用后并不会立即执行目标函数, 而是会返回一个新的函数, 然后新函数内部的 this 就是修改后的对象。这种特点,使其非常适合修改回调函数中 this 的场合。
效果:
代码:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>this 指向</title>
</head>
<body>
<div></div>
<script>
let div = document.querySelector('div')
// 声明一个对象
let person = {
name: 'ares5k',
age: 27
}
// 声明一个函数
function changePointer(param1, param2) {
div.innerText = `参数列表: ${param1}、${param2}\n当前对象: ${this}`
}
setTimeout(changePointer.bind(person, '参数1', '参数2'), 2000)
</script>
</body>
</html>