跟chatGPT一起复习前端 —— 输出结果

44 阅读8分钟

前言

又到一波面试准备的时刻,整理了一波前端相关要点内容,配合chatGPT完成要点内容整理,有纠正错误和需要补充的小伙伴可以在这里留言,及时更新。

变量声明

以下输出结果:

console.log(name);
console.log(age);
var name = "张三";
let age = 21;

在第一行代码中,name是使用var关键字声明的变量,它会被提升到作用域的顶部,因此在该行代码执行之前,name已经被声明了,但是它还没有被赋值。因此第一行代码的输出结果是undefined。

在第二行代码中,age是使用let关键字声明的变量,它不会被提升到作用域的顶部。由于在该行代码执行之前,age还没有被声明,因此在该行代码执行时会抛出ReferenceError异常。

在第三行代码中,name被赋值为"Lydia"。

在第四行代码中,age被赋值为21。

以下输出结果:

for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 1);
}
 
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 1);
}

在第一段代码中,i是使用var关键字声明的变量,它的作用域是整个函数。在每次循环中,setTimeout函数会被调用,并且会创建一个闭包,该闭包中包含了对i的引用。由于setTimeout函数的回调函数是异步执行的,因此在循环结束后,i的值已经变成了3。因此第一段代码的输出结果为3、3、3。

在第二段代码中,i是使用let关键字声明的变量,它的作用域是块级作用域。在每次循环中,setTimeout函数会被调用,并且会创建一个闭包,该闭包中包含了对i的引用。由于i是一个块级作用域变量,在每次循环中都会重新声明和初始化。因此第二段代码的输出结果为0、1、2。

let d = 13;
const c = 5;
(function() {
    a = b = 3;
    let d = 2;
})()
e = 5;
console.log(a);
console.log(b);
console.log(c);
console.log(d);
console.log(e);

类型转换

以下输出结果:

+true;
!"张三";

在第一行代码中,+true将true转换为数字1。

在第二行代码中,"张三"是一个非空字符串,因此它的布尔值为true。!运算符将其转换为false。

new操作符

以下输出结果:

function Foo() {
    this.a = 1;
    return { a: 2,b: 3 };
}
Foo.prototype.a = 4;
Foo.prototype.b = 5;
Foo.prototype.c = 6;
const x = new Foo();
console.log(x.a, x.b, x.c);

在这段代码中,Foo是一个构造函数。在构造函数中,this.a被赋值为1,但是在函数的最后,它返回了一个新的对象{ a: 2,b: 3 }。因此在创建新对象时,this.a的值被覆盖了。

在这个例子中,x是通过new关键字创建的一个新对象。由于Foo.prototype.a、Foo.prototype.b和Foo.prototype.c都是Foo的原型属性,因此它们都可以通过x访问到。x.a的值为2,因为在构造函数中返回的对象中a被赋值为2。x.b的值为3,因为在构造函数中返回的对象中b被赋值为3。x.c的值为undefined,因为它不是在构造函数中定义的属性,并且它也不是通过返回的对象定义的属性。

this指向

以下输出结果:

let obj = {a: function() {
    console.log(this)
}}
obj.a()
let b = obj.a
b()

在第一行代码中,obj.a是一个函数,它被调用时,this指向的是obj对象本身,因此第一行代码的输出结果是{ a: [Function: a] }。

在第二行代码中,b是一个函数,它的值等于obj.a。当b被调用时,this指向的是全局对象window(或global),因此第二行代码的输出结果是Window {…}。

这是因为在JavaScript中,函数的this指向取决于函数的调用方式。如果函数作为对象的方法被调用,this指向该对象;如果函数作为普通函数被调用,this指向全局对象window(或global)。

以下输出结果:

let obj = {a: () => {
    console.log(this)
}}
obj.a()
let b = obj.a
b()

在第一行代码中,obj.a是一个箭头函数,它没有自己的this,因此它的this指向的是定义时所在的作用域,也就是全局对象window(或global)。因此第一行代码的输出结果是Window {…}。

在第二行代码中,b是一个函数,它的值等于obj.a。由于箭头函数没有自己的this,因此b被调用时,this指向的仍然是定义时所在的作用域,也就是全局对象window(或global)。因此第二行代码的输出结果也是Window {…}。

以下输出结果:

const shape = {
  radius: 10,
  diameter() {
    return this.radius * 2;
  },
  perimeter: () => 2 * Math.PI * this.radius
};
 
shape.diameter();
shape.perimeter();

在这段代码中,shape是一个对象,它有一个radius属性和两个方法:diameter和perimeter。

在diameter方法中,this.radius指向的是shape对象的radius属性。因此diameter方法返回的值为20。

在perimeter方法中,this指向的是定义时所在的作用域,也就是全局对象window(或global)。由于在全局对象中并没有定义radius属性,因此this.radius的值为undefined。当undefined乘以2时,返回的结果为NaN。

事件循环

以下输出结果:

const promise = new Promise((resolve, reject) => {
  console.log(1);
  console.log(2);
});
promise.then(() => {
  console.log(3);
});
console.log(4);

