JavaScript相关this指向面试题

news/2024/12/25 21:50:12/

这篇文章用来整理this指向相关面试题,我们请看内容

##面试题一

javascript">var name = "window";var person = {name: "person",sayName: function () {console.log(this.name);}
};function sayName() {var sss = person.sayName;sss(); // 绑定: 默认绑定, window -> windowperson.sayName(); // 绑定: 隐式绑定, person -> person(person.sayName)(); // 绑定: 隐式绑定, person -> person(b = person.sayName)(); // 术语: 间接函数引用, window -> window
}sayName();

上面代码执行之后依次打印window person person window

解释一下,这里的第一个window是上面的person对象里面的sayName函数,那么我们这里调用的this为什么不是指向我们preson对象呢?原因是我们sayName方法使用环境,将sayName函数重新赋值给了一个变量sss,我们调用sss()执行sayName方法,当我们调用sss的时候我们当前的环境是在windows中,所以我们可以理解,假如我们直接调用person.sayName()才是打印person对象,也就是我们第二个打印出person的隐式绑定的方法。

第二个person的由来:第二个person我们知道当小括号包裹一段内容这段内容可以看做一个整体,并且先运行,所以我们可以将第一个小括号里的内容先运行出来,没错他就是我们的sayName方法,众所周知,函数体在JavaScript中是堆内存,所以我们执行这里只是调用了这个内存地址,并不关person的事,然后我们将这个内存地址运行的时候就需要在后面加个新的小括号了,这里讲的就是函数的调用。

最后一个window打印的时候,是将我们的person.sayName方法间接赋值给了一个全局变量b并且执行,假如在最后我们打印b变量会发现是ƒ () {console.log(this.name);}和我们打印sss的时候情况一模一样,所以就能体会到JavaScript实际使用中的玄妙了吧。

面试题二

