初识this

93 阅读2分钟

这是我参与更文挑战的第8天,活动详情查看:更文挑战

一.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,

2.隐式绑定(作为对象的属性调用)

当函数引用有上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象。

举个例子:

function aa(){

console.log(this.a);

}

var obj = {

a:2,

aa:aa

};

obj.aa(); // 2

因为调用 aa()时,this绑定到obj,所以this.a 与 obj.a是一样的。

需要注意的是,对象属性引用链中,只有上一层或者最后一层在调用位置中起作用。

隐式丢失:

隐式绑定的函数在某些情况下可能会丢失绑定对象,从而使用默认绑定(非严格模式)。

举个例子:

function aa(fn){

console.log(this.a);

}

function doAA(fn){

fn(); <---调用位置

}

var obj = {

a:2,

aa:aa

}

var a = "global";

doAA(obj.aa); // "global"

参数传递其实就是一种隐式赋值,fn是obj.aa的一个引用,但是它引用的是函数本身。所以fn()前面没有带任何的修饰函数调用,因此用了默认绑定。

回调函数丢失this绑定这种情况是格外需要注意的。

3.通过call,apply 显式绑定

如果我们不想在对象内部包含函数引用,而是想在某个对象上强制调用函数,那么就可以使用call或者apply来调用。绝大多数函数都可以使用这2个方法。

这两个方法第一个参数是一个对象,是将要绑定到this的对象 ,第二个参数是要向执行函数传递的参数。举个例子:

function aa{

console.log(this.a);

}

var obj = {

a:2

}

aa.call(obj);

通过aa.call(),把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。