再来40道this面试题酸爽继续|博客系列
# 前期准备
在正式阅读之前,你需要知道this的5种绑定方式:
- 默认绑定(非严格模式下this指向全局对象, 严格模式下
this会绑定到undefined) - 隐式绑定(当函数引用有上下文对象时, 如
obj.foo()的调用方式,foo内的this指向obj) - 显示绑定(通过
call()或者apply()方法直接指定this的绑定对象, 如foo.call(obj)) - new绑定
- 箭头函数绑定(
this的指向由外层作用域决定的)
# 1.1 题目一
老规矩,来看个最基本的案例:
var a = 10;
function foo () {
console.log(this.a)
}
foo();
我们知道在使用var创建变量的时候(不在函数里),会把创建的变量绑定到window上,所以此时a是window下的属性。
而函数foo也是window下的属性。
因此上面的代码其实就相当于是这样:
window.a = 10;
function foo() {
console.log(this.a)
}
window.foo();
在这里,调用foo()函数的是window对象,且又是在非严格模式下,所以foo()中this的指向是window对象,因此this.a会输出10。
答案:
10
# 1.2 题目二
改造下题目一,看看在严格模式下。
(想要开启严格模式,只要在需要开启的地方写上"use strict")
"use strict";
var a = 10;
function foo () {
console.log('this1', this)
console.log(window.a)
console.log(this.a)
}
console.log(window.foo)
console.log('this2', this)
foo();
需要注意的点:
-
开启了严格模式,只是说使得函数内的
this指向undefined,它并不会改变全局中this的指向。因此this1中打印的是undefined,而this2还是window对象。 -
另外,它也不会阻止
a被绑定到window对象上。
所以最后的执行结果:
f foo() {...}
'this2' Window{...}
'this1' undefined
10
Uncaught TypeError: Cannot read property 'a' of undefined
# 1.3 题目三
let a = 10
const b = 20
function foo () {
console.log(this.a)
console.log(this.b)
}
foo();
console.log(window.a)
如果把var改成了let 或者 const,变量是不会被绑定到window上的,所以此时会打印出三个undefined。
答案:
undefined
undefined
undefined
# 1.4 题目四
var a = 1
function foo () {
var a = 2
console.log(this)
console.log(this.a)
}
foo()
这里我们很容易就知道,foo()函数内的this指向的是window,因为是window调用的foo。
但是打印出的this.a呢?注意,是this.a,不是a,因此是window下的a。
并且由于函数作用域的原因我们知道window下的a还是1。
因此答案为:
Window{...}
1
# 1.5 题目五
把题目1.4改造一下 😁。
var a = 1
function foo () {
var a = 2
function inner () {
console.log(this.a)
}
inner()
}
foo()
其实这里和1.4很像,不过一看到函数内的函数,就很容易让人联想到闭包 😂,然后… 然后就脱口而出,答案是2啊,这还不简单。
小伙伴们,审题可得仔细啊,这里问你的是this.a,而在inner中,this指向的还是window。
答案:
1
(我知道有的小伙伴不满这种题目,这吖的不就是和人玩文字游戏吗?有什么技术含量,但是现实就是这样,很多面试官会喜欢问这种细节题来考察你细心不细心。在没能力改变这种情况的前提下,你只能试着接受它…)
# 2. 隐式绑定
OK👌,介绍完了默认绑定之后,让我们来看看第二种隐式绑定。
其实大佬 sunshine小小倩 教了我们一个简单的规则,this 永远指向最后调用它的那个对象。
谁最后调用的函数,函数内的this指向的就是谁(不考虑箭头函数)。
(了解上下文对象的小伙伴应该都知道,这与上下文对象以及作用域有关,想要了解的小伙伴可以看我的这篇文章《JavaScript进阶-执行上下文栈和变量对象(一周一更)》
下面你可以用这个规则来做题啦 😁。
# 2.1 题目一
function foo () {
console.log(this.a)
}
var obj = { a: 1, foo }
var a = 2
obj.foo()
(var obj = { foo }就相当于是var obj = { foo: foo },这个大家应该都知道吧)
在这道题中,函数foo()虽然是定义在window下,但是我在obj对象中引用了它,并将它重新赋值到obj.foo上。
且调用它的是obj对象,因此打印出来的this.a应该是obj中的a。
换个角度想,上面👆这段代码是不是就相当于是这样:
var obj = {
a: 1,
foo: function () {
console.log(this.a)
}
}
var a = 2
obj.foo()
在这里foo函数内的this指向的就是obj,和题目效果一样。
答案都是:
1
有小伙伴就会有有疑问了,obj.foo()不就相当于是window.obj.foo()吗?
那foo()内的this到底是算window呢,还是obj呢?
请注意我前面说的,是最后调用函数的对象,显然,obj要比window更后面一点。
# 3. 隐式绑定的隐式丢失问题
隐式绑定的基本概念大家应该都清楚了,不过其实有一个关于隐式绑定的常用考点,那就是隐式丢失问题。
隐式丢失其实就是被隐式绑定的函数在特定的情况下会丢失绑定对象。
有两种情况容易发生隐式丢失问题:
- 使用另一个变量来给函数取别名
- 将函数作为参数传递时会被隐式赋值,回调函数丢失this绑定
我们一种一种来看哈。
# 3.1 题目一
使用另一个变量来给函数取别名会发生隐式丢失。
function foo () {
console.log(this.a)
};
var obj = { a: 1, foo };
var a = 2;
var foo2 = obj.foo;
obj.foo();
foo2();
执行这段代码会打印出啥呢 🤔️?
在这里我们已经知道了,obj.foo()中this的指向是为obj的(可以看第二部分隐式绑定),所以obj.foo()执行的时候,打印出来的是obj对象中的a,也就是1。
但是foo2它不也是obj.foo吗?我只不过是用了一个变量foo2来盛放了它而已。所以你是不是认为它打印的也是1呢?
额 😅,其实这里不是的,它打印出的是window下的a。
答案:
1
2
这是因为虽然foo2指向的是obj.foo函数,不过调用它的却是window对象,所以它里面this的指向是为window。
其实也就相当于是window.foo2(),如果你不相信的话,可以看下面一题👇。
# 3.2 题目二
让我们在一个新的变量obj2中也定义一个foo2看看:
function foo () {
console.log(this.a)
};
var obj = { a: 1, foo };
var a = 2;
var foo2 = obj.foo;
var obj2 = { a: 3, foo2: obj.foo }
obj.foo();
foo2();
obj2.foo2();
这三种不同的foo()打印出来的分别是什么呢?
答案:
1
2
3
obj.foo()中的this指向调用者objfoo2()发生了隐式丢失,调用者是window,使得foo()中的this指向windowfoo3()发生了隐式丢失,调用者是obj2,使得foo()中的this指向obj2
# 3.3 题目三
再就是如果你把一个函数当成参数传递时,也会被隐式赋值,发生意想不到的问题。
来看看这道题目:
function foo () {
console.log(this.a)
}
function doFoo (fn) {
console.log(this)
fn()
}
var obj = { a: 1, foo }
var a = 2
doFoo(obj.foo)
这里我们将obj.foo当成参数传递到doFoo函数中,在传递的过程中,obj.foo()函数内的this发生了改变,指向了window。
因此结果为:
Window{...}
2
注意,我这里说的是obj.foo()函数,而不是说doFoo()。doFoo()函数内的this本来就是指向window的,因为这里是window调用的它。
但是你不要以为是doFoo()函数内的this影响了obj.foo(),不信你看下一题。
# 3.4 题目四
现在我们不用window调用doFoo,而是放在对象obj2里,用obj2调用:
function foo () {
console.log(this.a)
}
function doFoo (fn) {
console.log(this)
fn()
}
var obj = { a: 1, foo }
var a = 2
var obj2 = { a: 3, doFoo }
obj2.doFoo(obj.foo)
现在调用obj2.doFoo()函数,里面的this指向的应该是obj2,因为是obj2调用的它。
但是obj.foo()打印出来的a依然是2,也就是window下的。
执行结果为:
{ a:3, doFoo: f }
2
所以说,如果你把一个函数当成参数传递到另一个函数的时候,也会发生隐式丢失的问题,且与包裹着它的函数的this指向无关。在非严格模式下,会把该函数的this绑定到window上,严格模式下绑定到undefined。
一样的代码,试试严格模式下:
