彻底搞懂this的指向问题(JavaScript的this)

前言

为什么要有this

首先你应该先了解执行上下文的概念,例如下面这个函数,javaScript运行在函数体内部,引用当前上下文的其他变量。

function func() {
    
    
  console.log(a);
}

问题是:函数的调用可以在任何其他执行上下文中被调用,因此a的指向就不同了,因此我们需要依靠this来优雅地指向当前代码运行时所处的上下文环境

优雅的准确的

看下面这段代码,func1()中是不是会误导你认为这个name是全局的name,而func2()就清晰的让你知道,这个name,是局部的name,其实两者都是指向局部的name,而func2()的方式明显更优雅。假如对象的名字很长,很容易就产生误导。

//假设有个对象名字很长,而且有可能会改名
var iAmALongLongLongNameObject={
    
    
    name:"coffe",
    func1(){
    
    
     //如果光看代码,是不是容易看成调用了全局变量的name?
        return iAmALongLongLongNameObject.name;
    },
    func2(){
    
    
     //这里光看代码就很准确地知道是调用了局部变量的name!
        return this.name;
    }
}

this的指向规则

函数是否在new中被调用,指向新创建的对象

如果是的话,this 绑定的是新创建的对象
特殊情况:SetTimeout或SetInterval结合使用

function func(name) {
    
    
  this.name = name;
  this.getName = function() {
    
    
    return this.name;
  };
}

var obj = new func("coffe"); //this会指向obj
console.log(obj.getName()); //>> coffe

函数是否通过call、apply、bind显式指向,指向的是call、apply、bind三个方法的第一个参数指定的对象

如果是的话,this指向的是call、apply、bind三个方法的第一个参数指定的对象。

var obj1 = {
    
    
  name: "coffe"
};
function func() {
    
    
  return this.name; //这里的this本来指向window
}
var str = func.call(obj1); //改变了func函数里面this的指向,指向obj1
console.log(str); //>> coffe
var num = 0;
class Obj {
    
    
    constructor(num){
    
    
        this.num = num;
    }
    func(){
    
    
        console.log(this.num);
    }
    func1(){
    
    
        setTimeout(function () {
    
    
            console.log("setTimeout:"+this.num);
        }.bind(this), 1000);//bind
    }
    func2(){
    
    
        setInterval(function () {
    
    
            console.log(this.num);
        }.bind(this), 2000);//bind
    }
}
var obj = new Obj(1);
obj.func();//>> 1              输出的是obj.num
obj.func1()//>> setTimeout:1   输出的是obj.num
obj.func2()//>> 1 1 1 1 ……     输出的是obj.num

函数是否被当做某个对象的方法而调用(隐式指向)

var obj1 = {
    
    
  name: "coffe",
  func() {
    
    
    return this.name; //指向obj1
  }
};

//这里的obj1.func(),表明func函数被obj1调用,因此func中的this指向obj1
console.log(obj1.func()); //>> coffe

若以上都不是的话,使用默认绑定,绑定到全局对象

function func() {
    
    
    console.log( this.a ); // this指向全局对象
}
var a = 2;
func(); //>> 2
var num = 0;
class Obj {
    
    
    constructor(num){
    
    
        this.num = num;
    }
    func(){
    
    
        console.log(this.num);
    }
    func1(){
    
    
        setTimeout(function () {
    
    
            console.log("setTimeout:"+this.num);
        }, 1000)
    }
    func2(){
    
    
        setInterval(function () {
    
    
            console.log(this.num);
        }, 2000)
    }
}
var obj = new Obj(1);
obj.func();//>> 1              输出的是obj.num
obj.func1()//>> setTimeout:0   输出的是window.num
obj.func2()//>> 0 0 0 0 ……     输出的是window.num

箭头函数

箭头函数并不是使用function关键字定义的,而是使用被称为“胖箭头”的操作符 => 定义的。

箭头函数不遵守this的四种指向规则,而是根据函数定义时的作用域来决定 this 的指向。何谓“定义时的作用域”?就是你定义这个箭头函数的时候,该箭头函数在哪个函数里,那么箭头函数体内的this就是它父函数的this。
一旦被确定,就不可更改,一“箭”钟情!

function func() {
    
    
  // 返回一个箭头函数
  return a => {
    
    
    //this 继承自 func()
    console.log(this.a);
  };
}
var obj1 = {
    
    
  a: 2
};
var obj2 = {
    
    
  a: 3
};

var bar = func.call(obj1);
bar.call(obj2); //>> 2         不是 3 !

// func() 内部创建的箭头函数会捕获调用时 func() 的 this。
// 由于 func() 的 this 绑定到 obj1, bar(引用箭头函数)的 this 也会绑定到 obj1,
// this一旦被确定,就不可更改,所以箭头函数的绑定无法被修改。(new 也不行!)

注意

严格模式

对于默认指向来说,决定this指向对象的并不是调用位置是否处于严格模式而是被调用的函数体是否处于严格模式。如果被调用的函数体处于严格模式并且本身没有该属性,this会指向undefined,否则this会指向全局对象,记住,处于严格模式下,要么是指向全局对象,要么就是undefined。

var a = "coffe"; //为全局对象window添加一个属性a
function func() {
    
    
  "use strict";//开启严格模式
  return this.a;
}

//严格模式下,this指向undefined
console.log(func()); //>> TypeError
function func() {
    
    
  console.log(this.a);
}

var a = "1891";
(function() {
    
    
  "use strict";
  func(); //>> 1891
  //这里输出 1891 而不是报错,是因为严格模式下,this的指向与func的调用位置无关
})();
    var a = "coffe"; //为全局对象window添加一个属性a
    function func() {
    
    
    let a = 1
    "use strict";//开启严格模式
    return this.a;
    }

    //严格模式下,如果函数体内部也存在相同的变量,那么该变量指向全局对象
    console.log(func()); //>> coffe

SetTimeout或SetInterval结合使用

var num = 0;
class Obj {
    
    
    constructor(num){
    
    
        this.num = num;
    }
    func(){
    
    
        console.log(this.num);
    }
    func1(){
    
    
        setTimeout(function () {
    
    
            console.log("setTimeout:"+this.num);
        }, 1000)
    }
    func2(){
    
    
        setInterval(function () {
    
    
            console.log(this.num);
        }, 2000)
    }
}
var obj = new Obj(1);
obj.func();//>> 1              输出的是obj.num
obj.func1()//>> setTimeout:0   输出的是window.num
obj.func2()//>> 0 0 0 0 ……     输出的是window.num

猜你喜欢

转载自blog.csdn.net/m0_46983722/article/details/130480633