web前端 -关于this的面试题

本文详细探讨了JavaScript中this的默认绑定、隐式绑定、隐式绑定丢失以及显式绑定四种情况,通过一系列面试题解析了不同场景下this的指向问题。包括函数独立调用、严格模式、对象链式调用、函数别名、函数作为参数传递、回调函数等场景,帮助读者深入理解this的工作机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、默认绑定
默认绑定通常是指函数独立调用,不涉及其他绑定规则。非严格模式下,this指向window,严格模式下,this指向undefined。

01、题目1.1:非严格模式
var foo = 123;
function print(){
this.foo = 234;
console.log(this); // window
console.log(foo); // 234
}
print();
非严格模式,print()为默认绑定,this指向window,所以打印window和234。

这个foo值可以说道两句:如果学习过预编译的知识,在预编译过程中,foo和print函数会存放在全局GO中(即window对象上),所以上述代码就类似下面这样:

window.foo = 123
function print() {
this.foo = 234;
console.log(this);
console.log(window.foo);
}
window.print()
02、题目1.2:严格模式
把题目1.1稍作修改,看看严格模式下的执行结果。

"use strict"可以开启严格模式

“use strict”;
var foo = 123;
function print(){
console.log('print this is ', this);
console.log(window.foo)
console.log(this.foo);
}
console.log('global this is ', this);
print();
注意事项:开启严格模式后,函数内部this指向undefined,但全局对象window不会受影响

答案

global this is Window{…}
print this is undefined
123
Uncaught TypeError: Cannot read property ‘foo’ of undefined

03、题目1.3:let/const
let a = 1;
const b = 2;
var c = 3;
function print() {
console.log(this.a);
console.log(this.b);
console.log(this.c);
}
print();
console.log(this.a);
let/const定义的变量存在暂时性死区,而且不会挂载到window对象上,因此print中是无法获取到a和b的。

答案

undefined
undefined
3
undefined
04、题目1.4:对象内执行
a = 1;
function foo() {
console.log(this.a);
}
const obj = {
a: 10,
bar() {
foo(); // 1
}
}
obj.bar();
foo虽然在obj的bar函数中,但foo函数仍然是独立运行的,foo中的this依旧指向window对象。

05、题目1.5:函数内执行
var a = 1
function outer () {
var a = 2
function inner () {
console.log(this.a) // 1
}
inner()
}
outer()
这个题与题目1.4类似,但要注意,不要把它看成闭包问题

06、题目1.6:自执行函数
a = 1;
(function(){
console.log(this);
console.log(this.a)
}())
function bar() {
b = 2;
(function(){
console.log(this);
console.log(this.b)
}())
}
bar();
默认情况下,自执行函数的this指向window

自执行函数只要执行到就会运行,并且只会运行一次,this指向window。

答案

Window{…}
1
Window{…}
2 // b是imply global,会挂载到window上
2、隐式绑定
函数的调用是在某个对象上触发的,即调用位置存在上下文对象,通俗点说就是**XXX.func()**这种调用模式。

此时func的this指向XXX,但如果存在链式调用,例如XXX.YYY.ZZZ.func,记住一个原则:this永远指向最后调用它的那个对象。

07、题目2.1:隐式绑定
var a = 1;
function foo() {
console.log(this.a);
}
// 对象简写,等同于 {a:2, foo: foo}
var obj = {a: 2, foo}
foo();
obj.foo();
foo(): 默认绑定,打印1

obj.foo(): 隐式绑定,打印2

答案

1
2
obj是通过var定义的,obj会挂载到window之上的,obj.foo()就相当于window.obj.foo(),这也印证了this永远指向最后调用它的那个对象规则。

08、题目2.2:对象链式调用
感觉上面总是空谈链式调用的情况,下面直接来看一个例题:

var obj1 = {
a: 1,
obj2: {
a: 2,
foo(){
console.log(this.a)
}
}
}
obj1.obj2.foo() // 2
3、隐式绑定的丢失
隐式绑定可是个调皮的东西,一不小心它就会发生绑定的丢失。一般会有两种常见的丢失:

使用另一个变量作为函数别名,之后使用别名执行函数

将函数作为参数传递时会被隐式赋值

隐式绑定丢失之后,this的指向会启用默认绑定。

具体来看题目:

09、题目3.1:取函数别名
a = 1
var obj = {
a: 2,
foo() {
console.log(this.a)
}
}
var foo = obj.foo;
obj.foo();
foo();
JavaScript对于引用类型,其地址指针存放在栈内存中,真正的本体是存放在堆内存中的。

