一、this到底是什么
当一个函数被调用时,会创建一个执行上下文,执行上下文的生命周期如下:
创建 { 生成变量对象,建立作用域链,确定this指向 } —–> 执行{ 变量赋值,函数引用,执行其他代码 } —–> 执行完毕后出栈,等待被回收
我们需要消除对this的误会是,this既不指向函数自身也不指向函数的词法作用域。this就是执行上下文的一个属性,它指向的是执行上下文对象,this是在函数调用时才发生的绑定,它指向什么完全取决于函数在哪里被调用。
二、绑定规则
要想找出this指向的是什么,就要先找到函数执行的调用位置。调用位置是函数在执行过程中被调用的位置而不是声明的位置。比如:
function a(){
console.log('a');
}
function b(){
console.log('b')
a(); ---> 调用位置
}
a(); ---> 调用位置
复制代码
1、默认绑定
默认绑定也叫全局绑定。
在非严格模式下,当执行函数时,如果是不带任何修饰的函数引用进行调用的,就是默认绑定。
当使用默认绑定时,this指向的是全局对象。
举个例子:
var a = 1;
function aa(){
console.log(this.a);
}
function bb(){
var a = 2;
console.log(this.a);
aa();
}
bb(); //输出 1,1
复制代码
2、隐式绑定(作为对象的属性调用)
当函数引用有上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象。
举个例子:
function func() {
console.log(this.a);
}
var obj = {
a: 2,
b: 3 };
obj.func(); // 2
复制代码
因为调用 func()时,this绑定到obj,所以this.a 与 obj.a是一样的。
需要注意的是,对象属性引用链中,只有上一层或者最后一层在调用位置中起作用。
隐式丢失:隐式绑定的函数在某些情况下可能会丢失绑定对象,从而使用默认绑定(非严格模式)。
举个例子:
function func(fn){
console.log(this.a);
}
function do(fn){
fn(); <---调用位置
}
var obj = {
a: 2,
b: 3
}
var a = "global";
do(obj.func); // global
复制代码
参数传递其实就是一种隐式赋值,fn是obj.func的一个引用,但是它引用的是函数本身。所以fn()前面没有带任何的修饰函数调用,因此用了默认绑定。
回调函数丢失this绑定这种情况是格外需要注意的。
3、通过call,apply 显式绑定
如果我们不想在对象内部包含函数引用,而是想在某个对象上强制调用函数,那么就可以使用call或者apply来调用。绝大多数函数都可以使用这2个方法。
这两个方法第一个参数是一个对象,是将要绑定到this的对象 ,第二个参数是要向执行函数传递的参数。
举个例子:
function func{
console.log(this.a);
}
var obj = {
a:2
}
func.call(obj); // 把this绑定在obj上。
复制代码
4、new绑定
在javascript中,构造函数只是一些使用new操作符时被调用的函数。它们不属于某个类,它们只是被new操作符调用的普通函数而已。
使用new来调用函数时,这个新对象就会绑定到函数调用的this。
举个例子:
function aa(a){
this.a = a;
}
var bb = new aa(2);
console.log(bb.a); //2
复制代码
使用new来调用aa()时,就会构造一个新对象绑定到aa()调用中的this上。
三、优先级
现在我们已经知道了有这些绑定规则,但是如果某个调用位置对应多个规则怎么办,所以要给这些规则制定优先级。
以下优先级由高到低:
1、new 绑定。
2、call、apply 调用绑定。
3、由上下文调用绑定,即隐式绑定。
4、默认绑定:在严格模式下绑定到undefined,否则绑定到全局对象。
四、例外的箭头函数
箭头函数不是使用function来定义的,而是使用箭头定义的,它不使用this的四条规则,它会继承外层函数调用的this绑定,非常需要注意的一点是箭头函数的绑定是无法修改的!
举个例子:
function aa(){
return (a) => {
console.log(this.a);
}
}
var obj_1 = {
a:1
}
var obj_2 = {
a:2
}
var ss = aa.call(obj_1) // 1
ss.call(obj_2) // 1
复制代码
上面aa()函数的this绑定到obj_1,所以箭头函数也绑定到obj_1,后面再修改this指向obj_2,,但是不行的,还是输出1。
ps:有错误欢迎指出。
参考:
《你不知道的javascript》