Fork me on GitHub

关于JavaScript中的this

1.关于this的误会。

  误会(1):this指向所在函数本身。

    我们常常会对this产生各种误会,比如说我们可能认为this会指向所在函数本身,但实际上并非如此。

  

1 function foo (){
2     this.count ++;
3 }
4 foo.count = 0;
5 for(var i=0;i<5;i++){
6     foo();
7 }
8 console.log(foo.count);

    上面的例子旨在使用函数的this指向this,通过foo函数的count属性计算foo被调用的次数。根据这个目的,输出应该是5,因为for循环了五次,foo函数被调用了5次。但实际上呢?

    我们新建一个js文件,命名为test.js,代码如下:

    实际输出如下:

    输出的foo.count值为0。看上去foo中的this并没有指向foo,那么this指向了哪里呢?

  

 

    通过chrome JavaScript调试器可以发现,this指向了window全局作用域,并没有如我们预期的指向foo本身。

  误会(二):this指向函数所在作用域

    通过上面的例子,可能你很快就能得出结论:this指向所在函数所在的作用域。比如上一个例子中,函数foo位于全局作用域(在宿主环境中就是window),foo中的this便指向了全局作用域,但是果真如此吗?

    

 1 function foo (){
 2     var count = 0;
 3     function bar (){
 4            this.count ++;
 5     }
 6     for(var i=0;i<5;i++){
 7            bar();          
 8     }
 9     return count;
10 }

    上面的代码,旨在foo内部,声明一个bar函数用于控制foo作用域内的count变量,实现计算bar被调用次数的功能。理论上,bar函数中的this应该会指向bar所在的作用域,所以this也可以调用count(和bar函数同作用域),应该输出为5。但实际输出呢?

    实际输出仍然为0!说明this根本没有指向bar所在的作用域,那么this指向哪了呢?

    实际上,this还是指向了window作用域,真是非常神奇。

    要想了解this的工作方式,我们先了解一下this的绑定方式。

2.this的绑定规则

   (1)默认绑定

      当函数独立调用时,这条规则就是无法应用其他规则时的默认规则。

       直接使用不带任何修饰的函数,this会指向全局环境。

        

1 function foo (){
2      this.count = 100;
3 }
4 foo();         //直接使用,函数未经修饰
5 console.log(window.count);       //输出全局环境中的count变量,就是函数中的this.count,结果为100

        但是this不总是指向全局环境,当使用严格模式时,this会被禁止指向全局环境。

1 function foo (){
2      "use strict";    //使用严格模式
3      this.count = 100;
4 } 
5 foo();
6 console.log(window.count);  //输出:Uncaught TypeError: Cannot set property 'count' of undefined

    (2)隐式绑定:

       

1 function foo (){
2      console.log(this.count);
3 }
4 var obj = {
5      count:100,
6      foo:foo
7 };
8 obj.foo();    //输出100

        当所调用的函数是某个对象的成员函数时,函数中的this会指向函数所在的对象。

        隐式绑定可能会存在绑定丢失的场景,当函数作为参数传入到另外一个函数时,作为参数的函数所绑定的this会失效。

 1 function foo(){
 2      console.log(this.count);
 3 }
 4 function bar (func){
 5      func();
 6 }
 7 var count = 0;
 8 var obj = {
 9      count : 100,
10      foo : foo
11 };
12 bar(obj.foo);      //会输出0

        如上例,当obj.foo作为参数传入到函数bar时,原来绑定的this指向obj被修改为指向全局变量window。所以使用回调函数,可能会修改传入参数函数的this指向。

    (3)显式绑定

       使用call()或者apply()函数可以显式强制的绑定函数的this。

        

function foo (){
     console.log(this.count);
}
var count = 'window!';
var obj1 = {
     count : 100,
     foo : foo
};
var obj2 = {
     count : 200,
     foo : foo
};
foo.call(obj1);   //输出100
foo.call(obj2);   //输出200

        如上例所示,call两次修改了foo的this指向。使其指向某个固定的对象。当call的传入的第一个参数为null时,所修改函数的this指向不会被显式修改。

     (4)new 绑定

         使用new操作符,可以将函数的this指向新创建的对象。

       

function Foo(name, age){
     this.name = name;
     this.age = age; 
} 
var obj = new Foo('Tom',99);
console.log(obj);

        输出如下:

 

          新构建的obj对象具有了一系列属性,都是用函数Foo中的this所实现。

        通过这个原理,我们可以简单的实现一个_new_函数,以实现new操作符的功能。

         

function _new_ (func , arr){
    var obj = {};
    func.apply(obj,arr);
    return obj;
}
function Foo (name, age){
    this.name = name;
    this.age = age;
}
var obj = _new_(Foo,['Jerry',99]);
console.log(obj);

         输出的obj如下:

        说明基本的传参构造对象功能已经实现了。

 

3.四种绑定规则的优先级

    new绑定 > 显示绑定 >隐式绑定 > 默认绑定

    判断this绑定的顺序:

      1.判断是否是new调用,是的话就是new绑定,函数中的this会指向新构造的对象。

          2.判断是否是显示绑定,即是否是通过函数原型中的apply方法或者call方法调用 (还要注意bind返回的函数,this指向也会被修改)。

          3.判断是否是隐式绑定,及通过某个对象调用。是的话,this会指向所属对象。

          4.如果都没有的话,就是默认绑定,this指向全局对象window;严格模式下指向undefined。

 

 

……更过this相关,有待补充,如有错误,欢迎指正!

 

 

  

 

posted @ 2017-09-13 16:49  钟衷7  阅读(633)  评论(0编辑  收藏  举报