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 的结果或错误。