this是在运行时绑定的。函数调用方式的不同就可能使this所绑定的对象不同
一.几种绑定规则
Ⅰ.默认绑定
- 一般情况下,若无其他规则出现则默认将this绑定到全局对象上
function foo(){
var a = 3;
console.log(a);
}
var a = 2;
foo(); //2
//foo()在调用时使用不带任何修饰的函数引用,只能使用默认绑定
this常有的集中错误理解之一就是认为this指向当前函数的词法作用域
this与词法作用域及作用域对象是完全不同的东西
Ⅱ.隐式绑定
- 若在调用位置有上下文对象(可理解为此函数调用时使用一个对象.出来的),就遵循隐式绑定
function foo(){
console.log(a);
}
var obj = {a:2, foo:foo};
var a = "opps,global"; //全局对象的属性
obj.foo(); //2 隐式绑定
var bar = obj.foo; //这里进行了一次引用赋值
bar(); //opps,global
如果细心的话你会发现,上边的代码出现了一个问题:隐式丢失
- 当进行隐式绑定时,如果进行一次引用赋值或者传参操作,会造成this的丢失,使this绑定到全局对象中去。
由于第7行处进行了以此引用赋值,所以造成了bar()不具有上下文对象,故系统会自动采取默认绑定规则
我们再来举一个传参操作造成隐式丢失的例子
function foo(){
console.log(this.a);
}
function doFoo(fn){
fn(); //在此调用时参数传递为隐式赋值,丢失this绑定
}
var obj = {a:2, foo:foo};
var a = "opps,global";
doFoo(obj.foo); //看似为隐式绑定,但输出的是opps,global
所以我们需要记住:函数传递就是一种隐式赋值,而隐式赋值会造成隐式丢失
同样,js中的一些内置函数在接受一个函数作为参数时恩惠发生以上情况
function setTimeout(fn, delay){
//等待delay毫秒数
fn();
}
Ⅲ.显式绑定
使用apply和call为要执行的函数直接绑定this称为显式绑定
function foo(){
console.log(this.a);
}
var obj = {a:2};
foo.call(obj); //2
- PS:
- 若给call/apply传入的是一个基本类型数据,则这个基本类型数据将被转换成对应的基本包装类型
- 此种方法仍然无法解决绑定丢失问题
1.硬绑定
function foo(){
console.log(this.a);
}
var obj = {a:2};
var bar = function(){ //创建一个包裹函数以确保obj的绑定
foo.call(obj);
};
bar(); //2
setTimeout(bar, 1000); //时隔一秒打印2
bar.call(window); //2
通过此方法(包裹函数)可以确保obj的绑定,不过韩式不能为函数传参
function foo(something){
console.log(this.a, something);
return this.a + something;
}
function bind(fn, obj){ //辅助绑定函数
return function(){
return fn.apply(obj, arguments);
};
}
var obj = {a:2};
var bar = bind(foo, obj);
var b = bar(3);
console.log(b);
//2 3
//5
- ES5中内置了bind方法(其实就是个语法糖)。其返回一个硬编码的新函数,将你制定的对象绑定到调用它的函数的this上
function foo(something){
console.log(this.a, something);
return this.a + something;
}
function bind(fn, obj){ //辅助绑定函数
return function(){
return fn.apply(obj, arguments);
};
}
var obj = {a:2};
var bar = foo.bind(obj); //bind返回一个绑定到obj上的新函数
var b = bar(3);
console.log(b);
var a = "window's a";
foo("!"); //对原来的函数不产生影响
//2 3
//15 5
//window's a !
//"window's a!"
6.API调用参数指定this
function foo(e1){
console.log(e1, this.id);
}
var obj = {id:"Sir"};
[1,2,3].forEach(foo, obj); //forEach第二个参数用来设置this
//1 "Sir"
//2 "Sir"
//3 "Sir"
Ⅳ.new绑定
function foo(){
this.a = 1;
this.b = 2;
}
var instance = new foo();
console.log(instance.a);
发生函数的构造调用时自动执行以下操作
- 创建一个全新的对象
- 此对象会被执行[[Prototype]]链接
- 此新对象会绑定到函数调用的this(new绑定)
- 执行此函数代码
- 若函数无返回值,则自动返回此新对象
Ⅴ.箭头函数的this
function foo(){
return (a)=>{
console.log(this.a);
}
}
var obj1 = {a:2};
var obj2 = {a:3};
var bar = foo.apply(obj1);
bar.apply(obj2); //2
- 箭头函数是ES6中的语法。无法使用以上几种规则。
- 其根据外层的作用域来决定this
- 箭头函数的绑定无法修改,new绑定也不行