this是什么
this是运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用的各种条件。this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。当一个函数被调用时,会创建一个活动记录(也称执行上下文)。
this既不指向函数本身,也不指向函数的词法作用域。
this的绑定规则
默认绑定
function foo(){
console.log(this.a);
}
var a=2;
foo();//2
本例中函数调用时应用了this的默认绑定,因为foo()是直接使用不带任何修饰的函数引用进行调用的,因此this指向全局对象。
*:如果使用严格模式,则不能将全局对象用于默认绑定,因此this会绑定到undefined。
**:对于默认绑定来说,决定this绑定对象的并不是调用位置是否处于严格模式,而是函数体是否处于严格模式。
隐式绑定
function foo(){
console.log(this.a);
}
var obj={
a:2,
foo:foo
};
obj.foo();//2
当foo()被调用时,它的前面确确实实加上了对obj的引用。当函数引用有上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象。因此调用foo()时this被绑定到obj,因此this.a和obj.a是一样的。
对象属性引用链中只有上一层或者说最后一层在调用位置中起作用。
隐式丢失
function foo(){
console.log(this.a);
}
var obj={
a:2,
foo:foo
};
var bar=obj.foo;
var a=3;
bar();//3
虽然bar是obj.foo的一个引用,实际上,它引用的是foo函数本身,因此此时的bar()其实是一个不带任何修饰的函数调用,因此应用了默认绑定。
同样,传人回调函数时也会发生隐式丢失,不管传入的是自己声明的函数还是传入语言内置的函数。
显式绑定
function foo(){
consol.log(this.a);
}
var obj={
a:2
};
foo.call(obj);//2
通过foo.call(),我们在调用foo()时强制把它的this绑定到obj上。
*:从this绑定的角度来看,call()和apply()是一样的。
硬绑定
function foo(){
console.log(this.a);
}
var obj={
a:2
};
var bar=function(){
foo.call(obj);
};
bar();//2
setTimeout(bar,100);//2
bar.call(window);//2
本例中我们创建了bar(),并在内部手动调用了foo.call(obj),强制把foo的this绑定到了obj。无论之后怎样调用bar,它都会手动在obj上调用foo。这是一种显示的强制绑定,称为硬绑定。
由于硬绑定是一种非常常用的模式,ES5内置了bind()方法。其会返回一个新函数,把你指定的参数设置为this的上下文并调用原始函数。
API调用的上下文
function foo(el){
console.log(el,this.id);
}
var obj={
id="awesome"
};
//调用 foo()的时候把this绑定到obj
[1,2,3].forEach(foo,obj);
//1 awesome 2 awesome 3 awesome
new绑定
关于new,有一个重要的点:实际上并不存在所谓的“构造函数”,只有对于函数的“构造调用”。
使用new来调用函数,或者说发生构造函数调用时,会执行下列操作:
(1):创建一个全新的对象
(2):这个新对象会被执行[[Prototype]]连接
(3):这个新对象会绑定到函数调用的this
(4):如果函数没有返回其他对象,那么new表达式中的函数会自动返回这个新对象
function foo(a){
this.a=a;
}
var bar=new foo(2);
console.log(bar.a);//2
使用new来调用foo()时,我们会构造一个新对象并把它绑定到foo()调用的this上。
优先级
判断this:
-
函数是否在new中调用(new绑定)?如果是的话,this绑定的是新创建的对象
var bar=new foo();
-
函数是否通过call、apply(显式绑定)或者硬绑定调用?如果是的话,this绑定的是指定的对象
var bar=foo.call(obj2);
-
函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this绑定到的是那个上下文对象
var var=obj1.foo();
-
如果都不是的话,使用默认绑定。如果在于按个模式下,就绑定到undefined,否则绑定到全局对象
var bar =foo();
绑定例外
被忽略的this
如果你把null或者undefined作为this的绑定对象传入call、apply或者bind,这些值在调用时会被忽略,实际应用的是默认绑定规则。
function foo(a,b){
console.log("a:"+a+",","b:"+b);
}
//把数组展开成参数
foo.apply(null,[2,3]);//a:2,b:3
//使用bind()进行柯里化
var bar=foo.bind(null,2);
bar(3);//a:2,b:3
更安全的this
一种更安全的做法是把this绑定到一个特殊的对象,且不会对程序造成任何副作用。可以使用一个DMZ对象,比如'xx'=Object.create(null)
以保护全局变量。
间接引用
间接引用最容易在赋值时发生:
function foo(){
console.log(this.a);
}
var a=2;
var o={
a:3,
foo:foo
};
var p={
a:4
};
o.foo();//3
(p.foo=o.foo)();//2————p.foo=o.foo的返回值是目标函数的引用,因此调用位置是foo()而不是p.foo()或o.foo(),这里应用的是默认绑定
软绑定
function foo(){
console.log("name"+this.name);
}
var obj={ name:"obj"};
var obj1={ name:"obj1"};
var obj2={ name:"obj2"};
var fooOBJ=foo.softBind(obj);
fooOBJ();//obj
obj2.foo=foo.softBind(obj);
obj2.foo();//obj2
fooOBJ.call(obj3);//obj3
setTimeout(obj.foo,10);//obj
this词法
function fo(){
return (a)=>{
console.log(this.a);
};
}
var obj1={a:1};
var obj2={a:2};
var bar=foo.call(obj1);
bar.call(obj2);//1
foo()内部创建的箭头函数会捕获到调用foo()的this,由于foo()的this绑定到obj1,bar的this也会绑定到obj1,箭头函数的绑定无法被修改!
具体来说,箭头函数会继承外层函数调用的this绑定。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。