“this” 在 JavaScript 中的指向是一大难点,因为它总是变幻莫测。虽然移花接木、张冠李戴在它身上表现得淋漓尽致,但幸而它还是有迹可循的。本文就来扒一扒 “this” 那些事儿。
“this” 指向谁,只有在函数调用时才能确定。调用函数的不同方式,决定着判断 this 指向的不同方法。所以本文从以下几个方向来对其进行探讨:
- 函数调用函数
- 对象调用函数
- call()、apply()、bind() 与 “this”
一、函数调用函数
直接上例子是最直观的方式,所以我们先从这几个例子中来感受一下函数中的 “this”。
function fun(){ // demo 1 alert(this); } fun(); //[object window]
function fn() { // demo 2 function foo() { alert(this); } foo(); } fn(); // [object Window]
function foo() { // demo 3 function fun(){ function bar(){ function fn(){ alert(this); } fn(); } bar(); } fun(); }
foo(); // [object Window]
var a = 0; // demo 4 function foo() { var a = 1; alert(this.a); } function fn() { var a = 2; foo() }
foo(); // ?
fn(); // ?
"use strict" // demo 5 function fun(){ alert(this); } fun(); // undefined
从 demo 1/2/3 可看出,函数内部包裹的 “this” 最终都指向了全局对象。那 demo 4 呢?实际上答案都是 “0”,有看出什么猫腻么 ^_-
看没看懂没关系,接着往下看,瞧瞧在对象中调用函数时 “this” 又是怎样的。
二、对象调用函数
对象调用函数我就想出下面一个 demo ,但绝对有趣:
var a = 0; // demo 6 function fun(){ return this.a; } var obj1 = { a : 1,
b : this.a, fn : function(){ return this.a; }, fun : fun } var obj2 = { a : 2, bar : fun, obj3 : obj1 } alert(fun());
alert(obj1.b); alert(obj1.fn()); alert(obj2.bar());
alert(obj2.obj3.b); alert(obj2.obj3.fn()); alert(obj2.obj3.fun()); //答案:0 、 0 、 1 、 2 、 0 、 1 、 1
通过对比 “函数调用函数” 和 “对象调用函数”,可以说对象就像一个阀门,“this” 在这些调用者中从底层往上穿梭,遇到阀门就被阻挡下来,没有阀门就扶摇直上。至于 “this” 指向的是谁,就看它最开始遇到的阀门是哪一个。
如果一路是通行无阻的,那最后就看文档是不是严格模式了,如果不是 “use strict”,那就是 demo 1、2、3、4的结果------指向全局变量;如果是严格模式,那就是 demo 5 的结果------undefined。
下面我们重点分析一下 demo 6 :
alert(fun())------ 结果为0。这是通行无阻的情况,fun() 在全局环境中直接被调用,非严格模式下直接指向 window对象;
alert(obj1.b)------ 结果为0。这是因为 obj1.b = this.a ;这是在全局作用域中直接给 obj1.b 赋值,所以 “this” 就是指向全局对象了;
alert(obj1.fn())------ 结果为1。这里 fn() 被 obj1 这个对象调用,相当于遇到了 obj1 这个阀门,就被挡到 obj1 上面。所以结果是 obj1.a;
alert(obj2.bar())------ 结果为2。和 obj1.fn() 一样的调用过程;
alert(obj2.obj3.b)------ 结果为0。同 obj1.b,都是在全局环境中给 obj2.obj3.b 赋值为 this.a;
alert(obj2.obj3.fn())------结果为1。fn()中的 “this” 向上,最先碰到 obj3 这个对象,准确的说应该是 obj2.obj3 这个对象,于是 “this”指向 obj2.obj3,故 this.a = obj2.obj3.a 即 1;
alert(obj2.obj3.fun())------ 同obj2.obj3.fn()。
三、call()、apply()、bind() 与 “this”
call()、apply()、bind() 这三个方法可以显式的设置 “this” 指向,所以就比上面的那些推理更清晰明了。
1、call()、apply()
call()、apply() 作用相同:
function fun(){ // demo 7 return this.num; } var obj = { num : 1 }; alert(fun.call(obj)); //1
我们从 demo 7 来看call()、apply() 的作用:call()、apply()就是把 fun 这个函数中的 “this” 显式的设置为 obj,所以通过 fun 函数中的 this.num 就访问到了 obj 中的 num 属性。
至于call() 和 apply() 的区别,就是老生常谈了,也就是传入参数的方式的不同。
2、bind()
bind()官网解释:
bind方法会创建一个新函数,称为绑定函数.当调用这个绑定函数时,绑定函数会以创建它时传入bind方法的第一个参数作为this,传入bind方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。例:
var foo = { a : 1 } function fun(){ return this.a; } var boundFun = fun.bind(foo); alert(boundFun());
bind() 和 call()、apply() 是类似的,其区别是 call()、apply() 修改函数的作用域,而 bind() 是创建一个绑定函数。
(以上见解如有错误请指正)