惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Security Archives - TechRepublic
Security Archives - TechRepublic
T
The Exploit Database - CXSecurity.com
P
Proofpoint News Feed
Scott Helme
Scott Helme
NISL@THU
NISL@THU
Cisco Talos Blog
Cisco Talos Blog
C
Cybersecurity and Infrastructure Security Agency CISA
AWS News Blog
AWS News Blog
V
Vulnerabilities – Threatpost
J
Java Code Geeks
U
Unit 42
The GitHub Blog
The GitHub Blog
H
Help Net Security
T
Tenable Blog
aimingoo的专栏
aimingoo的专栏
Jina AI
Jina AI
Spread Privacy
Spread Privacy
Apple Machine Learning Research
Apple Machine Learning Research
人人都是产品经理
人人都是产品经理
L
Lohrmann on Cybersecurity
T
Threatpost
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Engineering at Meta
Engineering at Meta
A
About on SuperTechFans
I
InfoQ
Microsoft Azure Blog
Microsoft Azure Blog
B
Blog
L
LINUX DO - 最新话题
K
Kaspersky official blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
T
Threat Research - Cisco Blogs
C
Check Point Blog
T
The Blog of Author Tim Ferriss
有赞技术团队
有赞技术团队
宝玉的分享
宝玉的分享
Help Net Security
Help Net Security
Google DeepMind News
Google DeepMind News
A
Arctic Wolf
Y
Y Combinator Blog
N
News | PayPal Newsroom
M
MIT News - Artificial intelligence
Latest news
Latest news
H
Hacker News: Front Page
Blog — PlanetScale
Blog — PlanetScale
腾讯CDC
I
Intezer
爱范儿
爱范儿
F
Fortinet All Blogs
P
Palo Alto Networks Blog
C
CERT Recently Published Vulnerability Notes

szhshp 的第三边境研究所

