创作不易,请大家多多点赞👍、评论、收藏哦!
面试中经常出现的this问题,我们今天就一起来总结下,把this的面试题一网打尽!
让我们来看看在函数执行过程中调用位置如何决定this的绑定元素的;
四种绑定规则
1.默认绑定
function foo(){
console.log(this.a)
}
var a = 2
foo()
大家看下这段代码,应该都不陌生吧,请问第二行的log会打印出来什么?当调用foo()时,this是默认绑定,指向的全局变量,所以打印出2。
function foo() {
"use strict";
console.log(this.a);
}
var a = 2;
foo();
如果在foo中加上"use strict"呢?这里有一个细节需要注意,只有在foo()运行在非strict mode下时,默认绑定才能绑定到全局对象,因此这段代码log是会报TypeError错的。
2.隐式绑定
function foo() {
console.log(this.a);
}
var obj = {
a: 2,
foo,
};
obj.foo();
当foo()被调用时,它前面有obj;隐式绑定会把this绑定在obj上,因此会打印出2。
function foo() {
console.log(this.a);
}
var obj2 = {
a: 42,
foo: foo,
};
var obj1 = {
a: 2,
obj2,
};
obj1.obj2.foo();
如果foo()前面有多个对象呢?多个调用对象,起作用的是离foo()最近的那个,也就是说会打印出42。
隐式丢失
function foo() {
console.log(this.a);
}
var obj = {
a: 2,
foo: foo,
};
var bar = obj.foo;
var a = "global";
bar();
这是常见的隐式丢失,被隐式绑定的方法会丢失绑定对象;bar是obj.foo的一个引用,实际还是foo方法本身,bar()实际应用了默认绑定,第二行会打印出global;
function foo() {
console.log(this.a);
}
function doFoo(fn) {
// fn方法调用的地方
fn();
}
var obj = {
a: 2,
foo: foo,
};
var a = "global";
doFoo(obj.foo);
当我们给doFoo方法传入函数时也会被隐式赋值,fn()被调用时会被应用默认绑定,结果和上个例子一样,也会打印出global;
3.显示绑定
JS提供的call、apply可以去改变this的指向,我们上面的隐式丢失也可以通过call、apply硬绑定的方式来解决;
function foo() {
console.log(this.a);
}
var obj = { a: 2 };
var bar = function () {
foo.call(obj);
};
bar();
来看上面这个例子,创建了bar()方法,在内部用foo.call(obj)调用了foo方法,强制将foo的this绑定到obj,无论之后怎么调用bar,它总会手动在obj上调用foo,因此会打印2。
4.new绑定
使用new来调用函数,会执行下面的操作。
function foo(a) {
this.a = a;
}
var bar = new foo(2);
console.log(bar.a);
在上面这个例子中,使用new来调用foo()时,我们会构造一个新对象并把它绑定到foo()调用的this上,因此会打印2。
优先级比较
我们已经知道了四种thi绑定的方式,那它们的优先级是什么样呢?毫无疑问,默认绑定的优先级是最低的,那我们先看一下隐式绑定和显式绑定哪个优先级高?
function foo() {
console.log(this.a);
}
var obj1 = {
a: 2,
foo: foo,
};
var obj2 = {
a: 3,
foo: foo,
};
obj1.foo();//2
obj2.foo();//3
obj1.foo.call(obj2);//3
obj2.foo.call(obj1);//2
可以看到,显示绑定的优先级更高。现在我们需要看一下new绑定和隐式绑定的优先级谁高?
function foo(something) {
this.a = something;
}
var obj1 = {
foo: foo,
};
var obj2 = {};
obj1.foo(2);
console.log(obj1.a); // 2
obj1.foo.call(obj2, 3);
console.log(obj2.a); // 3
var bar = new obj1.foo(4);
console.log(obj1.a); // 2
console.log(bar.a); // 4
可以看到new绑定比隐式绑定优先级更高。但是new绑定和显式绑定谁的的优先级更高呢?
function foo(something) {
this.a = something;
}
var obj1 = {};
var bar = foo.bind(obj1);
bar(2);
console.log(obj1.a); // 2
var baz = new bar(3);
console.log(obj1.a); // 2
console.log(baz.a); // 3
上面的例子bar被硬绑定到obj1上,但new bar(3)并没有像我们预计的那样把obj1.a修改为3;相反new修改了之前bar的this;使用new之后,新对象baz,baz.a的值为3。 因此我们知道了优先级的排名,new>显示绑定>隐式绑定>默认绑定;
箭头函数
除了这四种this绑定规则,ES6中还介绍了一种特殊的函数类型:箭头函数;它的this是根据外层(函数或全局)的作用域来决定的;
var obj = {
name: 'obj',
foo1: () => {
console.log(this.name)
},
foo2: function () {
console.log(this.name)
return () => {
console.log(this.name)
}
}
}
var name = 'window'
obj.foo1()
obj.foo2()()
箭头函数内的this是由外层作用域决定的;
- 对于obj.foo(),它的外层是window,对象obj不是作用域,(作用域分为全局作用域window和局部作用域),所以会打印window;
- obj.foo2()(),首先会执行obj.foo(),它是普通函数,所以它的this是调用它的obj,因此打印出obj,而它返回的是一个箭头函数,它的this由外层作用域决定,也就是foo2,那它的this也是obj;