在上一篇文章中搞明白this指向,走遍天下都不怕(一)我们讲了全局环境中的this
和对象调用中的this
。
今天我们接着来看剩下其他的bind\call\apply、构造函数、箭头函数中的this指向问题。
bind\call\apply改变this指向
一般面试都会很喜欢问bind\call\apply的区别,这个问题也比较基础,总结来说就是它们都是用来改变函数this指向的,call和apply是直接进行函数调用;bind不会立即执行函数,而是返回一个新的函数,这个新函数已经自动绑定了新的this指向。剩下的call和apply就是传入参数的区别。
请看下面的示例
function fn(a, b) {
console.log(this)
console.log(a, b)
}
// call
const obj1 = { a: 1 }
fn.call(obj1, 'a', 'b')
// apply
const obj2 = { a: 2 }
fn.apply(obj2, ['a', 'b'])
// bind
const obj3 = { a: 3}
fn.bind(obj3, 'a', 'b')()
// 依次输出
// {a: 1}
// a b
// {a: 2}
// a b
// {a: 3}
// a b
基础面试题
const user = {
name: 'shuai',
print: function() {
console.log(this.name)
}
}
const student = {
name: 'guan'
}
console.log(user.print.call(student)) // ?
通过call函数将print函数内的this指向改到了student对象上,所以这道题的答案将输出guan
。
构造函数和this
new操作符调用构造函数时做了什么
function User() {
this.name = 'shuai'
}
const user = new User()
console.log(user.name) // ?
上面代码将会输出shuai
。
这道题也是面试中的常考题,简单总结来说
- 创建了一个新的对象
- 将构造函数内的this指向这个新对象
- 为这个对象添加属性和方法等
- 最后返回这个对象
上面的描述也可以用代码来描述
const obj = {}
obj.__proto__ = User.prototype
User.call(obj)
上面的内容只是最简单、最基本的版本。
我们接着来看两种特殊的情况,在构造函数内出现了return的情况
构造函数内return一个对象
function User() {
this.name = 'shuai'
const obj = {}
return obj
}
const user = new User()
console.log(user.name) // ?
上面的代码将输出undefined
,这时我们调用new User()
返回的是空对象obj
。
构造函数内return一个基本类型值
function User() {
this.name = 'shuai'
return 1
}
const user = new User()
console.log(user.name) // ?
运行上面的代码我们会发现将输出shuai
,也就是这时我们调用new User()
返回的是目标对象this,而是是返回1
。
所以这里要记住
如果构造函数中返回一个对象(复杂类型),那么this就会指向这个返回的对象;如果返回的不是对象,而是基本类型的值,那么this仍然会指向这个实例。
箭头函数中的this
普通函数
const obj = {
fn: function() {
setTimeout(function() {
console.log(this)
})
}
}
console.log(obj.fn()) // ?
在上面的代码中,由于this
出现在setTimeout
的匿名函数中,所以this指向window。
这个和我们昨天讲的是一样的
如果函数是被上一级对象调用那么this就指向上一级这个对象,否则就指向全局环境。
那么如果我们想要这个this指向到obj对象的话,就可以用箭头函数来解决。
箭头函数
const obj = {
fn: function() {
setTimeout(() => {
console.log(this)
})
}
}
console.log(obj.fn()) // ?
这里我们需要了解箭头函数的相关知识,在箭头函数中,this的指向是由外层函数或者全局作用域来决定。在本道题目中,setTimeout
中的匿名箭头函数的this
就由它的外层函数也就obj.fn
函数的作用域来决定,obj.fn
函数内的this
指向就是obj
对象,所以setTimeout
中的匿名箭头函数的this
也就是obj
对象。
今日总结
- 在函数中,严格模式this会被绑定到undefined上,非严格模式会被绑定到window全局对象
- 通过上下文对象调用函数时,函数内的this会被绑定到该对象上
- 通过new操作符调用构造函数时,构造函数内的this会被绑定到新创建的对象上
- 通过bind\call\apply方法调用函数时,函数内的this会被绑定到指定参数的对象
- 在箭头函数中,this的指向是由外层函数或者全局作用域来决定的
欢迎我的公众号【小帅的编程笔记】,让我们在前端的路上越走越远