一、this到底指向谁
在JS语言中,this的指向非常灵活、使用场景多样;
通常我们会理解为 “谁调用this,this便指向谁”,这么说大多情况下都是适用的,但是不够全面;经过长期的学习,我总结出以下几种情况:
- 在全局环境下调用函数,函数体中的this指向:
- 严格模式下,this 绑定在undefind上
- 非严格模式下,this绑定在window/global上
一个简单例子:
// 在全局环境中
function f1(){
console.log(this);
}
function f2(){
'use strict';
console.log(this);
}
f1()// 在浏览器环境下输出window,nodejs环境下输出global
f2()// undefined
- 通过new调用构造对象,构造函数内的this被绑定在新创建的对象上
一个简单例子:
function Foo(){
this.name = 'faye'
}
const instance = new Foo()
console.log(instance.name) // faye
上述输出结果很容易得出,但new一个复杂对象的时候,我们的判断就会变得模糊起来,如果需要弄清楚,则需要引申一个问题,new 过程到底做了什么? 以下是我的理解:
- 创建一个新的对象
- 将构造函数的this指向这个对象
- 为这个对象添加属性
- 最终返回这个新的对象
转换成代码如下所示:
function Foo(){} //一个构造函数
let instance = new Foo() //生成实例 、
//过程如下:
let instance = undefind //声明 instance
var obj = {} //创建一个新的对象
obj.__proto__ = Foo.prototype //将构造函数的this指向这个对象
instance = Foo.call(obj) //为这个对象添加属性
明白了new过程操作了什么后,回归正题,笔者认为有一点需要注意的是,构造函数中出现了显示return,this指向又被细分为两种情况
场景一:此时instance返回的是空对象obj
function Foo(){
this.name = 'faye'
let obj = {}
return obj
}
const instance = new Foo();
console.log(instance.name)//undefind
场景二:此时返回的目标对象是this
function Foo(){
this.name = 'faye'
return 1
}
const instance = new Foo();
console.log(instance.name)//faye
通过两个场景,可以明显得出结论,如果构造函数中显示的返回一个对象(复杂类型),那么this就指向这个对象,如果返回的不是一个对象(基本类型),那么this仍然指向实例
- 通过call、apply、bind方法显示调用时,this绑定在指定参数的对象上
注意:箭头函数没有this,所以这个结论在箭头函数上不成立;
//
function foo(a){
console.log(this.a)
}
const obj1 = {a:1,foo:foo}
const obj2 = {a:2,foo:foo}
obj1.foo.call(obj2) //2
obj2.foo.call(obj1) //1
foo.bind(obj1)
foo()//1
- 一般通过上下文对象调用时,函数内的this会被绑定在该对象上
//
const foo = {
bar:1,
fn:function(){
console.log(this)
}
}
var fn1 = foo.fn
foo.fn() //{bar: 1, fn: ƒ}
fn1() // 在浏览器环境下输出window,nodejs环境下输出global
上述代码中,foo.fn()执行的上下文是foo对象,this自然指向foo内部, 而fn1的执行上下文是全局环境,fn虽然是foo对象中的一个属性,但是经过赋值后,this指向全局从而会输出winodw or gloabl
复杂的嵌套也同理:
//
const foo = {
name:'faye',
album:{
name:'Di-Dar',
music:{
name:'流星',
fn:function(){
console.log(this.name)
}
}
}
}
foo.album.music.fn() //流星
- 在箭头函数中,this的指向是由外层(函数or全局)作用域来决定
this出现在setTimeout的函数中,因此this指向全局
const foo = {
fn:function(){
setTimeout(function(){
console.log(this)
})
}
}
foo.fn() //window or global
使用箭头函数,this指向执行作用域的上下文中,所以指向foo
const foo = {
fn:function(){
setTimeout(() =>{
console.log(this)
})
}
}