代码输出题
数据类型
instanceof
实现方式:原型链检测是否是某个构造函数的实例,在原型链的尽头是Object.prototype
,这是所有对象的原型。
1 | function myInstanceOf(obj,constructor){ |
当你使用 instanceof
操作符检测一个对象是否是某个构造函数的实例时,实际上是在沿着对象的原型链向上查找,直到找到构造函数的原型或者原型链的尽头。
举个例子:
1 | function Person() {} |
在这个示例中,person
对象的原型链是这样的:
1 | person --> Person.prototype --> Object.prototype --> null |
Person.prototype
是 person
对象的构造函数 Person
的原型,而 Object.prototype
是 Person.prototype
的原型,最终指向 null
,这就是原型链的尽头。
函数
防抖
节流
深拷贝
事件循环
111
1 | async function exampleAsync() { |
算法
快速排序
111
useEffect
你给出的代码是一个使用React Hooks的简单组件示例。这个组件展示了如何使用useState
和useEffect
来管理状态和副作用。下面,我会对这段代码进行解释,并翻译成中文注释。
组件功能解释:
useState使用:
useState(0)
创建一个状态变量count
和一个更新这个状态的函数setCount
。初始值为0
。
useEffect使用:
useEffect
是用来处理组件的副作用(如数据获取、订阅等)。这里的副作用是打印当前的count
值。- 每当
count
变化时,副作用会重新执行。首先执行副作用函数,然后在执行下一个副作用前,先执行清理函数。
渲染行为:
- 组件渲染一个
h3
元素,显示count
的值。 - 点击
h3
元素时,会通过setCount
函数更新count
值,增加1。由于count
是useEffect
的依赖项,这会导致组件重新渲染并触发副作用。
- 组件渲染一个
这个组件展示了React Hooks的基本用法,特别是状态管理和副作用的处理,使得组件逻辑更清晰和模块化。
1 | function App() { |
树结构输出父节点
打印value
1 | function printValuesBFS(root) { |
事件循环输出题
第1题
1 | const newPromise1 = new Promise((resolve, reject) => { |
执行流程
创建
newPromise1
:
-new Promise()
构造函数立即执行,输出A
。
-resolve('B')
被调用,将 Promise 的状态从 pending 转变为 fulfilled,并传递值'B'
作为res
的值。执行
newPromise1.then()
:newPromise1
是一个已完成(fulfilled)的 Promise,所以.then()
里的回调函数会在下一个微任务队列(microtask queue)执行,输出B
。
打印
newPromise1
和newPromise2
:console.log('C', newPromise1)
打印的是newPromise1
,此时它的状态已经是 fulfilled,值为'B'
。console.log('D', newPromise2)
打印的是newPromise2
,这是通过.then()
返回的 Promise。由于newPromise2
还在等待newPromise1.then()
的回调执行,它的状态暂时为 pending,然后会变为 fulfilled,但控制台会显示它当前的状态为 pending。
输出顺序:
'A'
是同步代码,立即输出。console.log('C', newPromise1)
输出C
和一个已完成的 Promise,值为'B'
。console.log('D', newPromise2)
输出D
和一个处于 pending 状态的 Promise。- 回调函数
console.log(res)
在事件循环的微任务队列执行,输出'B'
。
最终输出结果:
1 | A |
注意:B
是在微任务队列中执行的,所以会在所有同步代码(包括 console.log('C', newPromise1)
和 console.log('D', newPromise2)
) 之后输出。
第2题
1 | const newPromise = new Promise((resolve, reject) => { |
执行流程:
创建
newPromise
:- 当
new Promise()
被调用时,执行器函数立即执行,首先同步执行console.log('A')
,输出:1
A
- 然后
setTimeout()
被设置为 0 毫秒后异步执行。setTimeout
本身是异步的,所以它会将回调函数放入事件队列,并继续往下执行。 - 接着执行
console.log('B')
,输出:1
B
- 当
执行
newPromise.then()
:- 此时 Promise 还处于 pending 状态,因为
resolve()
尚未被调用。 newPromise.then()
中的回调函数被放入微任务队列,等待 Promise 变为 fulfilled 后执行。
- 此时 Promise 还处于 pending 状态,因为
执行
console.log('C')
:console.log('C')
是同步代码,立即执行,输出:1
C
处理
setTimeout
回调:- 当事件循环到达定时器回调时,
setTimeout()
的回调函数被执行,输出:1
timer start
- 调用
resolve("succeed")
,Promise 的状态变为 fulfilled,并且then()
回调准备执行。 - 接着输出
console.log("timer end")
:1
timer end
- 当事件循环到达定时器回调时,
执行
newPromise.then()
的回调:- 在事件循环的微任务队列中,
then()
回调会执行,输出resolve()
传递的值:1
succeed
- 在事件循环的微任务队列中,
输出顺序总结:
A
立即输出。B
立即输出。C
立即输出。- 定时器的回调被异步执行,输出
timer start
和timer end
。 - 最后,Promise 被
resolve
,输出succeed
。
最终输出结果:
1 | A |
第3题
1 | Promise.resolve().then(() => { |
执行流程:
1. 同步代码部分:
Promise.resolve()
会立即创建一个已完成的Promise
,并且将.then()
的回调放入 微任务队列。setTimeout()
将回调放入 宏任务队列,等待主线程的同步代码执行完成后执行。console.log('run')
是同步代码,立即执行。
2. 微任务队列执行顺序:
- 在同步代码执行完之后,事件循环首先会处理 微任务队列。
Promise.resolve().then(() => {...})
的回调会首先执行,因为它是放入 微任务队列 中的。这会输出outerPromise
。
3. 宏任务队列执行顺序:
- 微任务执行完成后,事件循环会开始处理 宏任务队列。
- 定时器回调(通过
setTimeout
注册的任务)会依次执行。 setTimeout(() => {...}, 0)
的回调函数console.log('outerTimer')
被执行。Promise.resolve().then(() => {...})
再次将其.then()
回调放入 微任务队列,输出innerPromise
。
4. 内层定时器的执行:
- 内层定时器
setTimeout(() => { console.log('innerTimer') }, 0)
也在 宏任务队列 中,它会在外层的宏任务执行完毕后执行。
输出顺序:
- 同步代码部分:首先执行同步代码,输出
run
。 - 微任务部分:在同步代码执行完毕后,执行微任务队列中的
Promise.resolve()
回调,输出outerPromise
。 - 外层定时器:然后执行
timer1
的回调,输出outerTimer
,紧接着再将内层Promise.resolve()
的.then()
放入微任务队列。 - 微任务部分:微任务队列中有
innerPromise
的输出,所以输出innerPromise
。 - 最后执行内层定时器:最后执行宏任务队列中内层定时器的回调,输出
innerTimer
。
最终输出顺序:
1 | run |
事件循环顺序回顾:
- 同步代码先执行(输出
run
)。 - 处理微任务队列(输出
outerPromise
)。 - 处理宏任务队列中的定时器(输出
outerTimer
和之后的innerPromise
)。 - 执行宏任务中的剩余定时器(输出
innerTimer
)。
第4题
在这段代码中,Promise
的行为可能会引发一些疑问。让我们逐步分析它的执行流程:
1 | const promise = new Promise((resolve, reject) => { |
执行流程解析:
Promise 构造函数的执行:
- 一旦
new Promise
被调用,传递的执行器函数立即开始执行。 - 第一个
resolve('succeed1')
调用了resolve
,这将把promise
的状态从pending
改变为 fulfilled,并将值设为'succeed1'
。一旦Promise
的状态变为fulfilled
或rejected
,它的状态就不可再改变。 reject('error')
和resolve('succeed2')
在resolve('succeed1')
之后被调用,但由于Promise
状态一旦从pending
转变为其他状态(无论是fulfilled
还是rejected
),后续的状态变更请求将被忽略。因此,这两个操作不会产生任何效果。
- 一旦
then
和catch
的执行:promise.then()
会被调用,因为promise
的状态已经是fulfilled
,并且值为'succeed1'
。.then()
的回调会打印出then: succeed1
。- 由于
Promise
已经是成功的状态,.catch()
不会被触发。
为什么 reject('error')
和 resolve('succeed2')
被忽略?
- 在
Promise
中,**一旦resolve
或reject
其中之一被调用,状态就锁定为fulfilled
或rejected
**。任何后续对resolve
或reject
的调用都会被忽略。因此,尽管reject('error')
和resolve('succeed2')
也被调用了,但它们都没有实际效果,因为Promise
状态在第一次resolve('succeed1')
调用后已经被锁定。
最终输出:
1 | then: succeed1 |
总结:
resolve
和reject
只会生效一次,后续的调用都会被忽略。- 代码中的
resolve('succeed1')
成功后,then
回调被执行,并输出'succeed1'
,后面的reject
和第二个resolve
调用无效。 catch
永远不会被触发,因为Promise
的状态是成功的(fulfilled)。
第5题
在这段代码中,Promise
的 .then()
方法看起来有些特殊,因为你在 .then()
中直接传递了非函数值。那么,这会导致什么样的行为?让我们逐步分析:
1 | Promise.resolve('A') |
关键点:
Promise.then()
的参数必须是一个函数:- 如果
.then()
中传递的不是一个函数(例如'B'
或Promise.resolve('C')
),它会被忽略,默认传递原始值下去,等效于:1
.then(value => value)
- 这意味着非函数值
B
和Promise.resolve('C')
会被视为普通值,并且没有特殊处理。
- 如果
执行流程分析:
**
Promise.resolve('A')
**:- 这行代码会立即返回一个已完成状态(
fulfilled
)的Promise
,值为'A'
。
- 这行代码会立即返回一个已完成状态(
**
then('B')
**:- 由于
'B'
不是一个函数,它会被忽略。.then()
会将前一个Promise
的值'A'
直接传递到下一个.then()
。
- 由于
**
then(Promise.resolve('C'))
**:Promise.resolve('C')
是一个已完成的Promise
,但是这里.then()
接受的是值而不是函数。由于Promise.resolve('C')
本身是一个对象(而不是函数),所以它也会被忽略。then()
还是会把上一个Promise
的值'A'
传递到下一个.then()
。
**
then(console.log)
**:- 最终,这里会执行
console.log('A')
,因为值'A'
被传递了下来。
- 最终,这里会执行
最终输出:
1 | A |
关键总结:
then()
中传递的非函数参数会被忽略。这意味着'B'
和Promise.resolve('C')
都不会影响结果。.then()
只是简单地传递上一个Promise
的结果,最终输出A
。
第6题
让我们一步步分析这段代码的执行流程,重点是 Promise 的状态变化以及控制台输出的内容。
1 | const promise1 = new Promise((resolve, reject) => { |
分析步骤:
**创建
promise1
**:promise1
是一个新的Promise
,并且由于setTimeout
还没有触发,promise1
在创建时处于 pending(待定)状态。
**创建
promise2
**:promise2
是由promise1.then()
创建的新的Promise
。此时,promise1
仍然处于 pending 状态,并且promise2
也同样处于 pending 状态。.then()
的回调还没有执行,因为promise1
还没有resolve
。
**第一次输出
promise1
和promise2
**:- 在这一步,
promise1
和promise2
都还处于 pending 状态,因为setTimeout
的延迟还没有达到,因此控制台会输出:
1
2promise1 Promise { <pending> }
promise2 Promise { <pending> }- 在这一步,
**
promise1
在 1 秒后变为fulfilled
**:- 在 1 秒之后,
setTimeout
回调触发,resolve('success')
被调用,因此promise1
的状态会变为 fulfilled,值为'success'
。 - 但是,
promise1.then()
的回调会抛出错误Error('error')
,因此promise2
的状态会变为 rejected,并且值是这个错误。
- 在 1 秒之后,
**第二次输出
promise1
和promise2
**(在 2 秒后):- 当第二个
setTimeout
回调(2000 毫秒)执行时:promise1
已经变为 fulfilled,值为'success'
。promise2
则因为.then()
抛出了错误,状态变为 rejected,值是这个错误。
因此,在第二次输出中,控制台会显示:
1
2innerPromise1 Promise { 'success' }
innerPromise2 Promise { <rejected> Error: error }- 当第二个
详细的输出顺序:
在最初输出时(在
setTimeout
触发之前):1
2promise1 Promise { <pending> }
promise2 Promise { <pending> }在第二次输出时(在 2 秒后):
1
2innerPromise1 Promise { 'success' }
innerPromise2 Promise { <rejected> Error: error }
总结:
promise1
在 1 秒后会变为 fulfilled,值为'success'
。promise2
会在promise1.then()
中因为抛出了错误,变为 rejected,并且这个错误会成为它的值。- 初次输出时,两个 Promise 都是 pending 状态。
- 1 秒后,
promise1
变为 fulfilled,promise2
变为 rejected。
第7题
让我们逐步分析这段代码的执行流程和最终输出:
1 | Promise.resolve('A') |
分析步骤:
Promise.resolve('A')
:- 这个创建了一个已完成的
Promise
,值为'A'
。
- 这个创建了一个已完成的
第一个
.then(res => {...})
:Promise.resolve('A')
状态是已完成(fulfilled
),所以会调用第一个.then()
中的回调函数。res
的值是'A'
,因此输出:1
A
- 回调函数的返回值是
'B'
,这个值将传递给下一个.then()
,因此接下来链中的Promise
也会以值'B'
变为fulfilled
。
.catch(err => {...})
:.catch()
只会在前面的Promise
被拒绝(rejected
)时执行。- 因为前一个
.then()
成功并返回了'B'
,所以.catch()
不会被执行。
第二个
.then(res => {...})
:- 由于
.catch()
没有被执行,前一个Promise
的值'B'
会传递到第二个.then()
中。 - 因此,这里
res
的值是'B'
,输出:1
B
- 由于
最终输出顺序:
1 | A |
总结:
- 第一个
.then()
接收到'A'
,输出'A'
并返回'B'
。 .catch()
不会被触发,因为前面的操作没有抛出错误。- 第二个
.then()
接收到'B'
,输出'B'
。
最终结果是:A
和 B
。
8
1 | Promise.resolve().then(() => { |
关键点:
- 返回
Error
对象:在Promise
的then()
回调中,返回一个普通的值(无论是对象、字符串,还是其他类型),Promise
会把它当作一个正常的返回值,而不是抛出异常。 - 区别于抛出异常:如果你是抛出一个
Error
(通过throw
),那么Promise
会进入 rejected 状态;但如果你只是返回Error
对象,它被视为一个普通的值,不会进入catch
。
执行流程分析:
**
Promise.resolve()
**:- 这行代码立即创建并返回一个已完成(
fulfilled
)的Promise
,没有值。
- 这行代码立即创建并返回一个已完成(
**第一个
.then()
**:- 由于
Promise.resolve()
的状态是fulfilled
,它会执行第一个.then()
的回调。 - 该回调返回了一个
Error
对象,但这只是一个普通的返回值,不会被视为异常抛出,所以它被当作普通值传递给下一个.then()
。
- 由于
**第二个
.then()
**:- 上一个
.then()
返回的Error
对象被传递到这里,res
的值就是这个Error
对象。 - 因此,控制台会输出:
1
then: Error: error
- 上一个
**
.catch()
**:- 因为没有任何
Promise
进入 rejected 状态,.catch()
不会被触发,因此不会执行。
- 因为没有任何
最终输出:
1 | then: Error: error |
总结:
- 在
Promise
中返回Error
对象不会导致Promise
进入 rejected 状态。 - 返回的
Error
对象会像普通值一样被传递到下一个.then()
,因此输出"then: Error: error"
。 .catch()
不会被触发,因为没有异常被抛出。
9
在这段代码中,Promise
的执行逻辑涉及到一些特殊的循环引用场景,让我们分析它的行为和结果:
1 | const promise = Promise.resolve().then(() => { |
代码解析:
const promise = Promise.resolve().then(() => { return promise; })
:- 首先,
Promise.resolve()
立即返回一个已完成(fulfilled
)的Promise
,并执行接下来的.then()
方法。 - 在
.then()
的回调中,代码试图返回promise
自己。这会导致一个循环引用的问题。
- 首先,
循环引用的后果:
- 在 JavaScript 中,
Promise
的then()
不能返回其自身。这是因为返回promise
自己会导致一种“无法解决的递归”:Promise
一直在等待自己被解决,从而永远无法完成。 - 为防止这种循环依赖,JavaScript 规范明确规定,如果
Promise
的.then()
返回了其自身,会导致 TypeError,并使该Promise
进入 rejected 状态。
- 在 JavaScript 中,
promise.catch(console.err)
:- 因为
.then()
中返回了promise
自身,promise
会进入 rejected 状态,理由是Promise
不能等待自身完成。 - 但是在代码中,
catch
方法的回调是console.err
,这实际上是一个错误的引用,console
的方法应该是console.error
。 - 如果
console.err
没有被定义,会抛出一个TypeError
,导致catch()
回调本身出错。
- 因为
执行过程的结果:
- 在
.then()
中返回promise
自己时,Promise
会进入 rejected 状态并抛出一个TypeError
。 - 因为
catch(console.err)
中的console.err
不是一个有效的方法名,执行时会报错,这本身是一个错误。
正确的修复方式:
- 使用
console.error
以正确地捕捉和打印错误。
1 | const promise = Promise.resolve().then(() => { |
输出:
- 代码中的
.then()
会导致promise
进入 rejected 状态,并抛出一个TypeError
,这会被catch()
捕获。 - 使用正确的
console.error
,输出会是类似这样的信息:1
TypeError: Chaining cycle detected for promise #<Promise>
结论:
- 在
Promise
的.then()
中返回自身会导致递归循环,进而使Promise
进入 rejected 状态。 - 应该使用
console.error
而不是console.err
,以正确地捕获并输出错误消息。
10
让我们再一次分析这段代码的执行流程,重点关注 .then()
中非函数值的处理。
1 | Promise.resolve('A') |
关键点:
then()
的参数必须是一个函数:.then()
方法的参数通常是一个回调函数。如果传递的不是函数,比如一个值(如'B'
或Promise.resolve('C')
),它会被忽略,并且.then()
会默认传递前一个Promise
的结果。
执行流程分析:
**
Promise.resolve('A')
**:- 这行代码立即创建一个
fulfilled
状态的Promise
,值为'A'
。
- 这行代码立即创建一个
**
then('B')
**:- 因为
'B'
不是函数,then()
会忽略这个值,并将前一个Promise
的结果'A'
直接传递到下一个then()
。
- 因为
**
then(Promise.resolve('C'))
**:Promise.resolve('C')
是一个已经被解析的Promise
,但由于它被传递给了then()
,且then()
期待的是函数,Promise.resolve('C')
会被忽略,仍然将之前的值'A'
传递下去。
**
then(console.log)
**:- 这里
console.log
是一个有效的函数,因此then()
会调用它,并打印出之前传递下来的值'A'
。
- 这里
关键结论:
.then()
如果接收到的是一个非函数值,它会被忽略,默认继续传递之前的结果。- 所以最终,
console.log
打印的值是'A'
。
最终输出:
1 | A |
总结:
- 非函数值传递给
.then()
会被忽略。 - 尽管有
'B'
和Promise.resolve('C')
传递给.then()
,它们都没有影响结果,最终仍然是'A'
被传递下来并输出。
11
让我们再一次分析这段代码的执行流程,重点关注 .then()
中非函数值的处理。
1 | Promise.resolve('A') |
关键点:
then()
的参数必须是一个函数:.then()
方法的参数通常是一个回调函数。如果传递的不是函数,比如一个值(如'B'
或Promise.resolve('C')
),它会被忽略,并且.then()
会默认传递前一个Promise
的结果。
执行流程分析:
**
Promise.resolve('A')
**:- 这行代码立即创建一个
fulfilled
状态的Promise
,值为'A'
。
- 这行代码立即创建一个
**
then('B')
**:- 因为
'B'
不是函数,then()
会忽略这个值,并将前一个Promise
的结果'A'
直接传递到下一个then()
。
- 因为
**
then(Promise.resolve('C'))
**:Promise.resolve('C')
是一个已经被解析的Promise
,但由于它被传递给了then()
,且then()
期待的是函数,Promise.resolve('C')
会被忽略,仍然将之前的值'A'
传递下去。
**
then(console.log)
**:- 这里
console.log
是一个有效的函数,因此then()
会调用它,并打印出之前传递下来的值'A'
。
- 这里
关键结论:
.then()
如果接收到的是一个非函数值,它会被忽略,默认继续传递之前的结果。- 所以最终,
console.log
打印的值是'A'
。
最终输出:
1 | A |
总结:
- 非函数值传递给
.then()
会被忽略。 - 尽管有
'B'
和Promise.resolve('C')
传递给.then()
,它们都没有影响结果,最终仍然是'A'
被传递下来并输出。
12
让我们详细分析这段代码的执行流程,并明确每个步骤的输出。
1 | Promise.resolve('A') |
执行流程解析:
第一部分:
1 | Promise.resolve('A') |
Promise.resolve('A')
:- 这个语句会立即返回一个
fulfilled
状态的Promise
,值为'A'
。
- 这个语句会立即返回一个
第一个
.then()
:- 因为
Promise.resolve('A')
是成功的,所以第一个.then()
的回调函数会被执行。 res
的值为'A'
,因此会输出:1
promise1 A
- 因为
第一个
.finally()
:.finally()
总会执行,无论Promise
是成功还是失败。- 所以接下来会执行
finally
回调,输出:1
finally1
第二部分:
1 | Promise.resolve('B') |
Promise.resolve('B')
:- 类似于第一部分,这行代码立即返回一个已完成的
Promise
,值为'B'
。
- 类似于第一部分,这行代码立即返回一个已完成的
第二个
.finally()
:- 虽然
.finally()
回调返回了'result'
,但它的返回值不会影响Promise
链的继续,也不会改变Promise
的值。 - 所以会执行
finally2
回调,输出:1
finally2
- 虽然
第二个
.then()
:.then()
依旧会接收来自Promise.resolve('B')
的值,即'B'
,而不是'finally'
返回的'result'
。- 因此,输出:
1
promise2 B
最终输出顺序:
promise1 A
finally1
finally2
promise2 B
总结:
.finally()
的回调函数不改变Promise
链中传递的值。即使在finally2
中返回了'result'
,它不会影响后续.then()
中的值。finally
回调总是执行的,无论Promise
成功还是失败。
13
让我们逐步分析这段代码,特别是 Promise.all()
的行为和异步函数的执行顺序。
1 | function runAsync(num) { |
代码的执行流程:
1. runAsync
函数:
runAsync
是一个返回Promise
的函数,它的核心逻辑是:- 创建一个
Promise
,并在setTimeout
1秒后调用resolve
。 resolve(num)
被调用时,还会立即执行console.log(num)
,在输出num
的同时,Promise
被解决(fulfilled
)。
- 创建一个
2. **Promise.all()
**:
Promise.all([runAsync(1), runAsync(2), runAsync(3)])
同时启动了 3 个runAsync
函数,这些函数在 1 秒后几乎同时解决(fulfilled
)。Promise.all()
会等待所有Promise
都解决,返回一个包含每个Promise
解决值的数组,按传入的顺序排列。- 如果所有
Promise
成功,Promise.all()
返回一个fulfilled
的Promise
,并将每个Promise
的返回值作为数组传递给.then()
。
执行顺序:
**
runAsync(1)
、runAsync(2)
、runAsync(3)
**:- 这三个函数几乎同时开始执行,它们各自启动一个 1 秒的计时器。
- 计时器结束后,按照调用顺序,依次执行
console.log(num)
并解决各自的Promise
。 - 虽然
setTimeout
函数调用几乎同时开始,但console.log
的输出顺序依赖于runAsync
的参数。由于所有Promise
的setTimeout
时间相同,因此它们的resolve
操作会在 1 秒后几乎同时执行。
**
Promise.all()
**:- 等到所有
runAsync
函数完成后,Promise.all()
将这些Promise
的结果按传入的顺序组合成一个数组,并传递给.then()
。
- 等到所有
执行结果分析:
console.log(num)
会在每个Promise
被解决前调用,分别输出1
,2
,3
。Promise.all()
将等待所有Promise
解决,并按传入顺序返回[1, 2, 3]
给.then()
。
最终输出顺序:
在 1 秒后,分别输出:
1
2
31
2
3Promise.all()
解决后,输出:1
[1, 2, 3]
代码的最终输出:
1 | 1 |
总结:
Promise.all()
保证返回的数组结果与传入的Promise
顺序一致。- 即使
console.log
在每个Promise
内部被调用,Promise.all()
仍会按顺序返回每个Promise
的结果。