javascript">var name = 'window'// {} -> 对象
// {} -> 代码块
var person1 = {name: 'person1',foo1: function () {console.log(this.name)},foo2: () => console.log(this.name),foo3: function () {return function () {console.log(this.name)}},foo4: function () {// console.log(this) // 第一个表达式this -> person1// console.log(this) // 第二个表达式this -> person2// console.log(this) // 第三个表达式this -> person1return () => {console.log(this.name)}}
}var person2 = { name: 'person2' }// 开始题目:
person1.foo1(); // 隐式绑定: person1
person1.foo1.call(person2); // 显式绑定: person2person1.foo2(); // 上层作用域: window
person1.foo2.call(person2); // 上层作用域: windowperson1.foo3()(); // 默认绑定: window
person1.foo3.call(person2)(); // 默认绑定: window
person1.foo3().call(person2); // 显式绑定: person2person1.foo4()(); // person1
person1.foo4.call(person2)(); // person2
person1.foo4().call(person2); // person1

上面执行之后在控制台打印:
person1 person2 window window window window person2 person1 person2 person1
person1.foo1()
这里打印person1,由于是由person1调用的foo1函数,是隐式调用的方式,this指向persion1对象。所以这里的thisname就是person1
person1.foo1.call(person2)
这里为什么会打印出person2呢,这是由于我们使用了call显式绑定,person2对象的name属性又是person2,我们又知道显式绑定的优先级高于隐式绑定,所以才打印我们的person1而不是person2

person1.foo2()
这里又为什么打印window呢?原因就是我们的箭头函数,箭头函数是没有作用域的,所以 foo2: () => console.log(this.name)中的this指向的就是window对象,那么有人又要有疑问了,箭头函数没有作用域的话他指向的name不应该是person1吗?不是的,在JavaScript中Object类型没有他的作用域的,我们的方法才具备块级作用域。所以就打印我们的window,这里的window是我们在顶级作用域定义的name,假如我们顶级没定义name的话,打印就的就是空。
person1.foo2.call(person2)
这里我们打印出来window,是为什么呢?由于我们看到使用了call显式调用数据,那不应该打印persion2吗?不是我们不能调用persion2,因为箭头函数没有作用域,这个特性要大于其他的特点。所以还是要乖乖打印顶级定义的name变量。

person1.foo3()()
这里打印我们window,原因是什么呢?person1.foo3函数调用返回了一个函数,注意这里返回的函数是一个函数地址,也就是把函数所对应的栈内存地址返回了,第二个括号调用我们的占内存地址,也就是我们的堆内存。由堆内存执行this。这里执行的this所对应的是我们的顶级作用域window,打印出的也就是变量name

person1.foo3.call(person2)()
这里我们调用的不应该打印我们person2对应的值吗?不是的,原因是foo3调用返回是一个方法,不能用call即使使用了call所改变的也不是我们person1函数的name。所以还是打印的结果和person1.foo3()()一样都是window

person1.foo3().call(person2)
这里打印出来的就是我们的person2,为什么是这个结果呢,由于我们将person1.foo3调用了,并且使用call关键词显示绑定为person2对应的name

person1.foo4()()
这里输出我们的person1,因为返回的是一个箭头函数,又因为箭头函数是没有作用域的,所以这里的this就是指向我们的person1,打印我们的person1

person1.foo4.call(person2)()
这里person1.foo4使用了显示调用call为什么在这里可以使用person2的name,并且把他打印出来,person1.foo4返回的是箭头函数,也就是我们内存地址,在没有调用箭头函数之前将person2对象绑定到person1.foo4函数中。紧接着调用,就能打印出person2对应的值

person1.foo4().call(person2)
这里打印出person1,原因是箭头函数的问题,箭头函数没有作用域所以也就不能绑定this,显式绑定就失去了作用,依然打印person1不打印person2

面试题三

javascript">var name = 'window'/*1.创建一个空的对象2.将这个空的对象赋值给this3.执行函数体中代码4.将这个新的对象默认返回
*/
function Person(name) {this.name = namethis.foo1 = function () {console.log(this.name)},this.foo2 = () => console.log(this.name),this.foo3 = function () {return function () {console.log(this.name)}},this.foo4 = function () {return () => {console.log(this.name)}}
}// person1/person都是对象(实例instance)
var person1 = new Person('person1')
var person2 = new Person('person2')// 面试题目:
person1.foo1() // 隐式绑定: person1
person1.foo1.call(person2) // 显式绑定: person2person1.foo2() // 上层作用域查找: person1
person1.foo2.call(person2) // 上层作用域查找: person1person1.foo3()() // 默认绑定: window
person1.foo3.call(person2)() // 默认绑定: window
person1.foo3().call(person2) // 显式绑定: person2person1.foo4()() // 上层作用域查找: person1(隐式绑定)
person1.foo4.call(person2)() //  上层作用域查找: person2(显式绑定)
person1.foo4().call(person2) // 上层作用域查找: person1(隐式绑定)

上面代码执行后打印:
person1 person2 person1 person1 window window person2 person1 person2 person1

person1.foo1()
这里打印我们的person1,为什么打印person1,由上面代码看出我们的person1是由一个方法调用出来的,这个类的参数是person1,Person类中的this就是指向我们的方法,我们person1调用foo(),这里foo1是有自己的作用域的,但是在自己作用域中没找到name,就到上级作用域找,找到person1

person1.foo1.call(person2)
打印person2是为什么呢?由于我们的foo1使用的是默认绑定打印this.name,call显式绑定person2,显式绑定优先级要高于隐式绑定,所以我们当然要调用person2this.name打印
person1.foo2()
这里主要是由于我们的箭头函数没有作用域,直接去上级找this.name打印person1
person1.foo2.call(person2)
这里打印person1person1.foo2是一个箭头函数,没有作用域,所以调用call然并卵。还是打印person1中的name
person1.foo3()()
window为什么呢?person1.foo3返回的是一个方法,这个方法又在最顶层首先调用并且再次调用person1方法
window为什么呢?person1.foo3返回的是一个方法,这个方法又在最顶层首先调用并且再次调用person1方法

** person1.foo3.call(person2)()**
这里也是返回window,原因是person1.foo3并没调用函数就直接call绑定,返回的依然是window,依旧是默认值
person1.foo3().call(person2)
这里打印我们的person2,显式绑定和上面的操作不同这里调用foo3方法之后call,可以改变name的值,用了显示绑定。
person1.foo4()()
这里打印person1,返回的是箭头函数没有作用域,直接上层查找返回person1,属于隐式绑定
person1.foo4.call(person2)()
这里person1.foo4调用person2函数,虽然说person1.foo4返回的是一个箭头函数但是我们将person2绑定到了person1.foo4上,在调用之后this.name改变了,打印出我们的person2
person1.foo4().call(person2)
这里打印person1,方法person1.foo4先调用,因为是箭头函数所以call绑定就失效了。还是调用隐式绑定的person1

面试题四

javascript">var name = 'window'/*1.创建一个空的对象2.将这个空的对象赋值给this3.执行函数体中代码4.将这个新的对象默认返回
*/
function Person(name) {this.name = namethis.obj = {name: 'obj',foo1: function () {return function () {console.log(this.name)}},foo2: function () {return () => {console.log(this.name)}}}
}var person1 = new Person('person1')
var person2 = new Person('person2')person1.obj.foo1()() // 默认绑定: window
person1.obj.foo1.call(person2)() // 默认绑定: window
person1.obj.foo1().call(person2) // 显式绑定: person2person1.obj.foo2()() // 上层作用域查找: obj(隐式绑定)
person1.obj.foo2.call(person2)() // 上层作用域查找: person2(显式绑定)
person1.obj.foo2().call(person2) // 上层作用域查找: obj(隐式绑定)

上述代码执行后将会打印出来:
window window person2 obj person2 obj

person1.obj.foo1()()
这里有一对象person1调用obj对象,然而obj返回了一个函数,这个函数打印了this.name,调用obj对象又调用foo1,使用了默认绑定,返回的是顶层定义的name.

person1.obj.foo1.call(person2)()
这里打印的为什么和上面的一样呢?原因其实很简单我们在一个作用域调用另外一个作用域返回的方法,这里的作用域使用call显式绑定也就不起作用了,因为foo1不是用this.foo1 = 定义的。这里使用的默认绑定返回window
person1.obj.foo1().call(person2)
这里打印person2,为啥就起作用了,person1.obj.foo1是一个内存地址,内存地址调用之后,将返回的方法绑定到了person2上,这里用了显示绑定,返回person2
person1.obj.foo2()()
这里打印了obj,person1.obj.foo2返回了一个箭头函数,箭头函数没有作用域,直接在上层作用域查找,打印obj
person1.obj.foo2.call(person2)()
这里将person2绑定到person1.obj.foo2方法上,使用了显式绑定,打印person2
person1.obj.foo2().call(person2)
这里先调用person1.obj.foo2,然后再绑定person2这里绑定失败,所以打印obj使用了隐式绑定
感谢大家观看,我们下次见


http://www.ppmy.cn/news/1558089.html

相关文章

MQTT入门:在Spring Boot中建立连接及测试

文章目录 项目配置编写MQTT测试类代码解析 验证连接状态运行测试总结 在物联网(IoT)应用中,消息队列遥测传输(MQTT)作为一种轻量级的消息协议,因其低带宽需求和高效传输特点,广泛应用于设备通信…

MONI后台管理系统-系统三员的设计

前言: 本项目设计立足于等保测评(国家信息安全等级保护三级认证),国家信息安全等级保护三级认证中的“三员管理”是指为了确保信息系统的安全性、稳定性和可靠性,而设立的系统管理员、安全管理员和审计管理员三个独立角…

golang异常

panic如果不处理会导致应用进程挂掉 defer recover可以处理这种情况 一个recover只处理自己协程 产生panic的情况 空指针 数组越界 空map中添加键值对 错误,error接口,不严重 error.wrapof解决嵌套问题或者error.unwrap erroe.is方法,判断是…

neo4j console 报错

项目场景: neo4j 开启失败 问题描述 在终端打开 neo4j 失败打开cmd, 输入: neo4j console 报错 原因分析: 1 可能是没有配置环境变量2 当前脚本的执行策略有问题 解决方案: 解决没有配置环境变量 添加环境变量 在path路径中将变量添加进去…

企业级NoSql数据库REDIS集群

1.1数据库主要分为两大类:关系型数据库与 NoSQL数据库 关系型数据库,是建立在关系模型基础上的数把库,其借助于集合代数等数学概念和方法来处理数据库中的数掘主流的 MySQLOracle、Ms sOLSerer和 DB2 都属于这类传统数据库 NoSQL数据库,全称…

[Unity]Unity集成NuGet-连接mysql时的发现

本次使用软件信息: Unity:2022.3.34f1c1。 mysql:mysql 8.0 安装于远程服务器。 使用插件:NuGetForUnity4.1.1.unitypackage 点击名称可前往下载界面。 一、导入插件 打开Unity的时候可直接双击导入道assets。导入后如下图&…

java 核心知识点——JVM

目录 什么是Java虚拟机? 虚拟机架构设计 为什么用元空间取代永久代 双亲委派机制 分代年龄为什么是15次 GC算法 如何判断一个对象可以被回收 什么是Java虚拟机? Java virtual machine。虚拟机,像真实的计算机一样,能够运行…

centos-stream9系统安装docker

如果之前安装过docker需要删除之前的。 for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo yum -y remove $pkg; done 安装yum-utils工具: dnf -y install yum-utils dnf-plugins-core 设置存储库&…