假设, AI 把我代替的那一刻真的到来 | szhshp 的第三边境研究所 小傻瓜都能懂的 AstrBot QQ 机器人集成 MCP 功能实战指南 | szhshp 的第三边境研究所 《智人之上: 从石器时代到 AI 时代的信息网络简史》阅读笔记 | szhshp 的第三边境研究所 iPadOS 26 无法设置空间场景图片壁纸的解决方法 | szhshp 的第三边境研究所 《一路云海》(终) | szhshp 的第三边境研究所 《一路云海》(四): 如何不按套路旅行 | szhshp 的第三边境研究所 《一路云海》(三): In Ya Mellow Tone | szhshp 的第三边境研究所 《一路云海》(二): 关西世博参观纪实 | szhshp 的第三边境研究所 《一路云海》(一): 新的征程 | szhshp 的第三边境研究所 2025 大阪世博会 [ 3 天前-先到先得 ] 阶段 场馆预约必中独家攻略 | szhshp 的第三边境研究所 Hackathon 随想 | szhshp 的第三边境研究所 一杯双皮奶 | szhshp 的第三边境研究所 Armbian + CasaOS + NAS 配置指南 | szhshp 的第三边境研究所 Docker 构建镜像报错: error getting credentials - err: exit status 1, out: `` | szhshp 的第三边境研究所 Disqus RIP! 论过高的维护成本如何治疗固执的坏习惯 | szhshp 的第三边境研究所 炸弹猫桌游变体规则 | szhshp 的第三边境研究所 《小岛经济学》阅读笔记 | szhshp 的第三边境研究所 《金钱心理学》阅读笔记 | szhshp 的第三边境研究所 为知笔记 RIP: 迁移剩余的笔记 | szhshp 的第三边境研究所 2025 博客第十年展望 - 再见我的过去 | szhshp 的第三边境研究所 我在独立游戏里面致敬的作品 | szhshp 的第三边境研究所 《How to make thing faster》阅读笔记 | szhshp 的第三边境研究所 《The Art of Clean Code》阅读笔记 | szhshp 的第三边境研究所 《Clean Architecture: A Craftsman Guide to Software Structure and Design》阅读笔记 | szhshp 的第三边境研究所 《How AI Works》阅读笔记 | szhshp 的第三边境研究所 游戏策划废案 - Project Uranus | szhshp 的第三边境研究所 游戏策划废案 - Project X | szhshp 的第三边境研究所 人生第一款独立游戏开发复盘 | szhshp 的第三边境研究所 Trap of Life | szhshp 的第三边境研究所 如果我用手搓了个暗物质雏形 | szhshp 的第三边境研究所 《Rust Programming Language》- EX - Cheatsheet | szhshp 的第三边境研究所 新的播放器上线了 | szhshp 的第三边境研究所 Cypress - End-to-End Testing: OKTA Authentication with MFA | szhshp 的第三边境研究所 VSC x VIM - 反正多学几个快捷键没有坏处 | szhshp 的第三边境研究所 两小时 Docker 速成 - Getting Started | szhshp 的第三边境研究所 《The Joy of Javascript》- 5 - Data | szhshp 的第三边境研究所 《The Joy of Javascript》- 4 - Meta Programming | szhshp 的第三边境研究所 《The Joy of Javascript》- 3 - ADT(Algebraic Data Type) | szhshp 的第三边境研究所 Web-Automation Testing | szhshp 的第三边境研究所 《The Joy of Javascript》- 2 - Functor/Monad | szhshp 的第三边境研究所 《The Joy of Javascript》- 1 - Object/Function | szhshp 的第三边境研究所 EvalError: Possible side-effect in debug-evaluate | szhshp 的第三边境研究所 前端 QA 工具链指南 - husky,commitlint,commitizen,eslint,typescript,express | szhshp 的第三边境研究所 效率工具 & 软件推荐 | szhshp 的第三边境研究所 站点开源 Project Titan | szhshp 的第三边境研究所 cmd/powershell-命令速查-Cheatsheet | szhshp 的第三边境研究所 Git-命令速查与相关问题解决-Cheatsheet & Troubleshooting | szhshp 的第三边境研究所 Tailwind.css not included in NextJS Production | szhshp 的第三边境研究所 Github GraphQL API - Data Integration | szhshp 的第三边境研究所 尝试 Windows Terminal + z.lua | szhshp 的第三边境研究所 StrokePlus.net - Powerful Hotkey Boost | szhshp 的第三边境研究所 优雅地 Hack 一台黑苹果 | szhshp 的第三边境研究所 一杯关东煮 | szhshp 的第三边境研究所 Gitee x PicGo 管理图床 | szhshp 的第三边境研究所 Create Your Own Redux (Typescript) | szhshp 的第三边境研究所 Getting Started: Ionic + Capacitor + React + Typescript + Android | szhshp 的第三边境研究所 GraphQL: File Upload & Troubleshooting | szhshp 的第三边境研究所 吉祥航空随心飞刷票工具循环查票工具 | szhshp 的第三边境研究所 NVM Windows Portable Installation | szhshp 的第三边境研究所 NextJS x Typescript - Integration & Troubleshooting | szhshp 的第三边境研究所 全站点升级 - Project Titan | szhshp 的第三边境研究所 AWS-EC2-常见问题解决-Troubleshooting | szhshp 的第三边境研究所 Workbox-Getting Started | szhshp 的第三边境研究所 《时间简史》阅读笔记 | szhshp 的第三边境研究所 《时间的秩序》阅读笔记 | szhshp 的第三边境研究所 ESLint-常见问题解决-Troubleshooting | szhshp 的第三边境研究所 站点索引 | szhshp 的第三边境研究所 个人知识库搭建 - Hexo 实现 Github/Coding 持续集成 | szhshp 的第三边境研究所 BMOB-常见问题解决-Troubleshooting | szhshp 的第三边境研究所 React + Typescript: 开启 HMR/Hot Loader | szhshp 的第三边境研究所 Yarn & NPM | szhshp 的第三边境研究所 ESLint: Typescript + React + TSX 集成 | szhshp 的第三边境研究所 《10101》EP15:过年 2020 | szhshp 的第三边境研究所 Cmder-Usage | szhshp 的第三边境研究所 Redux-DOM not refreshed | szhshp 的第三边境研究所 React Semantic UI-CheatSheet | szhshp 的第三边境研究所 Typescript: Getting Started | szhshp 的第三边境研究所 Flutter-Getting Started | szhshp 的第三边境研究所 Typescript: Access window object with type check | szhshp 的第三边境研究所 VSC Extension Development-Create A Code Formatter Extension | szhshp 的第三边境研究所 OneNote Extension-Main API | szhshp 的第三边境研究所 TortoiseGit Win10 Icon not showing correctly | szhshp 的第三边境研究所 Mongoose: aggregate() 方法实现聚合函数 | szhshp 的第三边境研究所 React In Depth - React 源码解析 - render() & Fiber | szhshp 的第三边境研究所 MongoDB x Mongoose: 实现类似 Join 的功能 | szhshp 的第三边境研究所 Disqus 评论框改造工程-实现 Disqus 代理的 Serverless 服务 | szhshp 的第三边境研究所 GUI, CLI, Terminal, Shell, TTY, Console, Bash | szhshp 的第三边境研究所 Jest Async Best Practise: No Nested Async | szhshp 的第三边境研究所 关系型数据库与非关系型数据库优缺点分析 | szhshp 的第三边境研究所 New Edge & Chromium Error - Package is invalid: 'CRX_VERSION_NUMBER_INVALID' | szhshp 的第三边境研究所 选择 GraphQL 的 N 个理由 | szhshp 的第三边境研究所 StrokePlus - 鼠标手势神器的代码编写技巧 | szhshp 的第三边境研究所 嗨,好久不见 | szhshp 的第三边境研究所 Vue + ElementUI 集成 Vue Router | szhshp 的第三边境研究所 IE8 - Unknown Runtime Error Using innerHTML & Ajax | szhshp 的第三边境研究所 React: Handling Events | szhshp 的第三边境研究所 React-Navigation Integration with Redux | szhshp 的第三边境研究所 React: States is tricky | szhshp 的第三边境研究所 《10101》EP14:小剧场 | szhshp 的第三边境研究所 ColdFusion - Basic - arguments in cffunction | szhshp 的第三边境研究所
一道 Javascript 面试题引发的血案 | szhshp 的第三边境研究所
2017-04-03 · via szhshp 的第三边境研究所

