最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 别再用self=this、that=this了,代码太难读懂!

    正文概述 掘金(沧沧凉凉)   2021-03-02   854

    1. 前言

    JavaScript中的this可能是当年设计的时候存在着设计缺陷,在ES6中能使用()=>这个高端的箭头函数就尽量使用箭头函数,箭头函数在其他语言中还有个高大上的名字Lambda表达式。

    PS:nodejs环境中没有window对象

    2. this指向

    对于this的指向,我的理解就是记住一句话,如果没有使用apply和call还有箭头函数的情况下,this指向最后一次调用它的对象,如果最后一次调用它的是函数,则在严格模式指向undefined非严格模式下指向全局变量window

    下面的例子全部为非严格模式。

    var a = 1;
    var obj = {
      a: 2,
      b: function() {
        return this.a;
      }
    };
    console.log(obj.b()); //2
    

    上面的例子应该很清晰,因为最后一次调用b方法的是obj这个对象,而对象obj里面a的值为2,所以这个例子会输出2。

    那么我们加大一下难度看下面的例子。

    var a = 1;
    var obj = {
      a: 2,
      b: function() {
        return this.a;
      }
    };
    var t = obj.b;
    console.log(t());
    console.log(obj.b());
    console.log(t() === obj.b()); 
    console.log(t === obj.b); 
    

    这个时候我们新加入一个变量t,将b方法赋值给t,大家思考一下这个时候函数t会输出什么。

    神奇的事情发生了!

    console.log(t()); //1
    console.log(obj.b()); //2
    console.log(t() === obj.b()); //false
    console.log(t === obj.b); //true
    

    函数t输出的结果是1。

    因为这次调用方法的是函数t而他的对象为window,因为对象window上的a值为1,故t的最后输出结果为1。

    下面这个例子也同样。

    var a = 1;
    var obj = {
      a: 2,
      b: function() {
        function fun() {
          return this.a;
        }
        console.log(fun());
      }
    };
    obj.b(); //1
    

    因为调用fun方法的是方法b,这时最后一次调用fun的不为对象,则this指向全局变量window

    3. 箭头函数

    • 箭头函数会捕获其所在上下文的 this 值,作为自己的 this 值。
    • 箭头函数中没有 this 绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this,否则,this 为 undefined

    3.1 解决的痛点

    ES5语法中使用this有时候会很难以预测this的指向,从而产生bug,这个问题到了现在框架时代大量通过类进行封装时变得尤为明显。如果学习过react,就会发现将父组件的函数传递给子组件使用时,this指向总是个头疼的问题。

    虽然使用call,apply,bind可以解决这部分痛点,但是需要额外的代码,不仅增大了工作量,而且增加了维护成本。

    3.2 简单题目

    var a = 1;
    var obj = {
      a: 2,
      b: () =>{
        return this.a;
      }
    };
    console.log(obj.b()); //1
    

    因为b是通过箭头函数进行声明,则它的this就指向了obj的this,而obj的this就是全局变量window

    3.3 中等题目

    var a = 1;
    var obj = {
      a: 2,
      b: function () {
        var fun = () => {
          return this.a;
        };
        console.log(fun());
      },
    };
    obj.b(); //2
    

    因为箭头函数会找上下文的this,所以这里找到的上文就是方法b的this,即为对象obj

    var a = 1;
    var obj = {
      a: 2,
      b: () => {
        var fun = () => {
          return this.a;
        };
        console.log(fun());
      },
    };
    obj.b(); //1
    

    箭头函数是没有this的,这个时候方法fun就逐层寻找this,最终找到的是对象obj的this。

    4. call,apply,bind

    1. apply:调用一个对象的一个方法,用另一个对象替换当前对象。例如:B.apply(A, arguments);即A对象应用B对象的方法。

    2. call:调用一个对象的一个方法,用另一个对象替换当前对象。例如:B.call(A, args1,args2);即A对象调用B对象的方法。

    3. bind:除了返回是函数以外,它的参数和call一样。

    4.1 箭头函数

    this是不能改变的,所以对箭头函数使用call,apply,bind是没有效果的

    var a = 1;
    var obj = {
      a: 2,
      b: function () {
        var fun = () => {
          return this.a;
        };
        console.log(fun.call(a));
      },
    };
    obj.b(); //2
    

    4.2 特殊情况

    var a = 1;
    var obj = {
      a: 2,
      b: function () {
        var fun = () => {
          return this.a;
        };
        console.log(fun());
      },
    };
    var obj1 = {
      a: 3,
    };
    obj.b.call(obj1); //3
    

    箭头函数的this虽然不能被改变,但是可以改变它上下文的this。

    5. 一大堆题

    既然上面都讲了那么多,那么我们来看一下下面的题:

    var age = 10;
    var person = {
      age: 20,
      getAge() {
        var age = 30;
        return this.age; //20
      }
    };
    console.log(age, age * 2); //10 20
    console.log(person.getAge()); //20
    var b = person.getAge;
    console.log(b()); //10
    console.log(person.getAge()); //20
    console.log((1, person.getAge)()); //10
    console.log((1, person.getAge.bind(person))()); //20
    console.log((person.getAge, person.getAge)()); //10
    console.log((person.getAge = person.getAge)()); //10
    console.log(person.getAge.call()); //10
    console.log(person.getAge.call(person)); //20
    function getAge2() {
      this.age = 40;
      console.log(person.getAge());
    }
    getAge2(); //20
    console.log(age); //node环境和浏览器环境不一样,因为浏览器环境中有window对象
    function getAge3() {
      this.age = 50;
      this.getAge4 = () => {
        console.log(person.getAge.call(this)); //50
      };
    }
    console.log(new getAge3().getAge4());
    console.log(age); //10
    function getAge4() {
      this.age = 60;
      this.getAge5 = () => {
        console.log(person.getAge.call(this)); //60
      };
    }
    console.log(new getAge4().getAge5()); //undefined
    console.log(age); //10
    var age2 = 10;
    var person2 = {
      age2: 20,
      getAge2: () => {
        var age2 = 30;
        return this.age2;
      }
    };
    console.log(person2.getAge2.call()); //10
    console.log(person2.getAge2.call(person2)); //10
    

    6. 别再用self=this、that=this

    文章到这里就应该完了,但是!现在已经进入新时代了啊!可以使用箭头函数尽量的避免使用const self = this这种代码,之前我不太明白为什么React和Vue要进入函数式编程时代,直到我阅读到一个代码中有const self = this这种代码,光说无凭,我们直接来看一眼下面的代码:

    <template>
      <div>
        <button @click="click">点我</button>
        <div>{{ center }}</div>
      </div>
    </template>
    <script>
    export default {
      name: "test",
      data() {
        // 万恶之源 const self = this
        const self = this;
        return {
          self: 100,
          center: 50,
          test: {
            click: () => {
              console.log(self.self);
              // 这里的this指的什么?
              this.center = 10;
              console.log(this.center);
            },
          },
        };
      },
      methods: {
        click() {
          this.test.click();
        },
      },
    };
    </script>
    
    <style scoped></style>
    

    可以看到这段代码,又是self又是this的,这仅仅是我从项目代码中截取后简化的一段,原代码更加复杂,下面我们来看一眼原项目中test那个位置是怎么写的:

    amapEvents: {
      // 这里用了箭头函数
      click: (e) => {
        let { lng, lat } = e.lnglat;
        // 这里的self其实就是this
        console.log(self.plugin);
        self.plugin[1];
        let address;
        // 这里没有用箭头函数
        self.plugin[1].getAddress([lng, lat], function (status, result) {
          if (status === "complete" && result.info === "OK") {
            if (result && result.regeocode) {
              // 这里的self就不是this了,因为上面没有使用箭头函数
              self.address = result.regeocode.formattedAddress;
              address = self.address;
              console.log("地址:" + self.address);
              self.$nextTick();
            }
          }
        });
        this.center = [lng, lat];
        this.address = address;
      },
    },
    

    眼花嘛?头晕嘛?这项目上又是this又是self的,又是用了箭头函数又是不用箭头函数的,错综复杂的调用关系,最关键的是它还将函数放在data里面!太不守代码基本法了,所以在项目中最好都使用箭头函数,因为箭头函数的this会逐层查找。反正我用了箭头函数后,再也不用纠结this的问题了!


    起源地下载网 » 别再用self=this、that=this了,代码太难读懂!

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元