this全面解析
首先,需要找到调用函数的位置,例子代码如下:
function foo() {
// 调用栈:全局->foo
console.log('foo');
bar();
}
function bar() {
// 调用栈:全局->foo->bar
console.log('bar');
baz();
}
function baz() {
// 调用栈:全局->foo->bar->baz
console.log('baz');
}
foo(); //从全局开始调用
根据上述的调用栈规则,只要最后调用的函数的上一个函数,那就是他函数所调用的位置,例如baz函数调用的位置位于bar。
搞清楚函数调用位置后,接下来再来了解函数调用时this的绑定规则。
之前说过,this是在函数调用时所绑定的,而this如何被绑定,绑定在哪里,取决于下面四个规则:
- 默认绑定
- 隐式绑定
- 显式绑定
- new绑定
默认绑定
function foo() {
foo.a = 5;
console.log(this.a);
}
var a = 10;
foo(); //10
像上述代码,由函数直接进行调用,且没有由任何对象引用所调用的,会执行this的默认绑定。在非严格模式下,会将this绑定到全局对象中(也就是this),在严格模式下,this将会绑定为undefined。默认绑定就像他的名字那样:优先级别最低。如果没有其他的绑定规则,那么就会触发默认绑定
隐式绑定
function foo() {
console.log(this.a);
}
var obj1 = {
a: 5,
foo: foo
}
obj1.foo(); //5
如上述代码,foo函数作为obj1对象的成员方法之一调用,所以根据隐式绑定的规则,函数调用时obj1对象会绑定到this上
以及下面这种情况:访问obj1的obj2的foo函数,实际上函数调用位置是在obj2:
function foo() {
console.log(this.a);
}
var obj1 = {
a: 5,
obj2: {
a: 15,
foo: foo
}
}
obj1.obj2.foo(); //15
另外,还需要注意下面这种情况:当把函数的引用赋值到另一个变量时,其函数的调用方式可能会发生改变:
function foo() {
console.log(this.a);
}
var obj1 = {
a: 5,
foo: foo
}
var a = 10;
let bar = obj1.foo;
bar() //5
以及,在使用回调函数时,也应特别注意函数的this绑定丢失:
function foo() {
setTimeout(function () {
console.log(this.a);
}, 100);
}
var obj1 = {
a: 5,
foo: foo
}
var a = 10;
obj1.foo(); //10
根据隐式绑定的规则,应该obj1绑定到了this上,可是代码结果显示其this绑定到了全局对象中。
原因是在setTimeout的函数中传入的回调函数,在实际上setTimeout函数中是下面这种:
//类似的代码
function setTimeout(fuc,delay) {
//...其他代码
fuc();
}
其中fuc函数就是传入的回调函数,所以实际上函数调用依然是应用了默认绑定(绑定到全局变量中)
隐式绑定的优先级别比默认绑定要高,但是如果使用不当的话可能会导致this绑定丢失
显式绑定
使用函数自带的方法可以实现显式绑定:
function foo() {
console.log(this.a);
}
var a = 10;
var obj1 = {
a: 5
}
foo.call(obj1); //5
foo.apply(obj1); //5
其中用call或者apply函数可以显式绑定,第一个参数是要绑定到this的对象
call和apply区别在于之后的参数
对于call来说,传入的所有参数依次像普通函数传参式一个个传入:fuc.call(a,b,c,b,d)
而apply,使得所有参数以数组的形式作为第二个参数:fuc.apply(obj1,[a,b,c,d])
显式绑定明确指定了this所绑定的对象。其优先级比隐式绑定要高
new绑定
new绑定是用new操作符来进行构造函数调用时所产生的的绑定,当用new来调用函数时,会执行以下操作:
- 根据传入的参数创建一个对象,如果没有则创建个空对象
- 该对象会执行[[prototype]]的链接(将该函数对象的prototype对象与新创建的对象的[[prototype]]相关联)
- 该对象会绑定函数调用的this
- 如果函数没有返回其他对象(比如返回了字符串、数字等,或者没有return),那么new调用的函数会自动返回这个新创建的对象
箭头函数
ES6出现的新的特殊函数类型,该函数中的this不再适用于之前所写的四种this绑定规则,而是一种全新的规则:this绑定于当前箭头函数外层的作用域