javascript代码阅读题
第一题
var f = function () {
var c = 'ccc'
return {
a: function () {
return c
},
b: function (d) {
c = d
}
}
}()
console.log(f.a()) //ccc
console.warn(f.c) //undefined
console.warn(f.b('www')) //undefined
console.warn(f.a()) //www
输出分析:
题中 f 是一个立即执行函数表达式(IIFE)。返回一个对象,其中包含两个方法 a和 b 。f 中还有一个局部变量 c。
- 在第一次调用
console.log(f.a())中,a方法被调用并返回c的值'ccc'。因此,它会输出'ccc'。 - 在第二次调用
console.warn(f.c)中,由于c是在f的作用域内定义的局部变量,而不是作为对象的属性,因此无法通过f.c来访问它。因此,它会输出undefined。 - 在第三次调用
console.warn(f.b('www'))中,b方法被调用,并将局部变量c的值更新为'www'。但是,b方法没有返回值,所以输出结果为undefined。 - 最后一次调用
console.warn(f.a())中,再次调用a方法并返回c的值。由于在前一步中已经将c的值更新为'www',因此它会输出'www'。
第二题
var a = 1;
(function () {
console.log(a);
var a = 2;
console.log(a);
})()
输出分析
在这个代码片段中,我们定义了一个全局变量 a 并赋值为 1。然后我们创建了一个立即执行函数,并在函数内部打印变量 a 的值,接着声明一个新的局部变量 a 并赋值为 2,并再次打印变量 a 的值。
- 由于 JavaScript 的变量提升机制,函数内部的变量声明会被提升到函数的顶部,在使用之前已经被分配了内存空间,但还没有被赋值。所以,第一个
console.log(a)会输出undefined - 接着变量
a被赋值2,所以,第二个console.log(a)会输出2。
上述代码相当于以下代码:
var a = 1;
(function () {
var a; // 声明一个局部变量 a
console.log(a); // 输出 undefined
a = 2; // 变量赋值
console.log(a); // 输出 2
})();
第三题
var a = {}
var b = {key: 'a'}
var c = {key: 'c'}
a[b] = '123'
a[c] = '456'
console.log(a[b]) //456
输出分析
在这个代码片段中,我们定义了一个空对象 a,一个具有属性 key 值为 'a' 的对象 b,和一个具有属性 key 值为 'c' 的对象 c。
接着,我们使用对象 b 作为属性键,给对象 a 赋值为 '123'。然后,我们又使用对象 c 作为属性键,给对象 a 赋值为 '456'。
首先,我们尝试输出 a[b] 的值。根据 JavaScript 的对象属性访问规则,对象的属性键实际上是以字符串形式存储的,而不论属性键是什么类型都会被强制类型转换。因此,当我们使用对象 b 作为属性键时,它会被转换为字符串 '[object Object]'。同样地,当我们使用对象 c 作为属性键时,它也会被转换为字符串 '[object Object]'。
- 所以,
a[b]实际上相当于a['[object Object]'],而我们之前给a赋值的最后一次操作是a[c] = '456'。因此,最终输出的结果是'456'。
第四题
console.log('script start')
async function async1() {
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2 end')
}
async1()
setTimeout(function () {
console.log('setTimeout')
}, 0)
new Promise(resolve => {
console.log('Promise')
resolve()
})
.then(function () {
console.log('promise1')
})
.then(function () {
console.log('promise2')
})
console.log('script end')
输出分析
这题考了 JavaScript 中的异步执行和事件循环机制。
- 首先,代码从顶部开始执行,打印出
'script start'。 - 接着,调用了
async1()函数,它是一个异步函数。在async1()函数内部,遇到了await async2(),它会暂停函数的执行并等待async2()函数完成。然后,会打印出'async2 end',await等待asyncs()执行结果,会阻塞下面的代码执行,并将下面那行console.log(‘async1 end’)放到微任务中。 - 接下来,遇到了
setTimeout,它是一个异步定时器函数,0秒后它的回调函数会放到异步队列中,作为一个宏任务。 - 紧接着,遇到了
new Promise,它是一个异步操作,会立即执行传入的回调函数。在这里,回调函数中的代码会打印出'Promise',然后调用resolve()完成 Promise。 - 接着,通过
.then()方法添加了两个异步的回调函数。这些回调函数会在 Promise 状态改变为 resolved 后执行。因为前面的 Promise 已经通过调用resolve()改变了状态,所以这两个回调函数会被添加到微任务队列中,等待当前事件循环结束后执行。 - 下一步,打印出
'script end'。 - 同步任务结束,访问异步回调队列中的微任务,依次打印
'async1 end','promise1','promise2'。 - 再执行宏任务,输出
'setTimeout'。
总结执行结果为:
script start
async2 end
Promise
script end
async1 end
promise1
promise2
setTimeout
第五题
new Promise(resolve => {
console.log('p1')
resolve()
console.log('p3')
}).then(() => {
console.log('p2')
})
输出分析
首先创建了一个 Promise 对象,并传入一个执行器函数。在执行器函数内部,先打印了 'p1',然后调用了 resolve() 方法来触发 Promise 的解决态,并继续打印了 'p3'。
接下来使用 .then() 方法注册了一个回调函数,在 Promise 进入解决态后,该回调函数会被添加到微任务队列中,等待当前宏任务结束后执行。
因此,代码的执行顺序如下:
- 打印
'p1' - 调用
resolve() - 打印
'p3' - 当前宏任务结束,开始执行微任务队列
- 执行
.then()注册的回调函数,打印'p2'
所以最终的输出结果是:
p1
p3
p2
执行器函数是 Promise 对象的一个参数,它会在 Promise 被创建时立即执行。执行器函数接受两个参数,分别是 resolve 和 reject,它们是由 JavaScript 引擎自动传入的两个函数。
在执行器函数中,我们可以执行异步操作,比如发送网络请求、读取文件等。当异步操作完成时,我们可以调用 resolve(value) 来将 Promise 置于解决态,并传递一个值作为解决的结果;或者调用 reject(reason) 来将 Promise 置于拒绝态,并传递一个原因(通常是一个 Error 对象)。
执行器函数的作用是初始化 Promise 的状态,并定义异步操作的逻辑。根据异步操作的结果,我们可以决定是将 Promise 解决(resolved)为成功态,还是拒绝(rejected)为失败态。这样,后续就可以通过 .then() 方法或 .catch() 方法来处理 Promise 的结果或错误。
本文是一组JavaScript代码阅读题目解析,涉及IIFE、变量提升、对象属性、异步执行和Promise。第一题讨论了IIFE及其作用域,第二题解释了JavaScript的变量提升,第三题分析了对象属性的字符串键,第四题阐述了异步执行和事件循环机制,第五题介绍了Promise的执行顺序。
2890

被折叠的 条评论
为什么被折叠?