当执行到 promise.then(() => {console.log(3);}); 时,then 方法会被放入微任务队列中,等待主线程执行完毕后再执行。所以,当主线程执行完毕后,会先输出 4,然后再执行微任务队列中的 then 方法,但是,由于没有 resolve,所以 then 方法不会被执行。 输出1 2 4

以下输出结果:

const promise1 = new Promise((resolve, reject) => {
  console.log('promise1')
  resolve('resolve1')
})
const promise2 = promise1.then(res => {
  console.log(res)
})
console.log('1', promise1);
console.log('2', promise2);

当执行到 console.log('1', promise1); 时,promise1 已经被 resolve,所以输出结果为 <resolved>: "resolve1"

当执行到 console.log('2', promise2); 时,promise2 还没有被 resolve,所以输出结果为 <pending>

当执行到 const promise2 = promise1.then(res => {console.log(res)}); 时,then 方法会被放入微任务队列中,等待主线程执行完毕后再执行。所以,当主线程执行完毕后,会先输出 promise1 和 <resolved>: "resolve1",然后再执行微任务队列中的 then 方法,输出 resolve1

以下输出结果:

const promise = new Promise((resolve, reject) => {
  console.log(1);
  setTimeout(() => {
    console.log("timerStart");
    resolve("success");
    console.log("timerEnd");
  }, 0);
  console.log(2);
});
promise.then((res) => {
  console.log(res);
});
console.log(4);

当执行到 console.log(1); 时,输出 1

当执行到 console.log(2); 时,输出 2

当执行到 setTimeout(() => {console.log("timerStart");resolve("success");console.log("timerEnd");}, 0); 时,setTimeout 方法会被放入宏任务队列中,等待主线程执行完毕后再执行。所以,当主线程执行完毕后,会先输出 4,然后再执行宏任务队列中的 setTimeout 方法,输出 timerStart 和 timerEnd,并且将 Promise 状态改为 resolved

当执行到 promise.then((res) => {console.log(res);}); 时,then 方法会被放入微任务队列中,等待主线程执行完毕后再执行。所以,当主线程执行完毕后,会先输出 success

以下输出结果:

Promise.resolve().then(() => {
  console.log('promise1');
  const timer2 = setTimeout(() => {
    console.log('timer2')
  }, 0)
});
const timer1 = setTimeout(() => {
  console.log('timer1')
  Promise.resolve().then(() => {
    console.log('promise2')
  })
}, 0)
console.log('start');

当执行到 console.log('start'); 时,输出 start

当执行到 Promise.resolve().then(() => {console.log('promise1');const timer2 = setTimeout(() => {console.log('timer2')}, 0)}); 时,then 方法会被放入微任务队列中,等待主线程执行完毕后再执行。所以,当主线程执行完毕后,会先输出 promise1

当执行到 const timer1 = setTimeout(() => {console.log('timer1')Promise.resolve().then(() => {console.log('promise2')})}, 0) 时,setTimeout 方法会被放入宏任务队列中,等待主线程执行完毕后再执行。所以,当主线程执行完毕后,会先输出 timer1

当执行到 Promise.resolve().then(() => {console.log('promise2')}) 时,then 方法会被放入微任务队列中,等待主线程执行完毕后再执行。所以,当主线程执行完毕后,会先输出 promise2

当执行到 const timer2 = setTimeout(() => {console.log('timer2')}, 0) 时,setTimeout 方法会被放入宏任务队列中,等待主线程执行完毕后再执行。所以,当主线程执行完毕后,会先输出 timer2

以下输出结果:

console.log(1)

setTimeout(() => {
  console.log(2)
})

new Promise(resolve =>  {
  console.log(3)
  resolve(4)
}).then(d => console.log(d))

setTimeout(() => {
  console.log(5)
  new Promise(resolve =>  {
    resolve(6)
  }).then(d => console.log(d))
})

setTimeout(() => {
  console.log(7)
})

console.log(8)

代码执行时,会发生以下情况:

  1. 执行 console.log(1),输出 1
  2. 执行 setTimeout(() => {console.log(2)}),将回调函数添加到事件循环中。稍后将执行它。
  3. 执行 new Promise(resolve => {console.log(3);resolve(4)}).then(d => console.log(d))。调用 Promise 构造函数,传入一个回调函数,该函数记录 3 并解析为值 4。在 Promise 对象上调用 then 方法,将回调函数添加到微任务队列中。稍后将执行它。
  4. 执行 setTimeout(() => {console.log(5);new Promise(resolve => {resolve(6)}).then(d => console.log(d))}),将回调函数添加到事件循环中。稍后将执行它。
  5. 执行 setTimeout(() => {console.log(7)}),将回调函数添加到事件循环中。稍后将执行它。
  6. 执行 console.log(8),输出 8
  7. 处理微任务队列,并执行在步骤 3 中添加的回调函数。它记录 4
  8. 事件循环处理步骤 2 中添加的第一个回调函数,并记录 2
  9. 事件循环处理步骤 4 中添加的第二个回调函数,并记录 5。它还创建一个新的 Promise 对象,该对象解析为值 6。在此 Promise 对象上调用 then 方法,并将回调函数添加到微任务队列中。稍后将执行它。
  10. 再次处理微任务队列,并执行在步骤 9 中添加的回调函数。它记录 6
  11. 事件循环处理步骤 5 中添加的第三个回调函数,并记录 7