this

作者: ZombieBrandg | 来源:发表于2018-06-04 23:07 被阅读0次

    this的重要性

    this是JavaScript中最为关键的语法点,一般在简单项目里,this可有可无,但大部分的开发任务中,this起着关键作用。

    this的由来

    以下面JS代码为例,调用该自身对象的其它属性的值。

    var obj = { 
    name: 'cheche',
    say: function(){
          console.log('你好,'+obj.name)
           }
     }
    obj.say()   // 'cheche'
    

    假如我们要在控制台打印出你好,cheche,通过上面的JS代码虽然用变量获取可以实现,但通过写obj.name,这样严重依赖了变量名,如果变量名obj变了,那么里面用到变量名的地方都得改变,上面代码的写法显然不可取。
    为了不依赖变量名,可以给obj.say()传入一个参数,

    var obj = {
    name: 'cheche',
    say: function(person,问候语){
           console.log(问候语+','+person.name)
         }
    }
    obj.say(obj,'早')   // '早,cheche'
    

    这样的写法算是比较友好的,没有依赖变量名。但是得传入两个参数,那能不能只传一个参数?
    如果只传一个参数便是这种形式obj.say('早'),但obj.say(obj,'早')obj.say('早')这二者很难区分,所以JS之父布兰登·艾克把第一个参数隐藏起来,但隐藏起来之后,怎么知道person.name是什么,于是就引入了this作为它的第一个参数。那么上面的代码就变成了这样,

    var obj = {
    name: 'cheche',
    say: function(问候语){
         console.log(问候语+','+this.name)
         }
    }
    obj.say('早')      // '早,cheche'
    

    其中obj,say('早')形式上等价于obj.say.call(obj,'早'),前者是一种语法糖的形式,后面这种才是计算机真正认为的,浏览器认为obj === this,所以我们可以认为this就是函数call的第一个参数。

    this的概念

    this的数据类型

    var obj = {
      foo: function(){
        console.log(this)
      }
    }
    var bar = obj.foo
    obj.foo()   //obj
    bar()      //undefined => Window
    

    上面代码里,调用obj.foo()打印出来的是obj这个对象自身,因为obj.foo()形式上等于obj.foo.call(obj),而call的第一个参数就是this,所以我们知道了this就是一个对象,即使你指定的this是数字1,它也会变为一种对象的形式。

    img
    在调用bar()时,并没有指定this,形式上相当于bar.call(undefined),这时浏览器会默认this等于window对象。但在nodejs里,this === global,global是个全局对象。
    不要以为bar = obj.foo,就认为bar() === obj.foo(),函数是独立存在的,不会进行补全。

    this这个参数

    当函数未调用时,this只是一个参数,无法确认this是什么。

    var fn = function(p1){
    console.log(this)
    console.log(p1)
    }
    

    这是你无法确认this和p1是什么,因为他们只是参数。当你调用之后,便可以指定参数了。

    this的确定

    既然我们知道,函数传的第一参数就是this,那我们该如何确定它?

    1. 通过console.log(this)
      这是最简单的方法,但是缺乏说服力。
    2. 读DOM或jQuery的源代码
      水平不够,读不懂
    3. 查对应API的文档
      html:<button id="xxx">点我</button>
      js:xxx.addEventListener('click',function(){console.log(this)}) //点击button
      

    通过查EventTarger.addEventListener文档,可以知道this值是触发事件元素的引用。所以,this === xxx,即等于button元素。

    改变this的值

    var obj = {
     fn : function(){
    console.log(this)}
    }
    setTimeout(obj.fn,1000)  //window对象
    

    当向setTimeout()传递一个函数时,该函数的this会指向一个错误的值,在非严格模式下,this是window对象,而在严格模式下,this是undefined。那我们该如何让this为obj这个对象呢?有三种方法。

    1. 用包装函数
      setTimeout(function(){obj.fn()},1000),这样强制指定this为obj

    2. bind

      setTimeout(obj.fn.bind(obj),1000)
      

      obj.fn.bind(obj)会返回一个新的函数,在形式上等于

      function(){obj.fn()}
      

      可以理解为在

      obj.fn
      

      外套一层,重新写个call覆盖原来的this。

    3. this的高级用法

    var a = [1,2,3]
    var b = a.join()
    var c = [4,5,6]
    var d = a.join.call(c)
    console.log(b)  // '1,2,3'
    console.log(d)  // '4,5,6'
    

    array.join()是个数组的API,是将数组所有元素连接到字符串中,默认以逗号隔开。那么a.join(),里面的join是怎么获取到数组a的,它的原理是什么?我们可以做出以下猜想。

    var a = [1,2,3]
    var a.join = function(){
    var result = ''
    for(var i=0;i<this.length;i++){
      if(i !== this.lenght -1){  //最后一个不用加逗号
       result += this[i] + ','
      }else{
        result +=this[i] }
    }                            
    

    我们做出猜想,join函数里有用了this,而这个this等于数组a。如果把this的传入改为数组c,那么join函数会把数组c变成字符串d。也就是说数组c调用了数组a的方法。
    如果c不是一个数组,而是一个伪数组,同样可以用a的方法。a的slice方法操作了this,把c当作this给a的slice操作就行了。slice()是一个截取数组的方法

    var a = [1,2,3]
    var c = {0:4,1:5,2:6,length:3}
    var e = a.slice.call(c,0,2)
    console.log(e) // [4,5]
    
    1. 打出想要的this
    html:
    <button id=xxx name="curry"
    javascript:
    var obj = {
    name: 'allen',
    say: function(){
        xxx.onclick = function(){console.log(this.name)}
        }
    }
    obj.say()  //xxx.name
    

    此时this是xxx这个按钮,我们想打出’cheche’,怎么办?

    • 古老方法
      将函数外的this赋值给一个变量,然后把函数里的this改成那个变量就行了。

      var obj = {
      name: 'cheche',
      var _this = this
      say: function(){
         xxx.onclick = function(){console.log(_this.name)}
        }
      }
      obj.say()  // 'cheche'
      
    • bind

      var obj = {
      name: 'cheche',
      say: function(){
         xxx.onclick = function(){console.log(_this.name)}
        }.bind(this)
      }
      obj.say()  // 'cheche'
      

    此时,bind传入的this是函数外的this,可以理解为bind()是最里面的this传入,当然以它传入为准。

    • 箭头函数
      var obj = {name: 'cheche',say: function(){xxx.onclick = ( ) =>  {console.log(this.name)}}obj.say()  // 'cheche'
      

    箭头函数没有隐藏的this,它自身并不会传入this,所以箭头函数里的this不是被点击的元素,而是它外面的this。

    箭头函数无法用call()指定this

    this的特殊用法

    在new关键字调用构造函数时,构造函数里的this表示的是一个实例对象。

    下边this因为

    function Arr(id){
     this.id = id
    }
    var s = new Arr(1)
    

    此时这个this就是对象`s’。

    下边代码this指向的是被点击button元素

    var button = document.querySelector('button');
    var name = 'jack';
    var object = {
        name: 'lucy',
        sayHi: function() {
            var that = this;
            button.onclick = function() {
                console.log(that.name);
            }
        },
    
    }
    object.sayHi();   //btn
    

    setTimeout和setInterval this处理

    setTimeout中函数内的this是指向了window对象,这是由于setTimeout()调用的代码运行在与所在函数完全分离的执行环境上。这会导致这些代码中包含的 this 关键字会指向 window (或全局)对象.

    方法一:保存this变量

    var num = 0;
    function Obj (){
        var that = this;    //将this存为一个变量,此时的this指向obj
        this.num = 1,
        this.getNum = function(){
            console.log(this.num);
        },
        this.getNumLater = function(){
            setTimeout(function(){
                console.log(that.num);    //利用闭包访问that,that是一个指向obj的指针
            }, 1000)
        }
    }
    var obj = new Obj; 
    obj.getNum();//1  打印的是obj.num,值为1
    obj.getNumLater()//1  打印的是obj.num,值为1
    

    方法二bind

    var num = 0;
    function Obj (){
        this.num = 1,
        this.getNum = function(){
            console.log(this.num);
        },
        this.getNumLater = function(){
            setTimeout(function(){
                console.log(this.num);
            }.bind(this), 1000)    //利用bind()将this绑定到这个函数上
        }
    }
    var obj = new Obj; 
    obj.getNum();//1  打印的为obj.num,值为1
    obj.getNumLater()//1  打印的为obj.num,值为1
    

    方法三箭头函数

    function Obj (){
        this.num = 1,
        this.getNum = function(){
            console.log(this.num);
        },
        this.getNumLater = function(){
            setTimeout(() => {
                console.log(this.num);
            }, 1000)    //箭头函数中的this总是指向外层调用者,也就是Obj
        }
    }
    var obj = new Obj; 
    obj.getNum();//1  打印的是obj.num,值为1
    obj.getNumLater()//1  打印的是obj.num,值为1
    

    相关文章

      网友评论

          本文标题:this

          本文链接:https://www.haomeiwen.com/subject/xjeusftx.html