这些JavaScript题目,真的和你想象的一样吗?

119 阅读2分钟

引言

最近在整理面试题时,我遇到了两道看似简单但暗藏玄机的JavaScript题目。下滑揭开谜底~

new 操作符

1. 首先我们来看一段代码

function Person() {
    this.name = 'Jack';
}
var p = new Person();
console.log(p.name);
var q = Person();
console.log(q);
console.log(name);
console.log(q.name);

这段代码主要是一个基本构造函数的调用,通过 new 操作符 和 直接调用。这有什么区别呢?

解析:
1. new 操作符:创建一个新的对象,并让 this 指向他,并且会默认返回 this(题目中 this 为 p)

2. 直接调用:this 指向全局对象,即 window.name = 'Jack' ,但函数没有显式 return,所以 q 是 undefined
答案: Jack undefined Jack(全局变量) undefined

2. 再来康康另一道题~

function Person() {
    this.name = 'Jack';
    return 'Tom';
}
var p = new Person();
console.log(p);
console.log(p.name);
解析:这段代码只使用了 new 操作符
1. 当使用 new 操作符,构造函数返回原始值,new 操作会忽略他,仍然返回 this(即新创建的对象)

2. 原始值 vs 非原始值
原始值:number、string、boolean、null、undefined、symbol、bigInt
非原始值:object、所有的引用类型
答案:{ name: 'Jack' } Jack(return 'Tom' 被忽略)

3. 什么!怎么还有一题!耐心康康吧~

function Person() {
    this.name = 'Jack';
    return { age: 18 };
}
var p = new Person();
console.log(p);
console.log(p.name);
console.log(p.age);
解析:当使用 new 操作符时,构造函数返回非原始值,则该返回值成为整个 new 表达式的结果。
答案:{ age: 18} undefined(返回值为{age:18} ) 18

async

接下来沉浸式做一下这道代码输出题~
class EventEmitter {
 /**
 * @type {Record<string, { resolve: (params: any) => void; promise: Promise<any> }>}
 */
 eventMap = {};

 emit(eventName, params) {
 if (this.eventMap[eventName]) {
 const { resolve } = this.eventMap[eventName];
 resolve(params);
 this.eventMap[eventName] = null;
 }
 }

 async wait(eventName) {
 if (!this.eventMap[eventName]) {
 let resolve;
 let promise = new Promise((r) => (resolve = r));
 this.eventMap[eventName] = {
 resolve,
 promise,
 };
 }
 return this.eventMap[eventName].promise;
 }
}

async function test() {
 const eventEmitter = new EventEmitter();

 let a = eventEmitter.wait("event1");
 let b = eventEmitter.wait("event1");
 eventEmitter.emit("event1", 1);
 console.log(a === 1);
 console.log(b === 1);
 console.log(a === b);
 console.log((await a) === 1);
 console.log((await b) === 1);
 console.log((await a) === (await b));
}

test();
首先公布答案: false false false true true true(大家会不会对第三个输出有疑惑?有疑惑的往下看~)
解析:
1. 这段代码实现了一个简单的 `EventEmitter` 类,它允许异步等待某个事件 (`wait`) 并触发该事件 (`emit`)。然后,`test()` 函数测试了它的行为。我们逐步分析它的执行过程和输出。

2. 主要问题是在 wait 函数:
如果 eventName 没有对应的 promise,则创建一个新的 promise 并存储 resolve 和 promise。
若 eventName 有对应的 promise 则返回 promise,调用者可以 await 它。 你是否认为调用两次 wait 会返回同一个 promise?(答案是不会)

3. 原因:因为 wait 函数前有 async ,async function 声明会创建一个 AsyncFunction 对象。每次调用异步函数时,都会返回一个新的 promise 对象.