面试中的js,看这一篇就够了

前言

为了在 面试中脱颖而出,不仅要对基础知识了如指掌,还要掌握全面,并且能够在回答问题时揭示出其他面试者可能不了解的点,具备这些能力,你将能在众多候选人中脱颖而出。接下来,我们将为友友盘点 JavaScript 面试中经常被问到的问题,并提供全面且深入的答案。

一:数组操作

1.增

  • push():向数组的末尾添加一个或多个元素,会改变原数组,并返回新的长度。

    • 如果需要添加多个元素,可以传递多个参数给 push()
    • 使用 push() 时,原数组会被修改。
    js 复制代码
    let arr = [1, 2, 3]; 
    arr.push(4, 5); // 添加两个元素 
    console.log(arr); // 输出: [1, 2, 3, 4, 5] 
    console.log(arr.length); // 输出: 5
  • unshift():向数组的开头添加一个或多个元素,会改变原数组,并返回新的长度。

    • unshift() 会使数组的所有元素向后移动,因此性能上不如 push()
    • 如果数组很大,频繁使用 unshift() 可能会影响性能。
    js 复制代码
    let arr = [1, 2, 3];
    arr.unshift(0, -1); // 添加两个元素
    console.log(arr); // 输出: [-1, 0, 1, 2, 3]
    console.log(arr.length); // 输出: 5
  • splice():可以在任意位置插入元素,它可以接受三个参数:起始索引、要删除的元素数量以及要添加的元素列表。

    • splice() 会修改原数组,并返回一个包含被删除元素的新数组。
    • 如果第二个参数为0,则不会删除任何元素,只会插入元素。
    • 如果第二个参数为负数,则从数组的末尾开始计算起始索引。
    js 复制代码
    let arr = [1, 2, 3, 4, 5];
    arr.splice(1, 0, 'a', 'b'); // 在索引1处插入'a'和'b'
    console.log(arr); // 输出: [1, "a", "b", 2, 3, 4, 5]
    
    arr.splice(2, 2); // 删除索引2和3处的元素
    console.log(arr); // 输出: [1, "a", 3, 4, 5]
    
    arr.splice(-1, 1, 'z'); // 从数组末尾开始删除一个元素并插入'z'
    console.log(arr); // 输出: [1, "a", 3, 4, 'z']
  • concat():连接两个或多个数组,并返回一个新的数组

    • concat() 返回的是新数组,所以不会影响原始数组。
    • 如果需要合并多个数组,可以连续调用 concat() 或者传递多个参数。
    js 复制代码
    let arr1 = [1, 2, 3];
    let arr2 = [4, 5, 6];
    let combined = arr1.concat(arr2);
    console.log(combined); // 输出: [1, 2, 3, 4, 5, 6]
    console.log(arr1); // 输出: [1, 2, 3] (原数组未变)

2.删

  • pop():移除数组最后一个元素,并返回该元素的值,会改变原数组。

    • pop() 总是从数组的末尾移除元素。
    • 如果数组为空,pop() 将返回 undefined
    js 复制代码
    let arr = [1, 2, 3, 4];
    let removedElement = arr.pop(); // 移除最后一个元素
    console.log(removedElement); // 输出: 4
    console.log(arr); // 输出: [1, 2, 3]
  • shift():移除数组第一个元素,并返回该元素的值,会改变原数组。

    • shift() 总是从数组的开头移除元素。
    • 如果数组为空,shift() 将返回 undefined
    • shift() 会使数组中的所有元素向前移动一位。
    js 复制代码
    let arr = [1, 2, 3, 4];
    let removedElement = arr.shift(); // 移除第一个元素
    console.log(removedElement); // 输出: 1
    console.log(arr); // 输出: [2, 3, 4]
  • splice():从数组中删除指定范围的元素,与增的方式类似,此处省略

  • slice():提取数组的一部分而不改变原数组。

    • slice() 接受两个参数:起始索引和结束索引(非包含)。
    • 如果省略结束索引,则返回从起始索引到数组末尾的所有元素。
    • 如果起始索引为负数,则从数组的末尾开始计数。
    • 如果结束索引为负数,则从数组的末尾开始计数。
    js 复制代码
    let arr = [1, 2, 3, 4, 5];
    let slicedArray = arr.slice(1, 4); // 提取索引1到3之间的元素
    console.log(slicedArray); // 输出: [2, 3, 4]
    console.log(arr); // 输出: [1, 2, 3, 4, 5] (原数组未变)
    
    let slicedArrayNegative = arr.slice(-4, -1); // 提取索引1到3之间的元素,使用负数索引
    console.log(slicedArrayNegative); // 输出: [2, 3, 4]

