JavaScript中常见的看代码写结果例题(一)

本文介绍了JavaScript中的事件循环机制,包括宏任务与微任务的执行顺序,并通过例题深入讲解。同时,文章还探讨了原型和原型链问题,解析了如何查找对象的方法并分析了不同情况下的输出结果。通过实例,帮助读者理解async函数的工作原理以及原型链的查找路径。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

同学们都知道,在前端面试中不管你的技术栈是vue还是react,各大公司对于JavaScript语言还是有一定要求的,此外在面试手撕代码环节,通常会考你一些JS基础或源码,尤其是看代码写结果最容易踩坑,通常出题的范围离不开以下几个方面:

  1. 事件循环机制问题
  2. 原型链问题
  3. 变量提升问题
  4. 箭头函数问题
  5. 闭包打印

一、事件循环机制问题

要了解事件循环机制,首先要知道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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

安萌萌萌萌萌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值