Meta

目录

为了面试新人我们公司准备了一些面试题, 几个同事都尝试做了一下, 结果大片玩家 HP-100000

覆盖的考点不多, 题目挺有意思, 建议手动执行一边玩玩.

Question 1

for (var i = 0; i < 5; i++) {
  setTimeout(function () {
    console.log(i);
  }, 1000);
}
console.log(i);
  • Q: 这道题目会输出什么?
  • A: 这道题目还比较简单, 如果对 Javascript 稍微有一点深入的同学都会发现这道题目循环里面出现了闭包, 因此输出的数字是完全相同的, 最后的输出也是完全相同的.
  • 考点: 闭包, (伪) 异步

Question 2

for (let i = 0; i < 5; i++) {
  //注意 var 变成了 let
  setTimeout(function () {
    console.log(i);
  }, 1000);
}
console.log(i);
  • Q: 这道题目会输出什么?

  • A: 这道题目其实是个坑. 首先题目与 Q1 的区别就是变量 i 的定义改为了关键字 let, 使用 let 的时候会将变量限制在循环之中, 因此第二个输出其实会报错. 另外 setTimeout 实现了 (伪) 异步, 同时因为 let 将变量作用域进行了控制, 破坏了闭包结构, 因此会按照正常顺序输出.

    关于 let 关键字 1

    Use the let statement to declare a variable, the scope of which is restricted to the block in which it is declared. You can assign values to the variables when you declare them or later in your script.
    A variable declared using let cannot be used before its declaration or an error will result..

  • 考点: 闭包, (伪) 异步, 作用域

Question 3

同样是 Q1 的代码

for (var i = 0; i < 5; i++) {//DO NOT MODIFY
  setTimeout(function () {//DO NOT MODIFY
    console.log(i);
  }, 1000);
}
console.log(i); //DO NOT MODIFY
  • Q: 修改上述代码 (部分行不允许修改, 可以在代码间插入) , 以实现”每隔一秒输出一个数字并且顺序为 0-5”

  • A

    1. 首先考到了破坏闭包结构, 破坏闭包的方法很多, 最简单的是将跨域变量转换成范围内的变量
    2. 其次考到了 setTimeout 事件队列的处理
    for (var i = 0; i <5 ; i++) {
        (function(i){
            setTimeout(function(){
                console.log(i)
            }, 1000*i)    
        })(i)           //将 i 作为参数传入匿名函数, 如此破坏了闭包内跨域访问
    }
    setTimeout(function (){
      console.log(i);
    }, 5000);               //强行将 5 放到 5sec 后输出
  • 考点: 闭包, (伪) 异步, 作用域, 事件队列

Question 4

window.setTimeout(function () {
  console.log(2);
}, 1);

//Ouput for a long time
for (var i = 0; i < 1000; i++) {
  console.log("");
}

console.log(1);

window.setTimeout(function () {
  console.log(3);
}, 0);
  • Q: 这道题目会输出什么?
  • A: 可能有些同学会记得, setTimeout 是一个回调函数, 因此无论延时多少结果都是最后输出.结果是 1, 2, 3
  • 考点: (伪) 异步, 事件队列

Question 5

这道题目其实是其他地方抄袭来的 2, 正好和之前考点有一定重叠因此一起放了过来:

