this到底是什么
作为类比,在C plus plus中,this的语意是代表本对象,在编写类代码时就已经绑定,必定是该类的一个实例,很明确。在js中,this也是代表着本对象。但是它与C++中不同的是,其是一个动态的概念,至于这个对象是“谁”,是在调用时决定的。
this是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决与函数调用时的各种条件。this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。
js中this实现的原理
当一个函数被调用时,会创建一个活动记录。这个记录会包含函数在哪里被调用(调用栈),函数调用方式,传入的参数信息。this就是这个记录的一个属性,会在函数执行的过程中用到。
判断this绑定的四条规则
默认绑定
默认绑定是this绑定到了全局对象,在独立函数调用时发生
function foo(){
console.log(this.a);
}
var a = 2;
foo();//输出2,为全局作用域中的变量a。this此时绑定的全局作用域
隐式绑定
先看如下两段代码
function foo(){
consloe.log(this.a);
}
var obj = {
a:2,
fun:foo
};
obj.fun(); //输出2
fucntion foo(){
console.log(this.a);
}
var obj = {
a:2,
foo:foo
};
var bar = obj.foo;
var a = "oops global";
bar();//输出 oops global
上述的代码中,obj中的fun成员函数和变量bar都是对全局函数的foo的间接引用。两者调用的方式不同,分别是obj.fun()和bar()。但是输出的结果是不同的。对obj.fun()的this是绑定到了对象obj,而bar()this是绑定到全局对象。对obj.fun()来说,虽然obj对象只是间接引用函数foo,但是可以认为是函数被调用时obj对象拥有或包含了foo,所以this绑定到了对象obj。而对bar()只是函数间接引用的调用,跟直接调用foo本质是一样的,所以this绑定到是全局对象。
如下代码
function foo(){
cosole.log(this.a);
}
function doFoo(fn){
fn();
}
var obj = {
a:2,
foo:foo
};
var a = "oops,global";
doFoo(obj.foo);//输出 oops,global
在函数doFoo中对fn()的调用同foo()调用时一样的,fn只是函数foo的间接引用,this绑定到时全局对象。
显示绑定
使用Function的call,apply,bind方法可以将this绑定到指定对象。此后,this绑定不会再修改。
function foo(){
consloe.log(this.a);
}
var obj = {
a:2
};
var bar = function(){
foo.call(obj); //将foo的this显示绑定到了obj对象
}
bar(); //输出2
被忽略的this
在显示绑定中,如果call,apply,bind函数传入的对象为null或undefined,那么这些值在调用时会被忽略,this直接绑定到全局对象。
function foo(){
console.log(this.a);
}
var a = 2;
foo.call(null); //输出为2
这种方式有一个很常见的应用,可以用apply来展开一个数组,如下代码
function foo(a,b){
console.log("a:"+a+",b:"+b);
}
foo.apply(null,[2,3]);
new绑定
new 来调用函数时会创建一个全新的对象,这个新对象会绑定到函数调用的this。
function foo(something){
this.a = something;
}
var obj1 = {};
var bar = foo.bind(obj1);
//bar的this被显示绑定为obj1
bar(2);
console.log(obj1.a); //输出2
//通过new调用bar,将产生一个新的对象,this被绑定为新对象
var baz = new bar(3);
console.log(obj1.a); //输出2
console.log(baz.a); //输出3
bar被显示绑定到obj1上,但是new bar(3)并没有把obj1.a修改为3。相反,new修改了显示绑定调用bar()中的this。因为使用了new绑定,我们得到了一个名字为baz的新对象,并且baz.a的值是3。
判断this
- 函数是否在new中调用(new 绑定)?如果是的话this绑定的新创建的对象。
var bar = new foo();
- 函数是否通过call,apply(显示绑定)调用?如果是的话,this绑定的是指定的对象。
var bar = foo.call(obj2);
- 函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this绑定的是那个上下文对象。
var bar = obj1.foo();
- 如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到undefined,否则绑定到全局对象。
var bar = foo();
以上。