2025春招-JavaScript面试题库

136 阅读5分钟

ES6

Set与Map以及WeakSet与WeakMap

参考文章:# ES6数据结构深度解析:Set, Map, WeakSet 和 WeakMap # 参考文章:js 中 Map 和 Set 的用法以及区别

Set与Map的区别(标准答案)

1.定义与用途

  • Map:是一个存储键值对的数据结构,每个键值都是唯一的,用于映射到一个值。适合用于需要通过唯一标识符快速查找数据的场景
  • Set:是一个不包含重复元素的集合。主要用于存储唯一项,确保集合中的每个元素都是独一无二的。

2.存储方式

  • Map: 存储的是键值对,即每条记录都包含一个键和一个对应的值。
  • Set: 只存储单个值,没有键值对的概念。

3.访问元素

  • Map: 通过键来访问值,例如 map.get(key)
  • Set: 不能通过键访问,只能遍历整个集合来访问元素。

4.插入元素

  • Map: 插入键值对,如果键已存在,则更新其对应的值。
  • Set: 插入单个值,如果值已存在,则不会插入新值。

5.删除元素

  • Map: 通过键删除键值对,例如 map.delete(key)
  • Set: 通过值删除元素,例如 set.delete(value)

6.应用场景

  • Map: 适用于需要通过唯一键快速查找对应值的场景,如缓存、配置管理等。
  • Set: 适用于需要确保集合中元素唯一性的场景,如去重、集合运算等。

数据类型判断

如何准确判断数组数据类型

<body>
    <script>
        // 1.isArray
        let a = [1, 2, 3]
        let b = '1111'
        let c = {}
        console.log(Array.isArray(a));
        // 2.instanceof 
        console.log(a instanceof Array);
        // 3.Object.prototype.toString.call()
        console.log(Object.prototype.toString.call(a) === '[object Array]');
    </script>
</body>

对象浅拷贝、深拷贝

  • 浅拷贝:只复制一层对象或者数组,对于嵌套的对象或者数组仅仅复制其引用
  • 深拷贝:复制整个对象,确保新旧对象之间没有任何共享的引用数据类型
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>深拷贝与浅拷贝</title>
</head>

<body>
    <script>
        let obj1 = {
            a: 1,
            b: {
                name: '张三'
            }
        }
        // 1.浅拷贝
        let obj2 = Object.assign({}, obj1)
        obj2.b.name = '李四'
        console.log('obj1', obj1);
        console.log('obj2', obj2);
        // 2.深拷贝
        let obj3 = JSON.parse(JSON.stringify(obj1))
        obj3.b.name = '李虎'
        console.log('obj3', obj3);
    </script>
</body>

</html>

深拷贝注意事项:

  • 使用 JSON.parse(JSON.stringify(...)) 进行深拷贝有一些限制:

    • 它不能处理循环引用的对象。
    • 它会忽略函数和原型链上的属性。
    • 它无法正确拷贝 DateRegExp 等特殊类型的对象。
  • 对于更复杂的情况,可以使用第三方库如 Lodash (_.cloneDeep) 或者自己实现一个递归拷贝的函数。

this指向与改变this指向

为什么要有this指向

在this之前,js当中没有一个机制可以让对象中的函数去访问对象自己的属性,如下面这段代码所示:调用对象obj中的方法bar访问obj内部属性,直接打印myname会报错,倘若打印this.myname就不会。所以,为了让对象中的函数有能力访问对象自己的属性,我们需要this这样一个机制。

let obj = {
    myname: 'qing',
    bar: function(){ //在对象内部方法使用对象内部的属性
        console.log(myname);
    }
}
obj.bar()

this绑定规则

  1. 默认绑定:当一个函数独立调用,即不带任何修饰符的时候,函数在哪个词法作用域下生效,函数中的this就指向哪里。通俗点说,只要是默认绑定,this就指向window(因为一个函数不带任何前缀被独立调用的时候,就说明它被声明在了全局中)
  2. 隐式绑定:当函数的引用有上下文对象的时候(当函数被某个对象所拥有时),函数的this指向引用它的对象
  3. 隐式丢失:当一个函数被多个对象链式调用时,函数的this指向就近的那个对象
  4. 显式绑定:call bind apply
  5. new绑定:当使用构造函数创建对象时,构造函数内部的this会绑定到新创建的对象实例上
  6. 箭头函数:使用箭头函数会让this指向其父级作用域

函数柯里化

什么是函数柯里化

所谓柯里化,简单来说就是把一个多参数的函数,转化为单参数的函数。

// 柯里化之前
function add(a, b, c) {
    return a + b + c
}
add(1, 2, 3) // 6

// 柯里化之后
let addCurry = curry(add)
addCurry(1)(2)(3) // 6

示例

下面是一个使用 JavaScript 函数柯里化的示例代码:

// 柯里化函数写法
function add(x) {
  return function(y) {
    return x + y;
  };
}

// 调用柯里化函数
const add5 = add(5);
console.log(add5(3)); // 8
console.log(add5(7)); // 12

在上面的示例代码中,add() 函数被柯里化为两个函数:一个是接收一个参数 y 的函数,返回 x + y;另一个是接收两个参数 x 和 y 的函数,返回 x + y。

柯里化函数的写法可以根据需要进行修改,比如可以定义多个参数的柯里化函数,也可以将参数进行优化等等。

优缺点分析

JavaScript 函数柯里化的优点是:

  • 函数更加灵活和可重用。通过柯里化,可以将一个多参数的函数转换为一系列单参数的函数,使函数更加灵活和可重用。
  • 可以避免重复的代码。通过柯里化,可以避免在调用函数时重复地传递参数,从而避免了重复的代码。

JavaScript 函数柯里化的缺点是:

  • 可能会降低性能。通过柯里化,函数的性能可能会降低,因为需要额外的内存来存储函数的返回值和参数。
  • 可能会增加代码复杂度。通过柯里化,可能会增加代码的复杂度,因为需要处理额外的参数和函数返回值。

手写实现

// 原始函数
function Fn_init (a, b, c) {
    console.log('最终的结果:', a * b * c)
}
​
// 柯里化函数
function curryization (fn, params) {
    // 获取函数参数长度
    const lth = fn.length
​
    params = params || []
    console.log('params', params);
​
    return function (...args) {
        // 收集fn函数的参数
        // newArgs = params.concat(Array.prototype.slice.call(arguments))
        newArgs = params.concat(args)
        console.log('newArgs', newArgs);
​
        if (newArgs.length < lth) {
            // 继续执行curryization柯里化函数,继续收集参数,this指向window
            return curryization.call(this, fn, newArgs)
        } else {
            // 所有参数收集完毕,整体执行源函数,this指向window
            return fn.apply(this, newArgs)
        }
    }
}
​
const curryFunc = curryization(Fn_init)
​
curryFunc(2)(3, 4) // 24
// curryFunc(2, 3)(4) // 24
// curryFunc(2, 3, 4) // 24
// curryFunc(2)(3)(4) // 24