3.查

  • indexOf():查找元素首次出现的位置,如果没有找到该元素,则返回 -1

    • indexOf() 从数组的开头开始搜索。
    • 如果数组中有重复的元素,indexOf() 只返回第一个匹配项的索引。
    js 复制代码
    let arr = [1, 2, 3, 2, 4];
    let index = arr.indexOf(2); // 查找首次出现的位置
    console.log(index); // 输出: 1
  • lastIndexOf():查找元素最后一次出现的位置,。如果没有找到该元素,则返回 -1

    • lastIndexOf() 从数组的末尾开始搜索。
    • 如果数组中有重复的元素,lastIndexOf() 返回最后一个匹配项的索引。
    js 复制代码
    let arr = [1, 2, 3, 2, 4];
    let index = arr.lastIndexOf(2); // 查找最后一次出现的位置
    console.log(index); // 输出: 3
  • includes():检查数组中是否包含特定元素,如果找到该元素,返回 true;否则返回 false

    • includes() 可以接受一个可选的第二个参数,用于指定搜索的起始位置。
    • 默认情况下,搜索从数组的开头开始。
    js 复制代码
    let arr = [1, 2, 3, 2, 4];
    let found = arr.includes(2); // 检查数组中是否包含2
    console.log(found); // 输出: true
    
    let foundFromIndex = arr.includes(2, 2); // 从索引2开始检查
    console.log(foundFromIndex); // 输出: true
  • find():找到第一个符合给定条件的元素,接受一个回调函数作为参数,该函数用于定义条件。

    • 回调函数接收三个参数:当前元素、当前元素的索引以及整个数组。
    • 如果没有找到符合条件的元素,find() 返回 undefined
    js 复制代码
        let arr = [1, 2, 3, 4, 5];
        let found = arr.find(function(element, index, array) {
            return element > 3; // 查找第一个大于3的元素
        });
        console.log(found); // 输出: 4

