闲来无事,小记一下前段时间面试遇到的一些问题。需要说明的是,这些问题并不是一开始直接提问的,而是由之前的提问引申出来的。比如第一问题是在事件循环 中引申出来的,第二个问题是在数据结构中引申出来的。
如何模拟一个微任务
微任务
首先简单了解一下什么是微任务?先谈宏任务,即不同类型的消息队列里的任务。微任务相对于宏任务,每个宏任务都关联一个微任务队列,在宏任务即将结束前都会清空微任务队列,执行微任务产生的新的微任务依然放在当前队列而不会延期到下一个宏任务。两者相辅相成,微任务的设计主要是在执行效率和时机上做一个权衡,在WebAPI中产生微任务的常见方式为:Promise与MutationObserver。
MutationObserver为什么被设计成微任务
使用Promise可以产生微任务众人皆知,那MutationObserver API为什么被设计成微任务呢?首先,微任务是异步的,可以解决同步频繁更新DOM带来的性能 问题。其次,微任务的执行时机在当前宏任务阶段,这样也保证了执行的实时性。
从caniuse中可以看到,在PC端,MutationObserver的兼容性较Promise好,在移动端Promise的覆盖范围更高,毕竟使用率极高,几乎得到了所有现代浏览器的支持。
代码实现
了解产生微任务的方式后,我们可以简单实现一下
javascript
function microtask(task) {
if (typeof Promise === "function") {
Promise.resolve().then(task)
}
else if(typeof MutationObserver === "function") {
const ob = new MutationObserver(task)
const textNode = document.createTextNode("")
ob.observe(textNode, {
// 监听文本数据
characterData: true
})
textNode.data = "1"
}
// 兼容node环境
else if(process && typeof process.nextTick === "function") {
process.nextTick(task)
}
else {
// 最后降级到setTimeout,虽然它是一个宏任务
setTimeout(task, 0)
}
}
queueMicrotask
期间被问到除了Promise和MutationObserver还有其它方式吗。详情参考MDN
判断数组及其优缺点
1. 使用toString
只要是涉及到判断数据类型,该方法是一定要提及的,否则你的技术形象在面试官眼里可能会瞬间崩塌。
css
Object.prototype.toString.call([]) === '[object Array]'
依稀记得之前在看准网看过一篇面经,不知道是不是段子,现在还是可以找到的:
回归正题,这是es6之前比较通用的方法,但在es6之后就不再那么适用了,因为我们可以篡改toString的结果:
javascript
Array.prototype[Symbol.toStringTag] = "篡改数组toString的结果";
Object.prototype.toString.call([]);
// '[object 篡改数组toString的结果]'
const obj = {
[Symbol.toStringTag]: "篡改对象toString的结果"
};
Object.prototype.toString.call(obj);
// '[object 篡改对象toString的结果]'
2. 借助原型链原理
典型的就是instanceof
与 constructor
ini
[] instanceof Array; // true
[].constructor === Array; // true
但这种方法也是有问题,比如篡改原型,举个例子: const object = { __proto__: Array.prototype }
,
很明显,object是对象,但object instanceof Array
与object.constructor === Array
的结果都是true。
再比如跨领域对象问题,当前页面的Array与iframe下的Array不同:
ini
const iframe = document.createElement("iframe");
document.body.appendChild(iframe);
const xArray = window.frames[window.frames.length - 1].Array;
const arr = new xArray(1, 2, 3);
arr instanceof Array; // false
可以看到arr是通过iframe环境下的Array构造出来的,但它不是当前页面全局环境的Array的实例。
3. Array.isArray
鉴于以上种种原因,es6新增了Array.isArray(target)来准确判断数组。
javascript
Array.isArray([]); // true
Array.isArray(new Array); // true
Array.isArray({ constructor: Array }); // true
Array.isArray({ __proto__: Array.prototype }); // false
Array.prototype[Symbol.toStringTag] = "篡改数组toString的结果";
Array.isArray([]); // true
const iframe = document.createElement("iframe");
document.body.appendChild(iframe);
const xArray = window.frames[window.frames.length - 1].Array;
const arr = new xArray(1, 2, 3);
Array.isArray(arr); // true
小结
其实,看似简单的问题也能触及到你的知识盲点。