前端必会的9道基础题目
变量提升
function sayHi() {
console.log(name)
console.log(age)
var name = 'Hello'
let age = 21
}
sayHi()
- A:
Hello
和undefined
- B:
Hello
和ReferenceError
- C:
ReferenceError
和21
- D:
undefined
和ReferenceError
在函数内部,我们首先通过 var 关键字声明了 name 变量。这意味着变量被提升了(内存空间在创建阶段就被设置好了),直到程序运行到定义变量位置之前默认值都是 undefined。因为当我们打印 name 变量时还没有执行到定义变量的位置,因此变量的值保持为 undefined。
通过 let 和 const 关键字声明的变量也会提升,但是和 var 不同,它们不会被初始化。在我们声明(初始化)之前是不能访问它们的。这个行为被称之为暂时性死区。当我们试图在声明之前访问它们时,JavaScript 将会抛出一个 ReferenceError 错误。
const
function checkAge(age) {
if (age < 18) {
const message = "Sorry, you're too young."
} else {
const message = "Yay! You're old enough!"
}
return message
}
console.log(checkAge(21))
- A:
"Sorry, you're too young."
- B:
"Yay! You're old enough!"
- C:
ReferenceError
- D:
undefined
const和let声明的变量是具有块级作用域的,块是大括号({})之间的任何东西,即上述情况if / else语句的花括号。 由于块级作用域,我们无法在声明的块之外引用变量,因此抛出ReferenceError。
箭头函数
const shape = {
radius: 10,
diameter() {
return this.radius * 2
},
perimeter: () => 2 * Math.PI * this.radius
}
shape.diameter()
shape.perimeter()
- A:
20
and62.83185307179586
- B:
20
andNaN
- C:
20
and63
- D:
NaN
and63
这里的区别在于函数的声明方式不一样,一个是常规函数,一个是箭头函数
对于箭头函数,this 关键字指向的是它当前周围作用域(简单来说是包含箭头函数的常规函数,如果没有常规函数的话就是全局对象),这个行为和常规函数不同。这意味着当我们调用 perimeter 时,this 不是指向 shape 对象,而是它的周围作用域(在例子中是 window)。在 window 中没有 radius 这个属性,因此返回 undefined。
=和
let a = 3
let b = new Number(3)
let c = 3
console.log(a == b)
console.log(a === b)
console.log(b === c)
- A:
true
false
true
- B:
false
false
true
- C:
true
false
false
- D:
false
true
true
new Number() 是一个内建的函数构造器。虽然它看着像是一个 number,但它实际上并不是一个真实的 number:它有一堆额外的功能并且它是一个对象。
当我们使用 == 操作符时,它只会检查两者是否拥有相同的值。因为它们的值都是 3,因此返回 true。
然后,当我们使用 === 操作符时,两者的值以及类型都应该是相同的。new Number() 是一个对象而不是 number,因此返回 false。
静态方法
class Chameleon {
static colorChange(newColor) {
this.newColor = newColor
return this.newColor
}
constructor({ newColor = 'green' } = {}) {
this.newColor = newColor
}
}
const freddie = new Chameleon({ newColor: 'purple' })
freddie.colorChange('orange')
- A:
orange
- B:
purple
- C:
green
- D:
TypeError
colorChange 是一个静态方法。静态方法被设计为只能被创建它们的构造器使用(也就是 Chameleon),并且不能传递给实例。因为 freddie 是一个实例,静态方法不能被实例使用,因此抛出了 TypeError 错误。
模板表达式
function getPersonInfo(one, two, three) {
console.log(one)
console.log(two)
console.log(three)
}
const person = 'JavaScript'
const age = 21
getPersonInfo`${person} is ${age} years old`
- A:
"JavaScript"
21
["", " is ", " years old"]
- B:
["", " is ", " years old"]
"JavaScript"
21
- C:
"JavaScript"
["", " is ", " years old"]
21
如果使用标记模板字面量,第一个参数的值总是包含字符串的数组。其余的参数获取的是传递的表达式的值!
对象的键值
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:
false
true
false
true
- B:
false
true
true
true
- C:
true
true
false
true
- D:
true
true
true
true
所有对象的键(不包括 Symbol)在底层都是字符串,即使你自己没有将其作为字符串输入。这就是为什么 obj.hasOwnProperty('1') 也返回 true。
对于集合,它不是这样工作的。在我们的集合中没有 '1':set.has('1') 返回 false。它有数字类型为 1,set.has(1) 返回 true。
Promise
const first = () => (new Promise((resolve,reject)=>{
console.log(3);
let p = new Promise((resolve, reject)=>{
console.log(7);
setTimeout(()=>{
console.log(5);
resolve(6);
},0)
resolve(1);
});
resolve(2);
p.then((arg)=>{
console.log(arg);
});
}));
first().then((arg)=>{
console.log(arg);
});
console.log(4);
解答 答案:3、7、4、1、2、5
第一次事件循环:
先执行宏任务,主script ,new Promise立即执行,输出【3】,执行p这个new Promise 操作,输出【7】,发现setTimeout,将回调放入下一轮任务队列(Event Queue),p的then,姑且叫做then1,放入微任务队列,发现first的then,叫then2,放入微任务队列。执行console.log(4),输出【4】,宏任务执行结束。 再执行微任务,执行then1,输出【1】,执行then2,输出【2】。到此为止,第一轮事件循环结束。开始执行第二轮。
第二次事件循环:
先执行宏任务里面的,也就是setTimeout的回调,输出【5】。resovle不会生效,因为p这个Promise的状态一旦改变就不会在改变了。 所以最终的输出顺序是3、7、4、1、2、5。
终极类型转换
这是一道非常烧脑的题目,懂的自然懂。哈哈
下面题目输出什么?
([]+{})[!+[]+!+[]]
解答 答案:b
1. []+{} 加法运算符,{}会转成字符串'[object Object]',因为加法运算符只有一个计算因子是字符串类型,则进行字符串拼接运算,[]转成"",所以[]+{} '[object Object]'输出
2. !+[]+!+[],分解+[]转成数字0,!+[],则表示!0,转成1,所以!+[]+!+[] 输出2
3. '[object Object]'[2] 字符串索引,则输出b