setTimeout(function(){console.log(4)}, 0);
new Promise(function(resolve){
    console.log(1)

    //time consuming ops
    for( var i=0 ; i<10000 ; i++ ){
        i==9999 && resolve();
    }

    console.log(2)
}).then(function(){
    console.log(5)
});
console.log(3);
  • Q: 这道题目会输出什么?
  • A: 关于这个输出, 有如下几个逻辑: 2. 4 是 setTimeOut.callback 的输出, 加入 MacroTask 末端, 2. 输出 1 3. 执行 Promise.resolve() 将输出 5 的 callback 放到 MicroTask 中 (注意这里不是 MacroTask )
    1. 输出 2
    2. 输出 3
    3. MacroTask 首个任务执行完毕
    4. 查找 MicroTask 里面有没有任务, 发现有, 执行, 输出 5
    5. 查找 MacroTask 里面有没有任务, 发现有, 执行, 输出 4
    6. 查找 MicroTask 里面有没有任务, 发现没有, 可以休息了
    7. 查找 MacroTask 里面有没有任务, 发现没有, 可以睡觉了
    8. 执行完毕

总结 1: 闭包

闭包什么的不想写了

总结 2: 关于事件循环/关于 macrotaskmicrotask

简介

一个事件循环 (EventLoop) 中会有一个正在执行的任务 (Task), 而这个任务就是从 macrotask 队列中来的. 在 whatwg 规范中有 queue 就是任务队列. 当这个 macrotask 执行结束后所有可用的 microtask 将会在同一个事件循环中执行, 当这些 microtask 执行结束后还能继续添加 microtask 一直到真个 microtask 队列执行结束. 1

怎么用

基本来说, 当我们想以同步的方式来处理异步任务时候就用 microtask (比如我们需要直接在某段代码后就去执行某个任务, 就像 Promise 一样) .

其他情况就直接用 macrotask.

两者的具体实现

  • macrotasks: setTimeout setInterval setImmediate I/O UI 渲染
  • microtasks: Promise process.nextTick Object.observe MutationObserver

从规范中理解

规范: https://html.spec.whatwg.org/multipage/webappapis.html#task-queue

  • 一个事件循环 (event loop) 会有一个或多个任务队列 (task queue) task queue 就是 macrotask queue
  • 每一个 event loop 都有一个 microtask queue
  • task queue == macrotask queue != microtask queue
  • 一个任务 task 可以放入 macrotask queue 也可以放入 microtask queue
  • 当一个 task 被放入队列 queue(macro 或 micro) 那这个 task 就可以被立即执行了

再来回顾下事件循环如何执行一个任务的流程

当执行栈 (call stack) 为空的时候, 开始依次执行:

  1. 把最早的任务 (task A) 放入任务队列
  2. 如果 task A 为 null (那任务队列就是空), 直接跳到第 6 步
  3. 将 currently running task 设置为 task A
  4. 执行 task A (也就是执行回调函数)
  5. 将 currently running task 设置为 null 并移出 task A
  6. 执行 microtask 队列
    1. 在 microtask 中选出最早的任务 task X
    2. 如果 task X 为 null (那 microtask 队列就是空), 直接跳到 g
    3. 将 currently running task 设置为 task X
    4. 执行 task X
    5. 将 currently running task 设置为 null 并移出 task X
    6. 在 microtask 中选出最早的任务 , 跳到 b
    7. 结束 microtask 队列
  7. 跳到第一步

上面就算是一个简单的 event-loop 执行模型

再简单点可以总结为:

  1. 在 macrotask 队列中执行最早的那个 task, 然后移出
  2. 执行 microtask 队列中所有可用的任务, 然后移出
  3. 下一个循环, 执行下一个 macrotask 中的任务 (再跳到第 2 步)

其他

  1. 当一个 task(在 macrotask 队列中) 正处于执行状态, 也可能会有新的事件被注册, 那就会有新的 task 被创建. 比如下面两个
    1. promiseA.then() 的回调就是一个 task
    2. promiseAresolvedrejected: 那这个 task 就会放入当前事件循环回合的 microtask queue
    3. promiseApending: 这个 task 就会放入 事件循环的未来的某个 (可能下一个) 回合的 microtask queue
    4. setTimeout 的回调也是个 task , 它会被放入 macrotask queue 即使是 0ms 的情况
  2. microtask queue 中的 task 会在事件循环的当前回合中执行, 因此 macrotask queue 中的 task 就只能等到事件循环的下一个回合中执行了
  3. click ajax setTimeout 的回调是都是 task, 同时, 包裹在一个 script 标签中的 js 代码也是一个 task 确切说是 macrotask.
  1. let 语句 (JavaScript) ↩ ↩2

  2. https://www.zhihu.com/question/36972010