JavaScript常见面试真题总结

1. var、let和const的区别

  • var是 ES5 语法,let conts是 ES6 语法,var有变量提升
  • varlet是定义变量,变量可修改;const是定义常量,不可修改
  • let const有块级作用域,var没有
// 1. 变量提升
console.log(a) // undefined
var = 100

var a
console.log(a)
a = 100

console.log(b) // 报错
let b = 100

// 2. 块级作用域
// let 形成块级作用域
for(let i = 0; i < 10; i++) {
    let j = i + 1
}
console.log(i, j) // 报错

// var 变量提升
for(var i = 0; i < 10; i++) {
    var j = i + 1
}
console.log(i, j) // 10 10


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

2. typeof能判断哪些类型

  • 基本类型:string,number,boolean,Symbol,undefined
  • 对象:object(注意,typeof null === 'object')
  • 方法:function

3. 列举强制类型转换和隐式类型转换

  • 强制类型转换:parseInt parseFloat toString 等
  • 隐式类型转换:if、逻辑运算、==、+拼接字符串

4. 手写深度比较isEqual


function isObject(obj) {
    return typeof obj === 'object' && obj !== null
}

function isArray(obj) {
    return Array.prototype.isArray(obj)
}

function isEqual(o1, o2) {
    // 如果是其中一个不是对象,则直接比较
    if (!isObject(o1) || !isObject(o2)) {
        return o1 === o2
    }
    // 如果是同一个对象,即引用相同
    if (o1 === o2) {
        return true
    }
    // 如果有一个是对象,有一个是数组
    if ((isObject(o1) && isArray(o2)) || (isArray(o1) && isObject(o2))) {
        return false
    }
    
    // 两个都是对象或数组
    const o1Keys = Object.keys(o1)
    const o2Keys = Object.keys(o2)
    if (o1Keys.length !== o2Keys.length) {
        return false
    }
    
    for (let key in o1Keys) {
        const res = isEqual(o1[key], o2[key])
        if (!res) {
            return false
        }
    }
    return true
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

5. push pop和unshift shift

面试回答思路:

  1. 功能是什么
  2. 参数和返回值时什么
  3. 是否会对原数组造成影响
  • push:在数组结尾添加一个元素,返回 length
  • pop:删除数组最后一个元素,返回被删除的元素
  • unshift:在数组的开头添加一个元素,返回 length
  • shift:删除数组第一个元素,返回被删除的元素

(1)改变原有数组的方法

  • push
  • pop
  • unshift
  • shift

(2)不改变原有数组的方法(纯函数)

  • concat
  • map
  • filter
  • slice

5. slice和splice的区别

1. slice

切片函数,无副作用,对原函数无影响

/**
 * @param startIndex 切片开始的位置
 * @param endIndex 切片结束的位置,返回的数组不包含该位置的元素
 * @return 新的数组
 */
slice(startIndex, endIndex)
1
2
3
4
5
6
let arr = [10, 20, 30, 40, 50]

const arr1 = arr.slice() // [10, 20, 30, 40, 50], 不切片,返回新数组,相当于克隆
const arr2 = arr.slice(1, 3) // [20, 30]
const arr3 = arr.slice(2)  // [30, 40], 从开始位置截取至末尾
const arr4 = arr.slice(-3) // [20, 30, 40], 从尾部往回截取

1
2
3
4
5
6
7

2. splice

剪接函数,有副作用,会对原函数造成影响

/**
 * @param startIndex 开始删除的位置
 * @param deleteCount 从startIndex起,删除的元素的个数
 * @param items 拼接的items
 * @return 包含被删除元素的数组
 */
splice(startIndex, deleteCount, ...items)
1
2
3
4
5
6
7
let arr = [10, 20, 30, 40]

// 删除 + 拼接
const arr1 = arr.splice(1, 2, 'a', 'b') // [20, 30],原数组:[10, 'a', 'b', 40]
// 只删除
const arr2 = arr.splice(1, 2)           // [20, 30],原数组:[10, 40]
// 只删除
const arr3 = arr.splice(1, 0, 'a', 'b') // [ ],原数组[10, "a", "b", 20, 30, 40]

1
2
3
4
5
6
7
8
9

6. [10, 20, 30].map(parseInt)

考察要点:

  1. map函数
  2. parseInt函数
  3. 函数简写拆解

答案:[10, Nan, Nan]解析:

  1. map传入一个函数,返回传入函数作用于原数组的新数组
  2. parseInt:第一个参数为需要转换的数据,第二参数为进制(0和10都是十进制)
  3. 简写拆解:
[10, 20, 30].map((num, index) => parseInt(num, index))
1

7. get和post的区别

  1. get一般用于查询操作,post一般用于提交数据
  2. get参数拼接在 url 上,post提交参数放在请求体中
  3. 安全性:post易于防止 CSRF