上面将obj.foo赋值给foo,就是将foo也指向了obj.foo所指向的堆内存,此后再执行foo,相当于直接执行的堆内存的函数,与obj无关,foo为默认绑定。笼统的记,只要fn前面什么都没有,肯定不是隐式绑定。

答案

2
1
不要把这里理解成window.foo执行,如果foo为let/const定义,foo不会挂载到window上,但不会影响最后的打印结果

10、题目3.2:取函数别名
如果取函数别名没有发生在全局,而是发生在对象之中,又会是怎样的结果呢?

var obj = {
a: 1,
foo() {
console.log(this.a)
}
};
var a = 2;
var foo = obj.foo;
var obj2 = { a: 3, foo: obj.foo }

obj.foo();
foo();
obj2.foo();
obj2.foo指向了obj.foo的堆内存,此后执行与obj无关(除非使用call/apply改变this指向)

答案

1
2
3
11、题目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)
用函数预编译的知识来解答这个问题:函数预编译四部曲前两步分别是:

找形参和变量声明,值赋予undefined

将形参与实参相统一,也就是将实参的值赋予形参。

obj.foo作为实参,在预编译时将其值赋值给形参fn,是将obj.foo指向的地址赋给了fn,此后fn执行不会与obj产生任何关系。fn为默认绑定。

答案

Window {…}
2
12、题目3.4:函数作为参数传递
将上面的题略作修改,doFoo不在window上执行,改为在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)
console.log(this): obj2.doFoo符合xxx.fn格式,doFoo的为隐式绑定,this为obj2,打印{a: 3, doFoo: ƒ}

fn(): 没有于obj2产生联系,默认绑定,打印2

答案

{a: 3, doFoo: ƒ}
2
13、题目3.5:回调函数
下面这个题目我们写代码时会经常遇到:

var name=‘zcxiaobao’;
function introduce(){
console.log('Hello,My name is ', this.name);
}
const Tom = {
name: ‘TOM’,
introduce: function(){
setTimeout(function(){
console.log(this)
console.log('Hello, My name is ',this.name);
})
}
}
const Mary = {
name: ‘Mary’,
introduce
}
const Lisa = {
name: ‘Lisa’,
introduce
}

Tom.introduce();
setTimeout(Mary.introduce, 100);
setTimeout(function(){
Lisa.introduce();
},200);
setTimeout是异步调用的,只有当满足条件并且同步代码执行完毕后,才会执行它的回调函数。

Tom.introduce()执行: console位于setTimeout的回调函数中,回调函数的this指向window

Mary.introduce直接作为setTimeout的函数参数(类似题目题目3.3),会发生隐式绑定丢失,this为默认绑定

Lisa.introduce执行虽然位于setTimeout的回调函数中,但保持xxx.fn模式,this为隐式绑定。

答案

Window {…}
Hello, My name is zcxiaobao
Hello,My name is zcxiaobao
Hello,My name is Lisa
所以如果我们想在setTimeout或setInterval中使用外界的this,需要提前存储一下,避免this的丢失。

const Tom = {
name: ‘TOM’,
introduce: function(){
_self = this
setTimeout(function(){
console.log('Hello, My name is ',_self.name);
})
}
}
Tom.introduce()
14、题目3.6:隐式绑定丢失综合题
name = ‘javascript’ ;
let obj = {
name: ‘obj’,
A (){
this.name += ‘this’;
console.log(this.name)
},
B(f){
this.name += ‘this’;
f();
},
C(){
setTimeout(function(){
console.log(this.name);
},1000);
}
}
let a = obj.A;
a();
obj.B(function(){
console.log(this.name);
});
obj.C();
console.log(name);
本题目不做解析,具体可以参照上面的题目。

答案

javascriptthis
javascriptthis
javascriptthis
javascriptthis
4、显式绑定
显式绑定比较好理解,就是通过call()、apply()、bind()等方法,强行改变this指向。

上面的方法虽然都可以改变this指向,但使用起来略有差别:

call()和apply()函数会立即执行

bind()函数会返回新函数,不会立即执行函数

call()和apply()的区别在于call接受若干个参数,apply接受数组。

15、题目4.1:比较三种调用方式
function foo () {
console.log(this.a)
}
var obj = { a: 1 }
var a = 2

foo()
foo.call(obj)
foo.apply(obj)
foo.bind(obj)
foo(): 默认绑定。

foo.call(obj): 显示绑定,foo的this指向obj

foo.apply(obj): 显式绑定

foo.bind(obj): 显式绑定,但不会立即执行函数,没有返回值

答案

2
1
1

今天爱创课堂小编的分享就到这里了,有想学习前端,了解前端更多知识的同学,欢迎大家私信小编领取前端学习资料,也可以关注,点赞加分享,多多支持小编!!!

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值