首先要了解的一点是:this是在运行时进行绑定的,而不是在编写时绑定的,它的上下文取决于函数调用时的各种条件
下面来看this的绑定规则
1、默认绑定
最常用的函数调用类型,在普通情况下就是全局,浏览器里就是window;在严格模式下就是undefined
例子:
//非严格模式下
function foo(){
console.log(this.a);
}
var a = 'global';
foo();//global
//严格模式下
function foo(){
'use strict';
console.log(this.a);
}
var a = 'global';
foo();//undefined
2、隐式绑定
当函数引用有上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象。对象引用链中只有上一层或是最后一层在调用位置起作用。
例子1:
function foo(){
console.log(this.a);
}
var obj1 = {
a:2,
obj2:obj2
}
var obj2 = {
a:3,
foo:foo
}
obj1.obj2.foo();//3
一个常见的this绑定问题就是隐式绑定的函数丢失对象,即使用默认绑定,应用默认绑定规则。
例子2:
function foo(){
console.log(this.a);
}
var a = 'global';
var obj = {
a:'local',
foo:foo
}
var bar = obj.foo;//函数别名
bar();//global
虽然bar是obj.foo的一个引用,但实际上引用的是foo函数本身,因此bar函数实际上是一个不带任何修饰的函数调用,即应用默认绑定。
例子3:
function foo(){
console.log(this.a);
}
var a ='global';
var obj = {
a:'local',
foo:foo
}
function doFoo(fn){
fn();
}
doFoo(obj.foo);
参数传递其实就是一种隐式赋值,因此传入函数时也会被隐式赋值,所以结果和例子2一样。
3、显式绑定
使用call()和apply()方式进行绑定。
例子1:
function foo(){
console.log(this.a);
}
var a = 'global';
var obj = {
a:'local',
foo:foo
}
var bar = function(){
foo.apply(obj);//调用foo函数时将this强制绑定到obj上
//foo.call(obj); apply和call产生一样的效果
}
bar();//local
//硬绑定的bar不可能再修改它的this
bar.call(window);//local
ES5提供了内置的方法Function.prototype.bind
例子2:
function foo(something){
return `this.a:${this.a},something:${something}`;
}
var obj = {
a:2
}
var bar = foo.bind(obj);
bar(3);//this.a:2,something:3
4、new绑定
例子1:
function foo(a){
this.a = a;
}
var bar = new foo(2);
console.log(bar.a);//2
5、this绑定的优先级
优先级排序:new绑定 > 显示绑定 > 隐式绑定 > 默认绑定
6、箭头函数
例子1:
function foo(){
return (a) => {
console.log(this.a);
}
}
var obj1 = {
a:2
}
var obj2 = {
b:3
}
var bar = foo.call(obj1);
bar.call(obj2);//2
箭头函数的绑定无法被更改,new也不行。
ES6之前就已经在使用一种几乎和箭头函数完全一样的模式。
例子2:
function foo(){
var self = this;
setTimeout(function(){
console.log(self.a);
})
}
var obj = {
a:2
};
foo.apply(obj);//2