同学们都知道,在前端面试中不管你的技术栈是vue还是react,各大公司对于JavaScript语言还是有一定要求的,此外在面试手撕代码环节,通常会考你一些JS基础或源码,尤其是看代码写结果最容易踩坑,通常出题的范围离不开以下几个方面:
- 事件循环机制问题
- 原型链问题
- 变量提升问题
- 箭头函数问题
- 闭包打印
…
一、事件循环机制问题
要了解事件循环机制,首先要知道Javascript是一门单线程语言,以及语句的执行顺序,还有什么是同步任务、什么是异步任务。这些内容在上一篇博客
JavaScript中的事件循环机制中已经详细介绍了,这里直接总结上案例。
1、宏任务一般包括:整体代码script,setTimeout,setInterval、I/O、UI render等
2、微任务一般包括:Promise、Object.observe、MutationObserver等
事件执行顺序
1、先按同步代码顺序运行
2、开始清空微任务队列
3、开始清空宏任务队列(执行一个宏任务,把相关微任务添加入微任务队列)
4、开始清空微任务队列(上一个执行宏任务中加入队列的微任务一次性全部执行完成)
5、开始清空宏任务队列(执行下一个宏任务,把相关微任务添加入微任务队列)
…循环一直到执行完成
1.1 例题
1. console.log('sync statement 1');
2. Promise.resolve().then(function() {
3. console.log('micro task 1');
4. setTimeout(function() {
5. console.log('macro task 1');
6. }, 0);
7. }).then(function() {
8. console.log('micro task 2');
9. });
10. setTimeout(function() {
11. console.log('macro task 2')
12. Promise.resolve().then(function(){
13. console.log('micro task 3');
14. })
15. }, 0)
16. console.log('sync statement 2');
输出结果:
sync statement 1
sync statement 2
micro task 1
micro task 2
macro task 2
micro task 3
macro task 1
1.2 例题
输出结果:
1 5 6 2 3 4 7 8 9
1.3 例题
输出结果:
1.4 例题
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0)
async1();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
});
console.log('script end');
输出结果:
script start
async1 start
async2
promise1
script end
promise2
async1 end
setTimeout
本题有两种理解:
1、async 函数中可能会有 await 表达式,这会使 async 函数暂停执行,等待表达式中的 Promise 解析完成后继续执行 async 函数并返回解决结果。
2、async 函数返回一个 Promise 对象,当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句。
MDN所描述的暂停执行,实际上是让出了线程,跳出async函数体,然后继续执行后面的脚本的。
二、原型和原型链问题
"原型和原型链"是大家老生常谈的问题,也是面试基础知识环节和手撕代码环节容易踩得大坑,要想完美、准确的得到代码结果,最好先去把Js基础原型链一章的知识吃透消化掉!下面是案例!!!
2.1 例题
var F = function() {};
Object.prototype.a = function() {
console.log('a');
};
Function.prototype.b = function() {
console.log('b');
} ;
var f = new F();
f.a();
f.b();
F.a();
F.b();
输出结果:
a
报错:
Uncaught TypeError: f.b is not a function
a
b
解析
1、f.a();f.b();
对于 f ,是一个实例对象,并不是 Function 的实例,因为它本来就不是构造函数,调用的是 Function 原型链上的相关属性和方法了,只能访问到 Object 原型链。所以 f.a() 输出 a ,而 f.b() 就报错了。
2、F.a();F.b();
F 是个构造函数,而 F 是构造函数 Function 的一个实例。因为 F instanceof Object === true 、F instanceof Function === true,所以F 是 Object 和 Function 两个的实例,即 F 能访问到 a、b。所以 F.a() 输出 a ,F.b() 输出 b。
3、分析查找路径
1> F.a 的查找路径:F 自身:没有 -> F.proto(Function.prototype):没有—> F.proto.proto(Object.prototype):找到了输出 a
2> F.b 的查找路径:F 自身:没有 -> F.prototype(Function.prototype):b
3> f.a 的查找路径:f 自身:没有 -> f.proto(Object.prototype):输出 a
4> f.b 的查找路径:f 自身:没有 -> f.proto(Object.prototype):没有 -> f.proto.proto(Object.prototype.proto:null):找不到,报错
2.2 例题
function Foo() {
Foo.a = function() {
console.log(1)
}
this.a = function() {
console.log(2)
}
}
Foo.prototype.a = function() {
console.log(3)
}
Foo.a = function() {
console.log(4)
}
Foo.a();
let obj = new Foo();
obj.a();
Foo.a();
输出结果:
4 2 1
解析
1、Foo.a() :这个是调用 Foo 函数的静态方法 a,虽然 Foo 中有优先级更高的属性方法 a,但 Foo 此时没有被调用,所以此时输出 Foo 的静态方法 a 的结果:4
2、let obj = new Foo():使用了 new 方法调用了函数,返回了函数实例对象,此时 Foo 函数内部的属性方法初始化,原型链建立。
3、obj.a() :调用 obj 实例上的方法 a,该实例上目前有两个 a 方法:一个是内部属性方法,另一个是原型上的方法。当这两者都存在时,首先查找 ownProperty ,如果没有才去原型链上找,所以调用实例上的 a 输出:2
4、Foo.a() : 根据第2步可知 Foo 函数内部的属性方法已初始化,覆盖了同名的静态方法,所以输出:1
2.3 例题
输出结果:
想解决该问题需要了解几个知识点:proto_(隐式原型) 和 prototype (显式原型)的关系、隐式原型链和显示原型链的作用以及 _proto__的指向问题。
该题解析
1、a.prototype === b =>false
prototype 属性是只有函数才特有的属性,当你创建一个函数时,js 会自动为这个函数加上 prototype 属性,值是一个空对象。而实例对象是没有 prototype 属性的。所以 a.prototype 是 undefined ,第一个结果为 false。
2、Object.getPrototypeOf(a) === b =>true
明确对象和构造函数的关系,对象在创建的时候, proto 指向其构造函数的 prototype 属性。Object 实际上是一个构造函数(typeof Object => “function”),使用字面量创建对象和使用 a = new Object() 创建对象是一样的,所以 a.proto = Object.prototype ,所以,Object.getPrototypeOf(a) 与 a.proto 是一样的,结果为 true。
3、fn.prototype === Object.getPrototypeOf(fn) =>false
fn.prototype 和 Object.getPrototypeOf(fn) 说的不是一回事,fn.prototype 是使用 new 创建的 fn 的实例的原型:fn.prototype === Object.getPrototypeOf(new fn());//true
Object.getPrototypeOf(fn) 是 fn 函数的原型:Object.getPrototypeOf(fn) === Function.prototype ; //true
所以答案为 false