深度解析JavaScript面试热点:事件循环、上下文、箭头函数、变量作用域与ES6模块
JavaScript面试中经常涉及到事件循环、上下文、箭头函数、变量作用域以及ES6模块等核心概念。通过清晰的代码示例,我们深入讨论这些主题,揭示其中的关键细节。
事件循环(Event Loop)
JavaScript开发者每天都与事件循环打交道,本文通过实际代码展示了 setTimeout、Promise 和同步代码之间的交互。通过分析回应,我们纠正了一些开发者对于Promise构造函数中执行器函数同步调用的错误观念,同时详细讨论了微任务队列和宏任务队列的执行顺序。
setTimeout(() => console.log(1), 0);console.log(2);new Promise(res => {console.log(3);res();
}).then(() => console.log(4));console.log(5);
// 输出结果:2 3 5 4 1
在这个例子中,我们看到了 setTimeout、Promise 以及一些同步代码。
给定0延迟,我们传递给setTimeout的函数会同步调用还是异步调用?
- 尽管
setTimeout函数有零延迟,回调函数是异步调用的。引擎会将回调函数放在回调队列(宏任务队列)中,并在调用栈为空时将其移至调用栈。因此,数字1将被跳过,数字2将首先在控制台中显示。
我们作为参数传递给 Promise 构造函数的函数会同步调用还是异步调用?
-
Promise构造函数接受的函数参数是同步执行的。因此,在控制台中接下来要显示的数字是3。给定零延迟,我们传递给promise的then处理程序的函数会同步调用还是异步调用? -
then方法中的回调是异步执行的,即使promise没有延迟就解决了。与setTimeout不同的是,引擎会将promise回调放在另一个队列中 —— 工作队列(微任务队列),在那里它将等待执行。因此,接下来进入控制台的数字是5。
哪个优先级更高 —— 微任务队列还是宏任务队列,换句话说 —— Promise 还是 setTimeout?
微任务(Promise)比宏任务(setTimeout)有更高的优先级,所以下一个在控制台中的数字将是4,最后一个是1。
通过分析回应,我们可以得出结论,大多数受访者在假设传递给 Promise 构造函数作为参数的执行器函数是异步调用的方面是错误的。
上下文(Context)
在面试中,对于 this 关键字的理解至关重要。我们通过具体的例子讨论了普通函数和箭头函数中 this 的不同行为,并解释了在不同上下文中函数调用的结果。
普通函数
'use strict';function foo() {console.log(this);
}function callFoo(fn) {fn();
}let obj = { foo };callFoo(obj.foo);
// 输出结果:undefined
箭头函数
深入研究了箭头函数内部的 this 值,展示了它与普通函数之间的差异。通过实例强调箭头函数不能作为构造函数使用的限制,以及它们在原型属性上的行为。
'use strict';
var x = 5;
var y = 5;function Operations(op1 = x, op2 = y) {this.x = op1;this.y = op2;
};Operations.prototype.sum = () => this.x + this.y;const op = new Operations(10, 20);console.log(op.sum());
// 输出结果:10
箭头函数没有自己的 this。相反,箭头函数体内的 this 指向该箭头函数定义所在作用域的this 值。
我们的函数是在全局作用域中定义的。
全局作用域中的 this 指向全局对象(即使在严格模式下也是如此)。因此,答案是 10。
另一个关于箭头函数的问题可能是这样的。
const Num = () => {this.getNum = () => 10;
}Num.prototype.getNum = () => 20;const num = new Num();console.log(num.getNum());
箭头函数不能用作构造函数,当使用 new 调用时会抛出错误。它们也没有原型属性:
TypeError:无法设置undefined的属性(设置'getNum')
这样的问题比较少见,但你应该为它们做好准备。你可以在 MDN 上查看更多关于箭头函数的信息。
变量作用域
通过临时死区、变量声明提升等概念,详细探讨了变量作用域。通过具体的代码案例,让读者更好地理解在不同作用域中变量的行为。
'use strict';console.log(foo());let bar = 'bar';function foo() {return bar;
}bar = 'baz';
// 输出结果:ReferenceError: bar is not defined
在let / const变量定义之前的作用域中的位置被称为临时死区。
如果我们在 let / const 变量定义之前尝试访问它们,将会抛出引用错误。
这种行为是因为 const 变量而被选中的。访问未定义的 var 变量时,我们得到的是undefined。对于 const 变量来说,这是不可接受的,因为它将不再是一个常量。
let 变量的行为以类似的方式完成,以便您可以轻松地在这两种类型的变量之间切换。
回到我们的例子。
由于函数调用在 bar 变量的定义之上,该变量处于临时死区。
代码抛出一个错误:
ReferenceError:初始化前不能访问'bar'
let func = function foo() {return 'hello';
}console.log(typeof foo);
在命名函数表达式中,名称只在函数体内部是局部的,外部无法访问。因此,全局作用域中不存在foo。
typeof运算符对未定义的变量返回undefined。
function foo(bar, getBar = () => bar) {var bar = 10;console.log(getBar());
}foo(5);
对于具有复杂参数(解构、默认值)的函数,参数列表被封闭在其自己的作用域内。
因此,在函数体中创建 bar 变量不会影响参数列表中同名的变量,getBar() 函数通过闭包从其参数中获取 bar。
一般来说,我们注意到尽管ES6已经发布了7年多,但开发人员对其特性的理解仍然很差。当然,每个人都知道这个版本中特性的语法,但只有少数人能更深入地理解它。
ES6模块
对ES6模块的导入机制进行了解析,强调了导入会被提升的特性。通过示例,展示了模块间依赖的加载顺序。
console.log('index.js');import { sum } from './helper.js';console.log(sum(1, 2));
// 输出结果:helper.js index.js 3
导入会被提升。
提升是JS中的一种机制,其中变量和函数声明在代码执行之前被移动到它们的作用域的顶部。
所有依赖项将在代码运行之前加载。
所以,答案是:helper.js index.js 3
'use strict';var num = 8;function num() {return 10;
}console.log(num);
函数和变量声明被放在其作用域的顶部,变量的初始化发生在脚本执行时。
具有相同名称的变量的重复声明将被跳过。
函数总是首先被提升。无论函数和具有相同名称的变量的声明在代码中以何种顺序出现,函数都优先,因为它上升得更高。
示例1
function num() {}
var num;
console.log(typeof num); // function
示例2
var num;
function num() {}
console.log(typeof num); // function
变量总是在最后被初始化。
var num = 8; function num() {}
将被转换为:
function num() {}
var num; // repeated declaration is ignored
num = 8;
结果,num = 8。
提高难度
import foo from './module.mjs';console.log(typeof foo);-------------------------
foo = 25;export default function foo() {}
结果export default function foo() {}
等于
function foo() {}
export { foo as default }
在引擎处理完模块代码后,你可以将其想象成以下形式:
function foo() {}
foo = 25;
export { foo as default }
所以正确答案是数字。
Promises
最后,通过一个Promise链的例子,深入讨论了Promise的执行过程,特别是在抛出错误和使用 catch 处理程序时的执行流程。
Promise.resolve(1).then(x => { throw x }).then(x => console.log(`then ${x}`)).catch(err => console.log(`error ${err}`)).then(() => Promise.resolve(2)).catch(err => console.log(`error ${err}`)).then(x => console.log(`then ${x}`));
// 输出结果:error 1 then 2
这篇文章通过清晰的代码示例和深度解析,为读者提供了深入理解JavaScript核心概念的机会,是面试前的必读材料。
相关文章:
深度解析JavaScript面试热点:事件循环、上下文、箭头函数、变量作用域与ES6模块
JavaScript面试中经常涉及到事件循环、上下文、箭头函数、变量作用域以及ES6模块等核心概念。通过清晰的代码示例,我们深入讨论这些主题,揭示其中的关键细节。 事件循环(Event Loop) JavaScript开发者每天都与事件循环打交道&am…...
Javaweb之Mybatis的动态SQL的详细解析
3. Mybatis动态SQL 3.1 什么是动态SQL 在页面原型中,列表上方的条件是动态的,是可以不传递的,也可以只传递其中的1个或者2个或者全部。 而在我们刚才编写的SQL语句中,我们会看到,我们将三个条件直接写死了。 如果页面…...
物联网与智能家居:跨境电商与未来生活的融合
物联网(Internet of Things,IoT)和智能家居技术正迅速崛起,成为跨境电商领域的创新引擎。这两者的巧妙结合不仅为消费者提供更智能、便捷的生活方式,同时也为电商平台和制造商带来了全新的商机。本文将深入探讨物联网与…...
Java内存模型(JMM)是基于多线程的吗
Java内存模型(JMM)是基于多线程的吗 这个问题按我的思路转换了下,其实就是在问:为什么需要Java内存模型 总结起来可以由几个角度来看待「可见性」、「有序性」和「原子性」 面试官:今天想跟你聊聊Java内存模型&#…...
Linux离线安装MySQL(rpm)
目录 下载安装包安装MySQL检测安装结果服务启停MySQL用户设置 下载安装包 下载地址:https://downloads.mysql.com/archives/community/ 下载全量包如:(mysql-8.1.0-1.el7.x86_64.rpm-bundle.tar) 解压:tar -xzvf mysql-8.1.0-1.el7.x86_64.…...
用 Socket.D 替代原生 WebSocket 做前端开发
socket.d.js 是基于 websocket 包装的 socket.d 协议的实现。就是用 ws 传输数据,但功能更强大。 功能原生 websocketsocket.d说明listen有有监听消息send有有发消息sendAndRequest无有发消息并接收一个响应(类似于 http)sendAndSubscribe无…...
Transformer架构和对照代码详解
1、英文架构图 下面图中展示了Transformer的英文架构,英文架构中的模块名称和具体代码一一对应,方便大家对照代码、理解和使用。 2、编码器 2.1 编码器介绍 从宏观⻆度来看,Transformer的编码器是由多个相同的层叠加⽽ 成的,每个…...
大数的乘法
题目描述 求两个不超过100位的非负整数的乘积。 输入 有两行,每行是一个不超过100位的非负整数,没有多余的前导0。 输出 一行,相乘后的结果。 样例输入 Copy 123456789 123456789样例输出 Copy 15241578750190521 代码实现࿱…...
年度征文 | 机器学习之心的2023
机器学习之心的2023 2023是极其复杂的一年。 生活上,养了很多宠物。 工作上,写了不少博客。 虽然遇见更多让人不开心的事情,但总体还是美好的。 愿大家新的一年健康平安,生活幸福! 机器学习是一项庞大的工程࿰…...
13.Kubernetes应用部署完整流程:从Dockerfile到Ingress发布完整流程
本文以一个简单的Go应用Demo来演示Kubernetes应用部署的完整流程 1、Dockerfile多阶段构建 Dockerfile多阶段构建 [root@docker github]# git clone https://gitee.com/yxydde/http-dump.git [root@docker github]# cd http-dump/ [root@docker http-dump]# cat Dockerfile …...
多年后再用TB,谈项目管理工具
背景 最近启动一个小项目,多年未曾使用项目管理工具,依稀记得使用过Basecamp,Tower,worktitle,teambition等等,当然还有mantis,vs project等等。于是随便翻阅找个用,不小心翻了TB的牌子,竟然已是阿里旗下的…...
Spark MLlib ----- ALS算法
补充 在谈ALS(Alternating Least Squares)之前首先来谈谈LS,即最小二乘法。LS算法是ALS的基础,是一种数优化技术,也是一种常用的机器学习算法,他通过最小化误差平方和寻找数据的最佳匹配,利用最小二乘法寻找最优的未知数据,保证求的数据与已知的数据误差最小。LS也被用…...
ubuntu桥接方式上网
vmvare:VMware Workstation 17 Pro ubuntu: Ubuntu 14.04.6 LTS window10 下面是我的电脑配置 下面是ubuntu虚拟机的配置 vi /etc/network/interfaces 下面的gateway就是window -ipconfig 截图里的默认网关 auto lo iface lo inet loopbackauto eth0 iface eth0 inet stat…...
收到的字符串写入xml并且将这个xml写入.zip文件中
文章目录 1、将数据写入xml文件WriteToXmlFile2、将xml文件写入zip压缩文件AddToZip3、组合起来4、使用到的头文件和动态库 1、将数据写入xml文件WriteToXmlFile void CSMSLoginDlg::WriteToXmlFile(const std::string& responseData, const std::string& xmlFileName…...
【读书笔记】《白帽子讲web安全》跨站脚本攻击
目录 前言: 第二篇 客户端脚本安全 第3章 跨站脚本攻击(XSS) 3.1XSS简介 3.2XSS攻击进阶 3.2.1初探XSS Payload 3.2.2强大的XSS Payload 3.2.2.1 构造GET与POST请求 3.2.2.2XSS钓鱼 3.2.2.3识别用户浏览器 3.2.2.4识别用户安装的软…...
第九节 初始化项目
系列文章目录 目录 系列文章目录 前言 操作方法 总结 前言 初始化项目,导入默认reset.scss 、variables.scss及mixins.scss等并修改main.js引入样式 操作方法 将默认样式表文件导入到项目。样式文件已经放到资源里请自行下载(...
【论文阅读】深度学习中的后门攻击综述
深度学习中的后门攻击综述 1.深度学习模型三种攻击范式1.1.对抗样本攻击1.2.数据投毒攻击1.3.后门攻击 2.后门攻击特点3.常用术语和标记4.常用评估指标5.攻击设置5.1.触发器5.1.1.触发器属性5.1.2.触发器类型5.1.3.攻击类型 5.2.目标类别5.3.训练方式 1.深度学习模型三种攻击范…...
Spring Boot中加@Async和不加@Async有什么区别?设置核心线程数、设置最大线程数、设置队列容量是什么意思?
在 Spring 中,Async 注解用于将方法标记为异步执行的方法。当使用 Async 注解时,该方法将在单独的线程中执行,而不会阻塞当前线程。这使得方法可以在后台执行,而不会影响主线程的执行。 在您提供的代码示例中,a1() 和…...
Vue_00001_CLI
初始化脚手架 初始化脚手架步骤: 第一步(仅第一次执行):全局安装vue/cli。 命令:npm install -g vue/cli 第二步:切换到要创建项目的目录,然后使用命令创建项目。 命令:vue creat…...
kubernetes ResourceQuotas Limits(资源配额)
开头语 写在前面:如有问题,以你为准, 目前24年应届生,各位大佬轻喷,部分资料与图片来自网络 内容较长,页面右上角目录方便跳转 简介 当多个用户或团队共享具有固定节点数目的集群时,人们会…...
比Jenkins轻量10倍!用Gitea Actions搭建内网自动化部署的完整踩坑记录
企业级内网CI/CD革命:Gitea Actions轻量化实战指南 在当今快节奏的软件开发环境中,持续集成与持续部署(CI/CD)已成为企业提升交付效率的关键。然而,传统解决方案如Jenkins往往伴随着沉重的资源消耗和复杂的配置流程,让许多中小团队…...
Qwen3.5-9B-AWQ-4bit部署教程:Docker容器内路径映射与模型加载权限配置
Qwen3.5-9B-AWQ-4bit部署教程:Docker容器内路径映射与模型加载权限配置 1. 引言 今天我们要探讨的是如何在Docker环境中部署Qwen3.5-9B-AWQ-4bit模型,这是一个支持图像理解的多模态模型。这个模型能够结合上传的图片与文字提示词,输出中文分…...
手把手教你用Ollama命令搭建个人AI助手:从拉取Llama 3到定制化部署
从零构建智能对话引擎:Ollama与Llama 3的深度实践指南 在人工智能技术日益普及的今天,拥有一个个性化的AI助手已成为许多开发者和技术爱好者的追求。不同于云端服务的黑箱操作,本地部署的AI模型能提供更高的隐私保护和定制自由度。本文将带你…...
MiniCPM-o-4.5-nvidia-FlagOS部署运维:使用Docker Compose管理多服务依赖
MiniCPM-o-4.5-nvidia-FlagOS部署运维:使用Docker Compose管理多服务依赖 你是不是也遇到过这种情况?想部署一个AI模型,发现它依赖一堆东西:模型服务本身、数据库、缓存、可能还有别的辅助工具。一个个手动去装、去配置、去启动&…...
为什么MedNeXt能超越Transformer?揭秘大卷积核在医学图像分割中的独特优势
MedNeXt如何用大卷积核重塑医学图像分割?技术优势全解析 当你在深夜的医院影像科,看着屏幕上模糊的CT扫描图,试图从那些灰度渐变中分辨出肿瘤边界时,是否会想过AI模型眼中的世界?医学图像分割——这个决定患者治疗方案…...
仅限前500位开发者获取:20年MCP协议老兵手写《Python服务器模板源码认知地图》PDF+可执行调试镜像
第一章:MCP协议核心原理与Python服务器模板设计哲学MCP(Model Control Protocol)是一种轻量级、面向模型交互的双向通信协议,专为AI代理系统与外部工具服务之间的结构化指令交换而设计。其核心在于以JSON-RPC 2.0为传输语义基础&a…...
Android USB串口通信终极指南:智能家居物联网项目实战
Android USB串口通信终极指南:智能家居物联网项目实战 【免费下载链接】usb-serial-for-android Android USB host serial driver library for CDC, FTDI, Arduino and other devices. 项目地址: https://gitcode.com/gh_mirrors/us/usb-serial-for-android …...
数据中台是什么?怎么搭建数据中台?
去年,一家零售企业的CEO找到我,说了一句让我印象很深的话: "我们公司有数据,但没有数据能力。"很多企业建数据中台,是为了管好数据。 但这个出发点,从一开始就错了。 数据中台的核心不是管理&…...
Display Driver Uninstaller(DDU):显卡驱动深度清理工具,解决游戏玩家与设计师的驱动残留难题
Display Driver Uninstaller(DDU):显卡驱动深度清理工具,解决游戏玩家与设计师的驱动残留难题 【免费下载链接】display-drivers-uninstaller Display Driver Uninstaller (DDU) a driver removal utility / cleaner utility 项…...
高效开源输入法词库转换实战指南:30+格式无缝互转技巧
高效开源输入法词库转换实战指南:30格式无缝互转技巧 【免费下载链接】imewlconverter ”深蓝词库转换“ 一款开源免费的输入法词库转换程序 项目地址: https://gitcode.com/gh_mirrors/im/imewlconverter 深蓝词库转换是一款功能强大的开源输入法词库转换工…...
