前言
本文章源自《JavaScript知识小册》专栏,感兴趣的话还请关注点赞收藏.
上一篇文章:《JavaScript中的作用域》
this指向
JavaScript
中的this
指向是面试常考问题,同时也是非常令人心烦的,因为它跟Java
这种纯粹面向对象的语言不太一样,Java
中的this
永远表示当前对象实例,而JavaScript
中的this
在不同情况下所指向的也不一样[个人觉得这其实无形中给开发者添加了不必要的心智负担...
],所以本文对this
在各种应用场景下的指向做一个汇总和梳理。
this
具体指向什么值,是在执行时动态决定的,而不是在编码时就已经被定义了。
普通函数
javascript
function print() {
console.log('this is ', this)
}
print()
首先是最简单的定义并调用一个普通函数,此时输出this
为Window
[因为是在浏览器中运行,所以当前this指向window全局对象,node.js环境不会指向window]
call/apply/bind
call
arduino
function print(){
console.log('this is ', this)
}
print.call({name: 'Bruse'})
使用call
的方式调用函数,可以改变函数中的this
指向,这里this
则指向了{name: 'Bruse'}
这个对象,输出this is {name: "Bruse"}
bind
arduino
function print(){
console.log('this is ', this)
}
print.bind({name: 'Bruse'})()
使用bind
修改this
指向,同样输出this is {name: "Bruse"}
,bind
的区别在于它其实是返回一个新的函数,并交由调用方决定在什么时候执行,而call
是更改this
指向同时立即执行函数
下边的示例中bind
返回的新函数可以不立即调用
arduino
function print() {
console.log('this is ', this)
}
const printV2 = print.bind({name: 'Bruse'})
console.log('call printV2')
printV2()
输出
kotlin
call printV2
this is {name: "Bruse"}
apply
arduino
function print() {
console.log('this is ', this)
}
print.apply({name: 'Bruse'})
使用apply
同样也可以修改this
的指向,输出this is {name: "Bruse"}
区别
同样是可以修改this
指向,apply
,bind
,call
之间还是有点区别的
call
是立即调用,第一个参数是this
指向,然后第二个参数是方法参数列表
arduino
function print(age, phone) {
console.log('this is ', this)
console.log('age is ', age)
console.log('phone is ', phone)
}
print.call({name: 'Bruse'}, 16, '135xxxxxx')
输出
kotlin
this is {name: "Bruse"}
age is 16
phone is 135xxxxxx
bind
是返回新的函数,但调用时机还是由调用方决定,第一个参数是this
指向,第二个参数是方法参数列表
arduino
function print(age, phone) {
console.log('this is ', this)
console.log('age is ', age)
console.log('phone is ', phone)
}
print.bind({name: 'Bruse'}, 16, '135xxxxxx')()
输出
kotlin
this is {name: "Bruse"}
age is 16
phone is 135xxxxxx
bind
传参还可以这么玩
php
// print.bind({name: 'Bruse'}, 16, '135xxxxxx')()
// 还可以是如下的方式
print.bind({name: 'Bruse'})(16, '135xxxxxx')
// or
const printV2 = print.bind({name: 'Bruse'})
printV2(16, '135xxxxxx')
apply
跟call
一样是立即调用,第一个参数是this
指向,然后第二个参数是一个数组,方法的参数都存放到这个数组中
arduino
function print(age, phone) {
console.log('this is ', this)
console.log('age is ', age)
console.log('phone is ', phone)
}
print.apply({name: 'Bruse'}, [16, '135xxxxxx'])
输出
kotlin
this is {name: "Bruse"}
age is 16
phone is 135xxxxxx
对象方法
执行对象方法时,this
即当前对象
javascript
const man = {
name: 'Bruse',
work(){
console.log(`${this.name} working...`)
}
}
man.work()
输出Bruse working...
但如果在对象方法中在setTimeout
setInterval
等回调代码中调用this
,会发现this
指向不再是当前对象。
javascript
const man = {
name: 'Bruse',
work() {
setTimeout(function () {
console.log(`${this.name} working...`)
}, 10)
}
}
man.work()
输出 working...
千万记住this
是在运行时动态决定的,而console.log(...)
调用的时候,并不是直接通过调用man
对象的work
方法,而是在setTimeout
触发的回调函数中调用的,所以这个时候this
指向的是window
对象。
只需要加上
ini
window.name = 'Chrome'
输出就会变为Chrome working...
箭头函数
箭头函数里无this。
记住这一句话就好了,比如下边的代码
javascript
const man = {
name: 'Bruse',
work() {
setTimeout(() => {
console.log(`${this.name} working...`)
}, 10)
}
}
man.work()
同样是在setTimeout
中调用console.log
,但是这次却输出Bruse working...
。
这是因为箭头函数中this永远指向的是上级作用域的this
,简单点理解就是箭头函数里没有this
,那么在调用console.log
时,就需要往上级作用域,也就是work
方法中找,而work
中的this
就是man
对象,所以顺理成章的this.name
就是man.name
class方法
还有一种则是在class
中的方法取this
javascript
class People {
constructor(name) {
this.name = name
}
work(){
console.log(`${this.name} do work...`)
}
}
const boy = new People('Bruse')
boy.work()
this
指向的是当前class
的实例,输出Bruse do work...
当然,如果还是遇到setTimeout
这样的,this
还是会在work
具体被调用的时候动态指向了window
javascript
window.name = 'Chrome'
class People {
constructor(name) {
this.name = name
}
work(){
console.log(`${this.name} do work...`)
}
}
const boy = new People('Bruse')
setTimeout(boy.work, 10)
输出Chrome do work...
而如果是箭头函数
的话,最后输出Bruse do work...
javascript
class People {
constructor(name) {
this.name = name
}
work () {
setTimeout(() => {
console.log(`${this.name} do work...`)
}, 10)
}
}
const boy = new People('Bruse')
boy.work()