JavaScript中的this详解
this一直都是难点所在,我查阅资料,对this进行了一些用法和误解的举例与分析,也帮助自己学习和理解。
目录
- 默认绑定
- 隐式绑定
- 显式绑定
- new绑定
- 一些特殊例子
默认绑定
var a = 2;
function foo(){
console.log(this.a);
}
foo();//2
这是一个this最简单的例子,看懂一段代码最好的方法就是先忽略那些声明的变量和函数,去看函数调用的地方。在这段代码里只有最后一行foo()是在调用,然后找到foo函数,里面执行console.log(this.a);因为foo在全局调用,所以它的this是window,再去window的作用域查找a为2。
现在来说说this的机制:this是在运行时进行绑定的,并不是在编写时绑定,与函数声明的位置没有关系。看函数被什么调用,也就是.函数名()前面的名字,如果没有则为默认绑定即全局对象。
隐式绑定
function foo(){
console.log(this.a);
}
var obj = {
a:2,
foo:foo
};
obj.foo();
按照默认绑定所说的方法分析,obj.foo(),那么foo()的this就是obj,所以输出obj.a为2。
总结来说,隐式绑定就是被调用的函数是否被某个对象拥有或者包含,若是有,则this就指向这个对象。
function foo(){
console.log(this.a);
}
var obj = {
a:2,
foo:foo
};
var bar = obj.foo;
var a = "window.a";
bar();
这里bar = obj.foo,看起来似乎这两个是一样的,仔细看看,obj.foo会找到obj里面的foo,然后执行foo(),此时的this是obj,所以会输出2;而当令bar = obj.foo时,obj.foo = foo,所以会先令bar = foo,此时再执行bar(),与执行foo()没有区别,因此会输出window.a。
function foo(){
console.log(this.a);
}
function doFoo(fn){
fn();
}
var obj = {
a: 2,
foo: foo
}
var a = "window.a";
doFoo(obj.foo);
将obj.foo作为参数传入doFoo(),传参的过程就是在doFoo中声明一个变量fn,然后令fn等于obj.foo,在这里就跟上一个例子的赋值操作相似了,因此相当于foo()直接在doFoo中执行,foo的调用位置没有显式调用对象,所以默认绑定为全局,this为window,所以输出window.a。
以上两个例子都说明了隐式丢失的问题,最明显的特征就是存在赋值操作,因此在赋值操作时,不要急于将this带入,而是应该将整个函数完整保留再赋值,最后再执行该函数。
显式绑定
function foo(){
console.log(this.a);
}
var obj = {
a:2
};
foo.call(obj);
显式绑定的第一种方式就是使用call()、apply()和bind()来显示指明this,它们的用法相似,第一个参数就是用来指定this。在这个例子中,call的第一个参数是obj,那么this就是obj,输出obj.a为2。
其实显式绑定比隐式绑定更为灵活,在隐式绑定中,必须将函数添加到对象中,再使用对象来调用才能绑定this的值,然而显式绑定写法更加简单,它通过使用JavaScript内置函数来改变this的指向。
new绑定
在JavaScript设计模式中提到过构造函数,包括内置函数在内的所有函数都可以用new来调用,这种函数调用被称为构造函数调用。
function foo(a){
this.a = a;
}
var bar = new foo(2);
console.log(bar.a);
首先new关键字会创建一个空的对象,然后会自动调用一个函数apply方法,将this指向这个空对象,这样的话函数内部的this就会被这个空的对象替代。在上面例子中,new foo()返回一个对象,此时将this绑定为bar,再通过foo()里面的this.a给bar.a赋值为2,所以输出2。
* 总结一下上述四条规则的优先级:new绑定 > 显式绑定 > 隐式绑定 > 默认绑定。
一些特殊例子
function foo(){
console.log(this.a);
}
var a = 2;
foo.call(null);
将null或者undefined作为this的绑定对象传入call、apply或者bind,这些值在调用的时候会被忽略,实际还是默认绑定,因此输出2。
function foo(){
console.log(this.a);
}
var a = 2;
var o = {a:3,foo:foo};
var p = {a:4};
o.foo();
(p.foo = o.foo)();
p.foo();
o.foo()就是上面提到的隐式绑定,输出o.a为3;(p.foo = o.foo)()是先将o.foo的foo()的引用返回给p.foo,这句表达式是一个立即执行函数,因此第一个括号里就是foo(),而不是p.foo(),这里就为默认绑定,输出2;p.foo()在上一行给p定义了foo函数,此时输出为4。
var o = {
a:10,
b:{
a:12,
fn:function(){
console.log(this.a);
}
}
}
o.b.fn();
前面说this绑定的是调用函数的对象,但是这里fn()前面有两个对象,他们是包含关系,但是this绑定有个规则,它绑定的是紧挨着函数的对象,所以输出12。
function fn(){
this.a = 2;
return {};
}
var bar = new fn;
console.log(bar.a);
在new绑定时说到,new函数时为构造这个函数的实例,所以bar.a应该输出2,但是实际上输出的undefined,正是因为有return语句,这里return了一个对象,所以this指向的就是那个返回的对象,而这个对象为空,所以bar.a自然为undefined。如果返回值不是一个对象那么this还是指向函数的实例。若是改为return 3,bar依然为fn()的实例,别忘了函数也是对象。