• js中this的指向问题


    this指向

    1、全局对象的 this 指向

    全局对象的 this 指向 window 对象(所在作用域?)

      var a = 1000;
      var obj = {
          a: 1,
          b: this.a+1
      }
      //普通对象 obj 没有 this,里面访问的 this指向 window 对象。
      console.log(obj.b);//1001
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2、全局作用域或者普通函数中 this 指向全局对象 window

    如果一个函数中有this,但是它没有被上一级的对象所调用,那么this指向的就是window

    //直接打印
    console.log(this) //window
    
    //function声明函数
    function bar () {console.log(this)}
    bar() //window
    
    //function声明函数赋给变量
    var bar = function () {console.log(this)}
    bar() //window
    
    //自执行函数
    (function () {console.log(this)})(); //window
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    函数中调用另一个函数

      function foo() {
        console.log(this); // window
      }
      function bar() {
        console.log(this); // window
        // 直接调用
        foo();
      }
      // 直接调用
      bar();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    全局函数执行 this 指向 window

      function fun(){
          console.log(this === window)   //true
      }
    
    • 1
    • 2
    • 3

      var name = "William";
      function test(){
          var name = "Lisi";
          let a = {
              name: this.name
          }
          console.log(a.name);
      }
      test();//William
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    3、this 永远指向最后调用它的那个对象

    this 永远指向最后调用它的那个对象,适用于非箭头函数的调用
    (隐式绑定:函数还可以作为某个对象的方法调用,这时this就指这个上级对象)

      var a = 1000;
      function fun(){
          var obj = {
              a: 1,
              c: this.a + 2
          }
          return obj.c;
      }  
      //fun 函数执行的时候内部 this 指向 window
      console.log(fun());//1002
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
      let obj = {
          name: "William",
          say: function(){
              console.log(this.name);
          }
      }
      //say 函数在执行的时候,是被 obj 对象调用的,所以指向的是 obj 对象,this.name 访问的是 obj.name
      obj.say();//William
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    var o = {
        a:10,
        b:{
            fn:function(){
                console.log(this.a); //undefined
            }
        }
    }
    o.b.fn();//undefined
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    分析:这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象

    var o = {
        a:10,
        b:{
            a:12,
            fn:function(){
                console.log(this.a); //undefined
                console.log(this); //window
            }
        }
    }
    var j = o.b.fn;
    j();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    分析:结合第七点看,同时,记住,this永远指向的是最后调用它的对象,虽然fn是对象b的方法,但是fn赋值给j时候并没有执行,所以最终指向window

    var obj = {
        number: 1,
        getOwnNumber: function () {
            var number = 2;
            return this.number;
        },
        getNumber: function () {
            var number = 3;
            return function () {
                var number = 4;
                return this.number;
            };
        }
    }
    console.log(obj.getOwnNumber());//1
    console.log(obj.getNumber()());//undefined
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    分析:

    obj.getNumber()运行结果是一个匿名函数,然后再执行匿名函数
    过程拆分为:
    var fun = obj.getNumber() // 返回一个匿名函数
    fun();
    所以此处this指向window对象

    4、new 关键词改变了 this 指向

      const a = 1000;
      function f1(a, b){
          this.a = a;
          this.b = b;
          console.log(this===window);
      }
      f1();                                  //true
      const f = new f1(1,2);                //false
      console.log(f.a);//1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    分析:
    new 操作符做了什么?

    1、首先内部创建了一个空对象 obj
    2、将新对象的__proto__指向构造函数的 prototype 对象
    3、将构造函数的作用域赋值给新的对象(也就是this 指向新对象)
    4、执行构造函数中的代码(为这个新对象添加属性)
    5、返回apply执行的结果或者返回对象

      //模拟 new 代码
      function create(fn, ...rest){
          const obj = Object.create(fn.prototype);
          const result = fn.apply(obj, rest);
          if(typeof result === "object"){
              return result;
          }else{
              return obj;
          }
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    再列举一些特殊情况:

    function fn()  
    {  
        this.user = 'xxx';  
        return {};  
    }
    var a = new fn();  
    console.log(a);//{}
    //new过程遇到return一个对象,此时this指向为返回的对象
    //实质还是他调用的对象
    console.log(a.user); //undefined
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    拓展:
    如果返回值是一个对象,那么this指向的就是那个返回的对象,如果返回值不是一个对象那么this还是指向函数的实例。

    function fn()  
    {  
        this.user = 'sweet';  
        return function(){};
    }
    var a = new fn;  
    console.log(a.user); //undefined
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    function fn()  
    {  
        this.user = 'sweet';  
        return 1;
    }
    var a = new fn;  
    console.log(a.user); //sweet
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    function fn()  
    {  
        this.user = 'sweet';  
        return undefined;
    }
    var a = new fn;  
    console.log(a.user); //sweet
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    function fn()  
    {  
        this.user = 'sweet';  
        return null;
    }
    var a = new fn;  
    console.log(a.user); //sweet
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    5、apply()、call()、bind()

    apply call bind 可以改变 this 指向,准确的说是改变非箭头函数的 this 指向。

      let obj = {
          name: "William",
          say: function(){
              console.log(this.name);
          }
      }
      obj.say();//William
      obj.say.apply({name: "Ben"})//Ben
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    6、箭头函数的 this

    箭头函数的 this 在定义时确定。
    箭头函数 this 指向:箭头函数没有自己的 this,看其外层的是否有函数,如果有,外层函数的 this 就是内部箭头函数的 this,如果没有,则 this 是 window。

    箭头函数的特性:

    • 箭头函数的 this 在定义时确定
    • 箭头函数没有prototype属性,是更纯粹的函数。
    • 箭头函数不能使用new 关键字。
    • 箭头函数没有 arguments,可以使用 …rest 解决参数不定长的问题
    • apply call bind 不能修改箭头函数的 this
      箭头函数
      通过 Babel 将 ES6 的代码进行转义,可以看到箭头函数的 this 在定义的时候会进行一个转换,所以说箭头函数的 this 在定义的时候确定
     window.color = "red";
      let color = "green";
      // var color = "black";
      let obj = {
        color: "blue"
      };
      let sayColor = () => {
        return this.color;
      };
      console.log(sayColor.apply(obj));//red
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    分析:

    1、sayColor 是一个箭头函数,applay 是不起作用的,所以函数里面的this 没有被改变。这里箭头函数里面的 this 按照定义的时候 this 的指向的。定义 sayColor 的时候 this 指向全局 window。
    2、this.color 指的是 window.color。又 let const 定义的变量并不会加载到 window 上面的。所以 let color并不会改变 window.color的值

    拓展

    const obj = {
      sayThis: () => {
        console.log(this);
      }
    };
    
    obj.sayThis(); // window    因为 JavaScript 没有块作用域,所以在定义 sayThis 的时候,里面的 this 就绑到 window 上去了
    const globalSay = obj.sayThis;
    globalSay(); // window 浏览器中的 global 对象
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    7、匿名函数的 this

    匿名函数的 this 永远指向 window ,没有变量指向匿名函数的执行环境具有全局性,因此其 this 对象通常指向 window。

     var name = "The window";
      var object = {
        name: 'My Object',
        //obj.getNameFunc 指向了后面的匿名函数
        getNameFunc: function () {
          console.log(this);//object
          return function () {
              return this.name;
          }
        },
        getName: function () {
        //此时,箭头函数的this指向就是当前所在环境了,不像上面没有指向的匿名函数会都指向window
          return ()=>{
            return this.name;
          }
        }
      }
      console.log(object.getNameFunc()());//The window
      console.log(object.getName()());//My Object
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
  • 相关阅读:
    Android 10.0后创建文件
    LeetCode每日一题——907. 子数组的最小值之和
    mysql创建触发器
    redis集群的安装部署
    Git使用教程
    【linux】Linux第一个小程序-进度条
    java.lang.ClassCastException: android.os.BinderProxy cannot be cast to ...
    含文档+PPT+源码等]精品基于PHP实现的社团活动小程序[包运行成功]计算机PHP毕业设计微信小程序项目源码
    策略梯度玩 cartpole 游戏,强化学习代替PID算法控制平衡杆
    【npm】常见错误
  • 原文地址:https://blog.csdn.net/m0_51055743/article/details/126390268