前端面试题系列文章:
【12】「2023」性能优化相关知识点
【13】「2023」H5相关知识点
X-Mind原图地址:
输出是什么?
function sayHi() {
console.log(name)
console.log(age)
var name = 'Lydia'
let age = 21
}
sayHi()
- A:
Lydia和undefined - B:
Lydia和ReferenceError - C:
ReferenceError和21 - D:
undefined和ReferenceError
答案
D
考点:变量作用域提升、let和const变量的区别
解析:在函数内部,我们通过var关键字声明的变量,存在变量作用域提升。直到程序运行到定义变量位置之前默认值都是 undefined。
通过let和const关键字声明的变量也会提升,但是和var不同,他们不会被初始化。在我们声明之前是不能够访问它们的,这个行为被称之为暂时性死区。当我们试图在声明之前访问它们时,JavaScript将会抛出一个ReferenceError错误。
输出是什么?
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 1)
}
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 1)
}
- A:
0 1 2和0 1 2 - B:
0 1 2和3 3 3 - C:
3 3 3和0 1 2
答案
C
考点:JS事件循环、作用域
解析:由于JavaScript的事件循环机制,setTimeout属于异步任务,只有等同步代码执行完了之后才会执行。而第一个循环中的i变量是以var的关键字声明的,所以这个值是全局作用域下的,当开始执行第一个setTimeout的回调时,i 已经为3了。
而在第二个遍历中,i是通过let关键字声明的。通过let和const关键字的变量都是在块级作用域内生效的。在每次的遍历过程中,i都会有一个新的值,并且每个值都在循环内的作用域中。
输出是什么?
const shape = {
radius: 10,
diameter() {
return this.radius * 2
},
perimeter: () => 2 * Math.PI * this.radius
}
shape.diameter()
shape.perimeter()
- A:
20and62.83185307179586 - B:
20andNaN - C:
20and63 - D:
NaNand63
答案
B
考点:this的指向问题、箭头函数
解析:this永远指向最后调用他的那个对象。箭头函数的this是继承父执行上下文里面的this ,所以文中的调用diameter时this指向shape实例,调用perimeter时,this指向window。
输出是什么?
let c = { greeting: 'Hey!' }
let d
d = c
c.greeting = 'Hello'
console.log(d.greeting)
- A:
Hello - B:
undefined - C:
ReferenceError - D:
TypeError
答案
A
考点:深浅拷贝
解析:由于c对象是一个复杂对象类型,所以在执行d=c时,实际上是将c变量的地址赋值给了d变量。修改了d变量的属性值本质上也将c变量存储的数据改变了。
输出是什么?
function getAge(...args) {
console.log(typeof args)
}
getAge(21)
- A:
"number" - B:
"array" - C:
"object" - D:
"NaN"
答案
C
考点:ES6语法 -> 拓展运算符
解析:扩展运算符(...args)会返回实参组成的数组。而数组是对象,因此 typeof args 返回 "object"。
输出是什么?
const obj = { 1: 'a', 2: 'b', 3: 'c' }
const set = new Set([1, 2, 3, 4, 5])
obj.hasOwnProperty('1')
obj.hasOwnProperty(1)
set.has('1')
set.has(1)
- A:
falsetruefalsetrue - B:
falsetruetruetrue - C:
truetruefalsetrue - D:
truetruetruetrue
答案
C
考点:Object类型、Object类型和Set类型的区别
解析:所有对象的key(Symbol除外)在底层都是字符串,即使你没有将其作为字符串输入(假设设定了数字1为key,那么该对象同时存在1和'1'属性)。
对于集合类型,它只判断集合中有没有该子项,是做强等判断的,所以set.has('1') 返回 false。
输出是什么?
<div onclick="console.log('first div')">
<div onclick="console.log('second div')">
<button onclick="console.log('button')">
Click!
</button>
</div>
</div>
- A: Outer
div - B: Inner
div - C:
button - D: 一个包含所有嵌套元素的数组。
答案
C
考点:浏览器事件机制
解析:导致事件的最深嵌套的元素是事件的 target。
输出是什么?
<div onclick="console.log('div')">
<p onclick="console.log('p')">
Click here!
</p>
</div>
- A:
pdiv - B:
divp - C:
p - D:
div
答案
A
考点:浏览器事件机制、事件触发的三个阶段
解析:在事件传播期间,有三个阶段:捕获、目标和冒泡。默认情况下,事件处理程序在冒泡阶段执行。
输出是什么?
[...'Lydia']
- A:
["L", "y", "d", "i", "a"] - B:
["Lydia"] - C:
[[], "Lydia"] - D:
[["L", "y", "d", "i", "a"]]
答案
A
考点:ES6拓展运算符、可迭代对象
解析:string 类型是可迭代的。扩展运算符将迭代的每个字符映射成一个元素。
输出是什么?
const firstPromise = new Promise((res, rej) => {
setTimeout(res, 500, "one");
});
const secondPromise = new Promise((res, rej) => {
setTimeout(res, 100, "two");
});
Promise.race([firstPromise, secondPromise]).then(res => console.log(res));
- A:
"one" - B:
"two" - C:
"two" "one" - D:
"one" "two"
答案
B
考点:Promise对象的理解
解析:Promise.race 当数组中的任意一个Promise被resolve或reject后,会立即调用.then或.catch回调。
输出是什么?
let person = { name: "Lydia" };
const members = [person];
person = null;
console.log(members);
- A:
null - B:
[null] - C:
[{}] - D:
[{ name: "Lydia" }]
答案
D
考点:JS的内存分配规则
解析: 当设置两个对象彼此相等时,会将两个对象指向同一块地址。但是当你将一个变量分配至另一个变量时,其实只是执行了一个 复制 操作(可能平时遇到的比较少)。
输出是什么?
console.log(3 + 4 + "5");
A: "345"
B: "75"
C: 12
D: "12"
答案
B
考点:运算符的优先级
解析:在这个例子中,我们只有一类运算符+,对于加法来说,结合顺序就是从左到右。首先计算3 + 4,得到数字7,由于类型的强制转换,7 + '5'的结果是"75"。
输出是什么?
const num = parseInt("7*6", 10);
- A:
42 - B:
"42" - C:
7 - D:
NaN
答案
C
考点:parseInt API 的转化原理
解析:用parseInt设定了进制后,parseInt会检查字符串中的字符是否合法,一旦遇到一个在制定进制中不合法的字符后,立即停止解析并且忽略后面所有的字符串。
输出是什么?
function Car() {
this.make = "Lamborghini";
return { make: "Maserati" };
}
const myCar = new Car();
console.log(myCar.make);
- A:
"Lamborghini" - B:
"Maserati" - C:
ReferenceError - D:
TypeError
答案
B
考点:JS类的实现
解析:当我们在 new 一个对象,返回值有返回属性的时候,属性的值会覆盖构造函数中设定的值。
输出是什么?
(() => {
let x = (y = 10);
})();
console.log(typeof x);
console.log(typeof y);
- A:
"undefined", "number" - B:
"number", "number" - C:
"object", "number" - D:
"number", "undefined"
答案
A
考点:作用域、let和var的区别
解析:由于y=10相当于在全局作用域中声明了y。而x则声明在块作用域内,外层访问不到,所以打印出来的是undefined。
输出是什么?
let num = 10;
const increaseNumber = () => num++;
const increasePassedNumber = number => number++;
const num1 = increaseNumber();
const num2 = increasePassedNumber(num1);
console.log(num1);
console.log(num2);
- A:
10,10 - B:
10,11 - C:
11,11 - D:
11,12
答案
A
考点:一元操作符、语言语法
解析:一元操作符num++先返回操作值,再进行累加操作。
使用哪个构造函数可以成功继承Dog类??
class Dog {
constructor(name) {
this.name = name;
}
};
class Labrador extends Dog {
// 1
constructor(name, size) {
this.size = size;
}
// 2
constructor(name, size) {
super(name);
this.size = size;
}
// 3
constructor(size) {
super(name);
this.size = size;
}
// 4
constructor(name, size) {
this.name = name;
this.size = size;
}
};
- A: 1
- B: 2
- C: 3
- D: 4
答案
B
考点:ES6 extends 语法继承
解析:使用extends语法继承类,在调用super之前不能访问this关键字。使用super关键字,需要用给定的参数来调用父类的构造函数。
输出是什么?
console.log(Number(2) === Number(2))
console.log(Boolean(false) === Boolean(false))
console.log(Symbol('foo') === Symbol('foo'))
- A:
true,true,false - B:
false,true,false - C:
true,false,true - D:
true,true,true
答案
A
考点:强制类型转化、Symbol类型
解析:由于Number()和Boolean()这里都是强制类型转化,转出来的都是简单类型,可以直接判断相等。每个Symbol都是完全唯一的,传递给Symbol的参数只是给Symbol的一个描述,所以不相等。
输出是什么?
async function getData() {
return await Promise.resolve("I made it!");
}
const data = getData();
console.log(data);
- A:
"I made it!" - B:
Promise {<resolved>: "I made it!"} - C:
Promise {<pending>} - D:
undefined
答案
C
考点:Promise基础知识
解析:异步函数始终返回一个Promise。await任然需要等待Promise的解决:当我们调用getData()并将其赋值给data,此时data为getData方法返回的一个挂起的promise,该promise并没有解决。
输出是什么?
const promise = new Promise((resolve, reject) => {
console.log(1);
console.log(2);
});
promise.then(() => {
console.log(3);
});
console.log(4);
答案
1
2
4
考点:微任务、Promise状态机制、Promise的构造函数
解析:首先,Promise的构造函数是同步任务,是立即执行的,所以先打印1、2。由于Promise内部的状态一直没有发生改变,一直处于pending状态,所以这里不输出3。
输出是什么?
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);
答案
promise1
1 Promise{<resolved>: resolve1}
2 Promise{<pending>}
resolve1
考点:宏任务 && 微任务、Promise状态机制
解析:
- 首先可以将整个
script看做是一个宏任务,按顺序执行这些代码 - 首先进入到Promise,执行构造函数的代码,打印
promise1 - 碰到
resolve函数,将promise1的状态改变为resolved,并将结果保存下来 - 碰到
promise1.then这个微任务,放入微任务队列 - 执行同步任务1,打印出
promise1的状态是resolved - 执行同步任务2,打印出
promise2的状态是pending - 宏任务执行完毕,查找微任务队列,发现
promise1.then这个微任务切状态为resovled,执行它
输出是什么?
const promise = new Promise((resolve, reject) => {
resolve('success1');
reject('error');
resolve('success2');
});
promise.then((res) => {
console.log('then:', res);
}).catch((err) => {
console.log('catch:', err);
})
答案
then: success1
考点:Promise的状态一旦发生改变,就不会再发生变化
解析:开始状态由pending变为resolve,说明已经变为已完成状态,下面的两个状态的就不会再执行,同时下面的catch也不会捕获到错误。
输出是什么?
Promise.resolve('1')
.then(res => {
console.log(res)
})
.finally(() => {
console.log('finally')
})
Promise.resolve('2')
.finally(() => {
console.log('finally2')
return '我是finally2返回的值'
})
.then(res => {
console.log('finally2后面的then函数', res)
})
答案
1
finally2
finally
finally2后面的then函数 2
考点:Promise的finallyAPI
解析:
.finally()方法不管Promise对象最后的状态如何都会执行。.finally()方法的回调函数不接受任何的参数,也就是说你在.finally()函数中是无法知道Promise最终的状态是resolved还是rejected的。- 它最终返回的默认会是一个上一次的
Promise对象值,不过如果抛出的是一个异常则返回异常的Promise对象。 - finally本质上是then方法的特例
输出是什么?
function runAsync (x) {
const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000))
return p
}
function runReject (x) {
const p = new Promise((res, rej) => setTimeout(() => rej(`Error: ${x}`, console.log(x)), 1000 * x))
return p
}
Promise.all([runAsync(1), runReject(4), runAsync(3), runReject(2)])
.then(res => console.log(res))
.catch(err => console.log(err))
答案
// 1s后输出
1
3
// 2s后输出
2
Error: 2
// 4s后输出
4
考点:Promise.all
解析:Promise.all只有所有的Promise都变成resolved才会执行.then的回调。只要有一个Promise异常就会进入到.catch中。但是!并不会影响数组中其它的异步任务的执行。
输出是什么?
async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end");
}
async function async2() {
console.log("async2");
}
async1();
console.log('start')
答案
async1 start
async2
start
async1 end
考点:async、await 语法
解析:
- 首先执行
async1中的同步代码async1 start,接着遇到了await,它会阻塞async2后面代码的执行,因此会先去执行async2中的同步代码。 - 接着执行同步代码
start - 在一轮宏任务全部执行完之后,再来执行
await后面的内容async1 end
这里最好理解的方法是:await 后面的语句相当于放到了 new Promise的构造函数中,下面一行及之后的语句相当于放在了Promise.then中
输出是什么?
async function async1 () {
console.log('async1 start');
await new Promise(resolve => {
console.log('promise1')
})
console.log('async1 success');
return 'async1 end'
}
console.log('srcipt start')
async1().then(res => console.log(res))
console.log('srcipt end')
答案
script start
async1 start
promise1
script end
考点:async、await语法
解析:这里需要注意的是在async1中await后面的Promise是没有返回值的,也就是它的状态始终是pending状态,所以在await之后的内容是不会执行的,包括async1后面的 .then。
输出是什么?
async function async1 () {
console.log('async1 start');
await new Promise(resolve => {
console.log('promise1')
resolve('promise1 resolve')
}).then(res => console.log(res))
console.log('async1 success');
return 'async1 end'
}
console.log('srcipt start')
async1().then(res => console.log(res))
console.log('srcipt end')
答案
script start
async1 start
promise1
script end
promise1 resolve
async1 success
async1 end
考点:async、await语法
解析:这里和上一题不同的是,await后的Promise的状态最终为resolved,await之后的代码会继续执行。async1后面的.then也会执行。