关于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相关,有待补充,如有错误,欢迎指正!