zoukankan      html  css  js  c++  java
  • javascript之深入剖析this

      this的重要性不言而喻,比如面试题经常考到,其次,如果彻底理解了this,那么对理解框架源码及编写高质量代码都有很大的帮助。本文就是要深入剖析this的几种情况,理解了原理,以后妈妈再也不用担心你的this了。。

      this是动态绑定的,其实相对应的是作用域,因为作用域是在代码刚刚写完的时候,就已经定义好了。理解了作用域,对理解闭包很有帮助。本文主要讲解this绑定,大家心里先有和作用域的一个大致对比就行,以后的文章会专门讲解。

      所谓动态绑定就是只有在函数被调用的时候,this才能确定它真正指向的是哪个对象。

      this分为以下四种情况,这四种掌握了,就打遍天下无敌手了~

      我们先定义一个函数: 

    function foo(name){
        this.name=name;
       console.log(this.name);
    }

     一、new绑定 

     var obj=new foo("yx");

      控制台输出结果:"yx"

      继续在控制台输入: obj.name  ,结果依然是"yx"

      对于上面的 foo 函数,

      如果改成

      function foo(name){
      var o={name:"peggy"};
      this.name=name;
      console.log(this.name);
      return o;
      }
      var obj=new foo();

      控制台输出结果依然是"yx"

      继续在控制台输入 obj.name ,发现结果竟然变成了"peggy"!

      其实我觉得原因大家已经猜出来了:

      如果 foo 函数返回了一个对象,那么 obj 等于返回的那个对象o,如果没返回对象的话,则默认是 return this ;

      可能有些小伙伴会认为 foo 应该大写,而且 foo 是构造函数,其实压根没有什么构造函数,函数名大写也只是一种约定而已,其实真实的情况是使用 new 关键字构造调用了 foo 函数!那么此时的 this 在 foo 函数被调用的过程中,绑定到了一个新创建的对象上,如果没有指定对象返回的话,那么返回的就是这个新创建的对象。这个新创建的对象的内部 [[proto]] 属性会指向 foo.prototype ,这就涉及到原型与继承问题了,以后再讨论。

      所以结果为什么这样就很清晰了:两次调用 console.log(this.name) 会返回 "yx" 是因为 this 就是新创建的这个对象。而最后 obj.name 不一样,是因为函数返回的对象不一样了,所以此时 obj 代表的对象也就不一样了。

      二、显式绑定

      所谓的显示绑定有三种: call , apply , bind 

      我们定义函数:

     function person(age, hobby){
      this.age=age;
      this.hobby=hobby;
      console.log(this.name);
      console.log(this.age);
      console.log(this.hobby);
     }

      因为函数也是对象,所以也是有方法的,每个函数其实可以理解为是由 Function  函数通过new构造调用的,即  new Function() 

      所以每一个函数内部 [[proto]] 属性都指向  Function.prototype ,所以可以继承 Function.prototype 中的方法,比如; call , apply , bind 

      call 方法和 apply 方法的共同点是:第一个参数代表 this 指向的对象

      不同点是: call 方法从第二个参数开始,传递的都是 person 函数需要的对应的参数,挨个挨个的传递

            apply 方法的第二个参数,传递的是一个数组,数组中的每一项与 person 函数需要的参数对应

      (1)call  
       var obj={name:"yx"};
      person.call(obj,24,"singing");

      此时 this 指向 obj ,所以控制台输出结果是"yx",24,"singing'

      (2)apply 
      var obj1={name:"peggy"};
      person.apply(obj1,[24,"running"]);

      此时, this 指向 obj1 ,控制台输出结果是"yx",24,"running"

      使用 apply 还可以用来展开数组,第二个参数传递 arguments 数组( arguments 数组代表函数实际传递的参数)

      比如:继承会用到

      var obj2={name:"xixi"};
      function student(age,hobby){
      person.apply(this,arguments);
      }

       student.call(obj2,24,"singing"); 

      此时 student 函数中的 this 指向传入的 obj2 , arguments 是一个类数组,代表传递的实参,

      所以如果想实现继承 person 函数的属性或者方法,可以用 apply 展开 arguments 

      (3) bind
       var obj3={name:"nancy"};
       var obj4={name:"mingming"};
       var newFn=person.bind(obj3);

      调用 bind 函数会返回一个新函数,新函数的 this 会绑定在 obj3 上,无论以后是怎么调用 newFn , this 都会一直绑定在 obj3 

      比如在控制台输入: newFn.call(obj4,24,"singing") 或者输入 newFn(24,"singing") 都不能改变this的绑定

      发现结果依然是输出:"nancy",而不是"mingming"

      bind 还可以用来进行科里化,就是可以先传参数进去,比如:

      var newFn1 = person.bind(obj3,24);
      newFn1("singing");
      newFn1("running");

      相当于先传了一个参数,之后调用的时候再传另外的参数就好。 

      不是所有的函数都有 protoype ,用 bind 创建的函数就没有 prototype 。比如: 

    function fn(){return this.x}; var boundFn=fn.bind({x:1})

      那么 boundFn 就是没有 prototype 属性的。

      三、隐式绑定 

      function foo(){
      console.log(this.name)
      }
      var obj={
        name:"yx",
        fn:foo
      }
      obj.fn();

      控制台结果:"yx"

      此时 this 指向 obj , obj 为此时的上下文环境

      四、默认绑定:

      var name="yx";
      function foo(){
        var name="peggy";
        console.log(this.name);
      }
      foo();

      控制台结果:"yx"

      此时: this 指向 global 对象

      (1)浏览器环境中: this=window 

      (2)node开发环境中: this=global 

      其实上面四种情况的顺序,我是刻意这样安排的,因为从上到下优先级依次降低。

      根据这几种不同的调用方式, this 指向的对象是不同的。

      依次判断 new绑定-> 显式绑定-> 隐式绑定->默认绑定

      大家可以亲自去验证下,是不是这个顺序。

      牛刀小试:

    (1)

    function a(xx){this.x=xx;return this};
    var x=a(5);var y=a(6);
    console.log(x.x);
    console.log(y.x);
    (2)
    var obj = {
        x: 1,
        y: 2,
        t: function() {
            console.log(this.x)
        }
    }
    obj.t();
    var dog={x:11};
    dog.t=obj.t;
    dog.t();
    show=function(){
        console.log('show'+this.x);    
    }
    dog.t=show;
    dog.t();
    (3)
    var number=2;
    var obj={
       number:4,
       fn1:(function(){
               var number;
               this.number*=2;//4
    
               number=number*2;//NaN
               number=3;
               return function(){
                     var num=this.number;
                     this.number*=2;//8 this=window
                     console.log(num);
                     number*=3;//9为了混淆,利用了闭包
                     alert(number);
               }//这是个闭包
        })(),
    
        db2:function(){
            this.number*=2;
        }
    }
    var fn1=obj.fn1;
    alert(number);
    fn1();//this=window
    obj.fn1();//this=obj
    alert(window.number);
    alert(obj.number);
  • 相关阅读:
    POJ2528——Mayor's posters (线段树区间更新查询+离散化)
    C++STL——unique函数总结
    HDU 5618 Jam's problem again(CDQ分治+树状数组(三维模板题))
    c++解决爆栈,手动加栈!
    POJ1741——Tree (树分治之点分治)
    树分治之点分治模板总结
    CodeForces
    字典树
    卡特兰数高精度算法
    基数排序
  • 原文地址:https://www.cnblogs.com/yxhao/p/6825863.html
Copyright © 2011-2022 走看看