4.迭代

  • forEach():对每个元素执行一次提供的函数,不返回任何值,只是执行提供的函数。

    • forEach() 不会跳过缺失的元素,但如果数组元素是 undefined 或者被设置为 undefined,则提供的函数仍然会被调用。
    • 如果数组在 forEach() 执行过程中被修改,新增的元素会被处理,但已处理过的元素不会再被处理。
    js 复制代码
     let arr = [1, 2, 3, 4, 5];
     arr.forEach(function(item, index, array) {
         console.log(`Item at index ${index} is ${item}`);
     });
     // 输出:
     // Item at index 0 is 1
     // Item at index 1 is 2
     // Item at index 2 is 3
     // Item at index 3 is 4
     // Item at index 4 is 5
  • map():创建一个新数组,其结果是该数组中的每个元素都调用了提供的函数,原数组不会被修改。

    • map() 会跳过缺失的元素。
    • 如果数组在 map() 执行过程中被修改,新增的元素不会被处理。
    js 复制代码
    let arr = [1, 2, 3, 4, 5];
    let squared = arr.map(function(item) {
        return item * item;
    });
    console.log(squared); // 输出: [1, 4, 9, 16, 25]
  • filter():创建一个新数组,包含通过测试的所有元素,测试是通过提供的函数实现的

    • filter() 会跳过缺失的元素。
    • 如果数组在 filter() 执行过程中被修改,新增的元素不会被处理。
    js 复制代码
    let arr = [1, 2, 3, 4, 5];
    let evenNumbers = arr.filter(function(item) {
        return item % 2 === 0;
    });
    console.log(evenNumbers); // 输出: [2, 4]
  • reduce():对数组中的每个元素执行一个由提供的缩减函数(升序执行),将其结果汇总为单个返回值。

    • reduce() 接受一个回调函数,该函数至少需要两个参数:累积器(accumulator)和当前值(currentValue)。
    • 第一个参数(累积器)是累积的计算结果,或者是初始值。
    • 第二个参数(当前值)是当前正在处理的数组元素。
    • 第三个参数是当前索引。
    • 第四个参数是原数组。
    js 复制代码
    let arr = [1, 2, 3, 4, 5];
    let sum = arr.reduce(function(accumulator, currentValue) {
        return accumulator + currentValue;
    }, 0); // 初始值为0
    console.log(sum); // 输出: 15
  • every():检查是否数组中的所有元素都符合提供的测试函数,如果所有 元素都符合测试条件,则返回 true;否则返回 false

    • every() 会跳过缺失的元素。
    • 如果数组在 every() 执行过程中被修改,新增的元素不会被处理。
    js 复制代码
    let arr = [1, 2, 3, 4, 5];
    let allPositive = arr.every(function(item) {
        return item > 0;
    });
    console.log(allPositive); // 输出: true
  • some():检查是否数组中有任何元素符合提供的测试函数,如果有任何一个 元素符合测试条件,则返回 true;否则返回 false

    • some() 会跳过缺失的元素。
    • 如果数组在 some() 执行过程中被修改,新增的元素不会被处理。
    js 复制代码
    let arr = [1, 2, 3, 4, 5];
    let hasEven = arr.some(function(item) {
        return item % 2 === 0;
    });
    console.log(hasEven); // 输出: true

