关于JS --- this 的理解

157 阅读3分钟

一、this到底指向谁

在JS语言中,this的指向非常灵活、使用场景多样;
通常我们会理解为 “谁调用thisthis便指向谁”,这么说大多情况下都是适用的,但是不够全面;经过长期的学习,我总结出以下几种情况:

- 在全局环境下调用函数,函数体中的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)
    })
  }
}