5.转换

  • sort():按字母顺序对数组进行排序,默认情况下,它将数组元素转换为字符串,并按照字符串的Unicode码点顺序进行排序。

    • sort() 方法会直接修改原数组。
    • 对于数值排序,需要提供一个比较函数来确保正确的排序顺序。
    • 如果数组包含非字符串类型的元素,它们会被转换为字符串。
    js 复制代码
    let arr = [10, 5, 3, 25, 1];
    arr.sort(); // 默认按字符串排序
    console.log(arr); // 输出: [1, 10, 25, 3, 5]
    
    // 数值排序
    let arrNumbers = [10, 5, 3, 25, 1];
    arrNumbers.sort((a, b) => a - b); // 使用比较函数
    console.log(arrNumbers); // 输出: [1, 3, 5, 10, 25]
  • reverse():颠倒数组中元素的顺序,直接修改原数组

    • reverse() 方法会改变原数组的顺序。
    • 如果需要保留原数组不变,可以先复制数组再调用 reverse()
    js 复制代码
    let arr = [1, 2, 3, 4, 5];
    arr.reverse();
    console.log(arr); // 输出: [5, 4, 3, 2, 1]
  • toReversed():返回一个新的反转数组,不会修改原数组。

    • toReversed() 方法返回一个新数组,原数组保持不变。
    • 此方法在较新的JavaScript环境中可用,例如ES2022及以后版本。
    js 复制代码
    let arr = [1, 2, 3, 4, 5];
    let reversedArr = arr.toReversed();
    console.log(reversedArr); // 输出: [5, 4, 3, 2, 1]
    console.log(arr); // 输出: [1, 2, 3, 4, 5] (原数组未变)
  • join():将数组元素组合成一个字符串,接受一个可选的分隔符参数,用于指定元素之间的分隔符,默认分隔符是一个逗号(,

    • join() 方法不会修改原数组。
    • 如果数组中包含非字符串类型的元素,它们会被转换为字符串。
    js 复制代码
    let arr = ['apple', 'banana', 'cherry'];
    let str = arr.join('-');
    console.log(str); // 输出: apple-banana-cherry
    
    let arrNumbers = [1, 2, 3];
    let strNumbers = arrNumbers.join(' ');
    console.log(strNumbers); // 输出: 1 2 3

二:字符串操作

1.增

  • concat():连接两个或多个字符串,并返回一个新的字符串,不会修改原有的字符串

    • concat() 方法可以接受一个或多个字符串参数。
    • 如果参数不是字符串,concat() 会尝试将其转换为字符串。
    • 如果只需要连接两个字符串,使用加号 (+) 运算符可能更为简洁。
    js 复制代码
    let str1 = 'Hello, ';
    let str2 = 'world!';
    let fullStr = str1.concat(str2);
    console.log(fullStr); // 输出: Hello, world!
    
    // 连接多个字符串
    let str3 = 'Welcome to ';
    let str4 = 'JavaScript ';
    let str5 = 'tutorial.';
    let fullStr2 = str3.concat(str4, str5);
    console.log(fullStr2); // 输出: Welcome to JavaScript tutorial.

使用加号 (+) 运算符连接字符串

虽然 concat() 方法可以用来连接字符串,但在实际开发中,使用加号 (+) 运算符连接字符串更为常见和简洁

js 复制代码
 let str1 = 'Hello, ';
 let str2 = 'world!';
 let fullStr = str1 + str2;
 console.log(fullStr); // 输出: Hello, world!

2.删

  • substr():已被废弃,不推荐使用,它用于提取字符串的一部分,接受两个参数:起始索引和要提取的长度。 如果你需要提取字符串的一部分,请使用 substring()slice()

  • substring():提取字符串的一部分,接受两个参数:起始索引和结束索引(非包含)

    • 如果参数的顺序颠倒,substring() 会自动交换它们。
    • 如果只提供一个参数,substring() 会从该索引到字符串的末尾提取。
    • 参数必须是非负整数
    js 复制代码
    let str = 'Hello, world!';
    let extracted = str.substring(7, 12);
    console.log(extracted); // 输出: world
  • slice():提取字符串的一部分,接受两个参数:起始索引和结束索引(非包含)

    • slice() 方法的行为类似于 substring(),但支持负数索引。
    • 如果只提供一个参数,slice() 会从该索引到字符串的末尾提取。
    • 如果参数为负数,则从字符串的末尾开始计数。
    js 复制代码
    let str = 'Hello, world!';
    let extracted = str.slice(7, 12);
    console.log(extracted); // 输出: world
    
    let extractedNegative = str.slice(-6, -1);
    console.log(extractedNegative); // 输出: world

3.改

  • replace():替换字符串中的子串,接受一个要替换的模式(可以是字符串或正则表达式)和一个替换文本或一个函数

    • 默认情况下,replace() 只替换第一个匹配项。
    • 如果要替换所有匹配项,可以使用全局正则表达式 /g
    js 复制代码
    let str = 'Hello, world!';
    let replaced = str.replace('world', 'JavaScript');
    console.log(replaced); // 输出: Hello, JavaScript!
    
    // 替换所有匹配项
    let str2 = 'Hello, world! Goodbye, world!';
    let replacedAll = str2.replace(/world/g, 'JavaScript');
    console.log(replacedAll); // 输出: Hello, JavaScript! Goodbye, JavaScript!
  • toUpperCase():将字符串转换为全大写,不会修改原字符串

    js 复制代码
    let str = 'Hello, world!';
    let upperCaseStr = str.toUpperCase();
    console.log(upperCaseStr); // 输出: HELLO, WORLD!
  • toLowerCase():将字符串转换为全小写,不会修改原字符串

    js 复制代码
    let str = 'Hello, World!';
    let lowerCaseStr = str.toLowerCase();
    console.log(lowerCaseStr); // 输出: hello, world!
  • trim():去除字符串两端的空白字符,不会修改原字符串,而是返回一个新的去除空白字符后的字符串

    js 复制代码
    let str = '   Hello, world!   ';
    let trimmedStr = str.trim();
    console.log(trimmedStr); // 输出: Hello, world!
  • trimStart():去除字符串开始的空白字符,不会修改原字符串,而是返回一个新的去除起始空白字符后的字符串

    js 复制代码
    let str = '   Hello, world!';
    let trimmedStartStr = str.trimStart();
    console.log(trimmedStartStr); // 输出: Hello, world!
  • trimEnd():去除字符串结束的空白字符,不会修改原字符串,而是返回一个新的去除末尾空白字符后的字符串。

    js 复制代码
    let str = 'Hello, world!   ';
    let trimmedEndStr = str.trimEnd();
    console.log(trimmedEndStr); // 输出: Hello, world!
  • padStart():在字符串开始处添加填充字符,不会修改原字符串,而是返回一个新的字符串,并且接受两个参数:目标长度和填充字符串。

    js 复制代码
    let str = '123';
    let paddedStr = str.padStart(6, '0');
    console.log(paddedStr); // 输出: 000123
  • padEnd():在字符串结束处添加填充字符,不会修改原字符串,而是返回一个新的字符串,并且接受两个参数:目标长度和填充字符串。

    js 复制代码
    let str = '123';
    let paddedStr = str.padEnd(6, '0');
    console.log(paddedStr); // 输出: 123000

4.查

  • charAt():获取指定索引处的字符

    • charAt() 方法接受一个整数参数,表示索引位置
    • 如果索引超出范围,charAt() 返回空字符串 ''
    js 复制代码
    let str = 'Hello, world!';
    let char = str.charAt(7);
    console.log(char); // 输出: w
  • indexOf():查找子串首次出现的位置

    • indexOf() 方法接受一个子串作为参数,可选地接受一个开始搜索的索引位置。
    • 如果没有找到子串,indexOf() 返回 -1
    js 复制代码
    let str = 'Hello, world!';
    let index = str.indexOf('world');
    console.log(index); // 输出: 7
  • lastIndexOf():查找子串最后一次出现的位置,其他特性与indexOf()类似

  • includes():检查字符串是否包含另一个字符串

    • includes() 方法接受一个子串作为参数,可选地接受一个开始搜索的索引位置。
    • 如果找到子串,includes() 返回 true;否则返回 false
    js 复制代码
    let str = 'Hello, world!';
    let contains = str.includes('world');
    console.log(contains); // 输出: true
  • startsWith():检查字符串是否以指定的字符串开始

    • startsWith() 方法接受一个子串作为参数,可选地接受一个开始搜索的索引位置。
    • 如果字符串以指定子串开始,startsWith() 返回 true;否则返回 false
    js 复制代码
    let str = 'Hello, world!';
    let startsWith = str.startsWith('Hello');
    console.log(startsWith); // 输出: true
  • endsWith():检查字符串是否以指定的字符串结束,其他特性与startsWith()类似

  • match():执行正则表达式搜索

    • match() 方法接受一个正则表达式作为参数。
    • 如果找到匹配项,match() 返回一个数组,其中包含匹配的字符串;如果没有找到匹配项,返回 null
    js 复制代码
    let str = 'Hello, world!';
    let matchResult = str.match(/world/);
    console.log(matchResult); // 输出: ["world", index: 7, input: "Hello, world!", groups: undefined]
  • search():执行正则表达式搜索并返回匹配的位置

    • search() 方法接受一个正则表达式作为参数。
    • 如果找到匹配项,search() 返回匹配的起始位置;如果没有找到匹配项,返回 -1
    js 复制代码
    let str = 'Hello, world!';
    let searchResult = str.search(/world/);
    console.log(searchResult); // 输出: 7

5.转换

  • split():将字符串分割成数组,接受一个分隔符作为参数,并可选地接受一个限制结果数组长度的参数。

    • split() 方法接受一个分隔符作为参数,可以是一个字符串或正则表达式。
    • 如果分隔符是一个空字符串 ''split() 会将字符串分割成单个字符的数组。
    • 如果提供了第二个参数,它指定了返回数组的最大长度。
    • 如果没有找到分隔符,split() 返回一个只包含原字符串的数组。
    js 复制代码
    let str = 'Hello, world! Welcome to JavaScript.';
    let words = str.split(' ');
    console.log(words); // 输出: ["Hello,", "world!", "Welcome", "to", "JavaScript."]
    
    // 使用正则表达式作为分隔符
    let wordsRegex = str.split(/[ ,.]+/);
    console.log(wordsRegex); // 输出: ["Hello", "world", "Welcome", "to", "JavaScript"]
    
    // 分割成单个字符
    let chars = str.split('');
    console.log(chars); // 输出: ["H", "e", "l", "l", "o", ",", " ", "w", "o", "r", "l", "d", "!", " ", "W", "e", "l", "c", "o", "m", "e", " ", "t", "o", " ", "J", "a", "v", "a", "S", "c", "r", "i", "p", "t", "."]
    
    // 限制结果数组的长度
    let limitedWords = str.split(' ', 3);
    console.log(limitedWords); // 输出: ["Hello,", "world!", "Welcome"]

当然可以。下面是关于 JavaScript 中类型转换机制的详细介绍,包括显示转换和隐式转换的例子。

三:类型转换机制

1.介绍

JavaScript 是一种动态类型的语言,这意味着变量可以在运行时改变类型,其中支持两种类型的类型转换:显示转换和隐式转换。

2.显示转换

显示转换是指开发者明确地将一个值从一种类型转换为另一种类型。这通常通过内置的类型转换函数完成。

方式

  • 使用 Number(), Boolean(), String() 等函数。

    • Number():将值转换为数字。
    • Boolean():将值转换为布尔值。
    • String():将值转换为字符串。
  • parseInt():将字符串转换为整数。

    • parseInt() 函数用于将字符串解析为整数。
js 复制代码
let num = Number('123'); // 显示转换为数字
console.log(num); // 输出: 123

let bool = Boolean(0); // 显示转换为布尔值
console.log(bool); // 输出: false

let str = String(123); // 显示转换为字符串
console.log(str); // 输出: "123"

let int = parseInt('123abc'); // 显示转换为整数
console.log(int); // 输出: 123

3.隐式转换

隐式转换是指 JavaScript 自动将一个值从一种类型转换为另一种类型,通常发生在算术运算、比较运算符等场合。

js 复制代码
let num = 5;
let str = '10';
let result = num + str; // 隐式转换为字符串
console.log(result); // 输出: "510"

let bool = true;
let num2 = 1 + bool; // 隐式转换为数字
console.log(num2); // 输出: 2

let arr = [];
let bool2 = !arr; // 隐式转换为布尔值
console.log(bool2); // 输出: false

let emptyArr = [];
let num3 = emptyArr == !emptyArr; // 隐式转换为数字
console.log(num3); // 输出: true

4.经典题

js 复制代码
[] == ![] // true
// 解析: [] -> false -> 0, ![] -> false -> 0, 0 == 0
  • [] 作为空数组,在布尔上下文中会被隐式转换为 false
  • ![] 也是空数组,取反后仍然是 false
  • 当进行 == 比较时,两边都会被转换为数字,false 转换为 0
  • 因此,0 == 0 返回 true

四:== vs ===

1.== 比较运算符

== 运算符用于比较两个值是否相等。如果两个值的类型不同,JavaScript 会尝试进行类型转换,使得值可以被比较。这种行为被称为类型强制(type coercion)。 特点:

  • 如果类型不同,会尝试进行类型转换。
  • 如果类型相同,则直接比较值。
js 复制代码
let num = 5;
let str = '5';
console.log(num == str); // 输出: true
// 解析: 由于类型不同,JavaScript 尝试将两者转换为相同的类型再比较。
// '5' 转换为数字 5,因此 5 == 5 返回 true。

2.=== 比较运算符

=== 运算符用于比较两个值是否严格相等。这意味着不仅要比较值,还要比较类型。如果两个值的类型不同,=== 直接返回 false

特点:

  • 如果类型不同,直接返回 false
  • 如果类型相同,则直接比较值。
js 复制代码
let num = 5;
let str = '5';
console.log(num === str); // 输出: false
// 解析: 由于类型不同,直接返回 false。

在实际开发中,推荐使用 === 运算符,能够减少由类型转换而导致的意外结果的可能性

五:拷贝问题

1.介绍

  • 浅拷贝:只复制对象的第一层属性,对于对象内部的对象属性,浅拷贝只会复制引用。

    • 新对象仍然引用了原对象的内部属性。
    • 如果原对象的内部属性发生变化,新对象也会受到影响。
  • 深拷贝:递归地复制所有层级的属性,创建一个完全独立的新对象。

    • 新对象与原对象之间没有任何引用关系。
    • 原对象的变化不会影响到新对象。

2.浅拷贝方法

  • Object.create()

    • 创建一个新对象,使用现有的对象来提供新创建对象的原型。
    • 只复制对象的可枚举属性。
    js 复制代码
    let obj = { a: 1, b: { c: 2 } };
    let shallowCopy = Object.create(obj);
    shallowCopy.a = 3;
    console.log(shallowCopy.b.c); // 输出: 2
  • Object.assign()

    • 复制源对象的所有可枚举属性到目标对象。
    • 只复制第一层属性。
js 复制代码
let obj = { a: 1, b: { c: 2 } };
let shallowCopy = Object.assign({}, obj);
shallowCopy.a = 3;
console.log(shallowCopy.b.c); // 输出: 2
  • {...obj}:快速复制对象的第一层属性。

    js 复制代码
    let obj = { a: 1, b: { c: 2 } };
    let shallowCopy = { ...obj };
    shallowCopy.a = 3;
    console.log(shallowCopy.b.c); // 输出: 2
  • [...arr]:快速复制数组

    js 复制代码
    let arr = [1, 2, 3];
    let shallowCopy = [...arr];
    shallowCopy.push(4);
    console.log(shallowCopy); // 输出: [1, 2, 3, 4]
  • concat():复制数组,但不适用于对象

    js 复制代码
    let arr = [1, 2, 3];
    let shallowCopy = arr.slice();
    shallowCopy.push(4);
    console.log(shallowCopy); // 输出: [1, 2, 3, 4]
  • slice():复制数组,但不适用于对象

    js 复制代码
    let arr = [1, 2, 3];
    let shallowCopy = arr.slice();
    shallowCopy.push(4);
    console.log(shallowCopy); // 输出: [1, 2, 3, 4]
  • arr.toReversed().reverse():先反转数组,再反转回来,相当于浅拷贝数组。

    js 复制代码
    let arr = [1, 2, 3];
    let shallowCopy = arr.toReversed().reverse();
    shallowCopy.push(4);
    console.log(shallowCopy); // 输出: [1, 2, 3, 4]

3.深拷贝方法

  • JSON.parse(JSON.stringify(obj))

    • 简单快速,但有局限性。
    • 无法处理循环引用,忽略函数和 Symbol 类型的属性。
    js 复制代码
    let obj = { a: 1, b: { c: 2 } };
    let deepCopy = JSON.parse(JSON.stringify(obj));
    deepCopy.a = 3;
    console.log(deepCopy.b.c); // 输出: 2
  • structuredClone(obj)

    • 现代浏览器支持,更安全。
    • 支持更多类型,如 Blob 和 File,但不支持循环引用。
    js 复制代码
    let obj = { a: 1, b: { c: 2 } };
    let deepCopy = structuredClone(obj);
    deepCopy.a = 3;
    console.log(deepCopy.b.c); // 输出: 2
  • lodash.cloneDeep(obj):提供更全面的支持,可以处理循环引用等问题

    js 复制代码
    let _ = require('lodash'); // 假设已经安装并引入 lodash 库
    let obj = { a: 1, b: { c: 2 } };
    let deepCopy = _.cloneDeep(obj);
    deepCopy.a = 3;
    console.log(deepCopy.b.c); // 输出: 2

以上就是最全面的深浅拷贝的方法,面试中往往面试者只能答出其中几个,倘若全部能答出,定会让面试官刮目相看, 像lodash.cloneDeep(obj)这种方法更是很少人知道

六:闭包

1.定义

闭包是指能够读取其他函数内部变量的函数。当一个函数被定义在一个外层函数内,并且保留对外层函数局部变量的引用时,就形成了闭包。

js 复制代码
function outerFunction() {
    let outerVariable = 'I am outside!';

    function innerFunction() {
        console.log(outerVariable);
    }

    return innerFunction;
}

const myClosure = outerFunction();
myClosure(); // 输出: I am outside!

在这个例子中,innerFunction 是一个闭包,因为它可以访问 outerFunction 的局部变量 outerVariable,即使 outerFunction 已经返回。

2.优点

  • 定义私有变量:闭包允许我们创建私有变量,这些变量只能被闭包内部的函数访问。
  • 防止全局变量污染:通过闭包,我们可以避免将变量暴露在全局作用域中,从而减少全局变量的数量,避免命名冲突。

3.缺点

  • 可能导致内存泄漏:闭包会维持对外层函数中变量的引用,如果这些变量不再被需要,但是由于闭包的存在而不能被垃圾回收器回收,可能会导致内存泄漏。

4.应用场景

  • 防抖节流:控制函数的调用频率,避免频繁触发事件导致性能问题。
  • 单例模式:确保类只有一个实例,通常通过闭包实现。
  • 柯里化:函数部分参数提前绑定,以便稍后调用时可以使用预设的参数。

这里友友要注意哦,接下来面试官可能会根据你的回答进行下一步的提问,比如手写防抖节流,聊聊单例模式,讲讲柯里化的实现,提前准备好这些,又可以再接面试官一招,暗搓搓的反客为主

相关推荐
CXDNW2 分钟前
【网络面试篇】HTTP(2)(笔记)——http、https、http1.1、http2.0
网络·笔记·http·面试·https·http2.0
neter.asia4 分钟前
vue中如何关闭eslint检测?
前端·javascript·vue.js
~甲壳虫4 分钟前
说说webpack中常见的Plugin?解决了什么问题?
前端·webpack·node.js
光影少年23 分钟前
vue2与vue3的全局通信插件,如何实现自定义的插件
前端·javascript·vue.js
As977_25 分钟前
前端学习Day12 CSS盒子的定位(相对定位篇“附练习”)
前端·css·学习
susu108301891127 分钟前
vue3 css的样式如果background没有,如何覆盖有background的样式
前端·css
Ocean☾28 分钟前
前端基础-html-注册界面
前端·算法·html
Rattenking28 分钟前
React 源码学习01 ---- React.Children.map 的实现与应用
javascript·学习·react.js
Dragon Wu30 分钟前
前端 Canvas 绘画 总结
前端
CodeToGym35 分钟前
Webpack性能优化指南:从构建到部署的全方位策略
前端·webpack·性能优化