JS面试相关

深拷贝、浅拷贝、递归、优化
扁平化
柯里化
this指向+原型
继承
call、apply、bind
js取整的方法,parseInt第二个参数是什么
forEach和map有什么区别,使用场景?
内存泄漏的场景
原型链+原型
严格模式
Js中for in 和for of的区别
slice、splice、split 三者的区别
变量提升、函数提升
循环引用?如何解决
null与undefined
闭包
数组多种去重方法
slice是干嘛的,splice是否会改变原数组
== 和 === 有什么区别?
什么是事件委托?为什么它是有用的?
什么是立即执行的函数表达式(IIFE)?
如何检查一个变量是否是数组?
描述map、reduce 和 filter 方法。

这些前端JS面试题涵盖了许多JavaScript的核心和高级概念。我们可以根据不同的主题对它们进行分类。以下是一个分类示例:

一. 基础概念与语法:

  • null与undefined
  • == 和 === 有什么区别?
  • Js中for in 和for of的区别
  • slice、splice、split 三者的区别
  • 变量提升、函数提升
  • 严格模式

二. 数据结构与类型操作:

1.深拷贝、浅拷贝、递归、优化

浅拷贝:只复制引用,而未复制真正的值,改变其中一个的值,另一个的值也随之改变
深拷贝:复制真正的值,改变其中一个的值,另一个的值不会改变

assignAPI实现一级内容深拷贝,二级内容浅拷贝

assign:一级内容深拷贝,二级及以上浅拷贝 一次把深拷贝和浅拷贝都写出来

// assin:一级内容深拷贝,二级及以上浅拷贝
let obj = {name:"Tom", age:18, job:["web"]}
let objCopy = Object.assign({}, obj)//这里一定要两个参数{},不然就全是深拷贝

// 深拷贝
console.log(obj)
console.log(objCopy)
objCopy.job[0] = "IT"
console.log(obj)
console.log(objCopy)

console.log("--------------------")

// 浅拷贝
console.log(obj)
console.log(objCopy)
objCopy.name = "Mick"
console.log(obj)
console.log(objCopy)

let objCopy = Object.assign({}, obj)//这里一定要两个参数{},不然就全是浅拷贝

递归实现深拷贝

递归实现深拷贝主要是对对象的每一个属性进行递归复制。下面是一个递归实现深拷贝的基本示例:(核心步骤:使用instanceof检查对象(用typeof就要多加一个判断不等于null的条件)循环引用处理

循环引用:WeakMap 用于存储已经被复制过的对象。如果在递归的过程中遇到同一个对象,则直接从 WeakMap中获取,以此避免循环引用的问题。

function deepClone(target, map = new Map()) {
    if (typeof target === 'object' && target !== null) {
        // 避免循环引用问题
        if (map.has(target)) {
            return map.get(target);
        }

        // 处理数组和对象的情况
        const cloneTarget = Array.isArray(target) ? [] : {};
        map.set(target, cloneTarget);

        for (let key in target) {
            if (target.hasOwnProperty(key)) {
                cloneTarget[key] = deepClone(target[key], map);
            }
        }

        return cloneTarget;
    } else {
        return target;
    }
}

// 示例
const obj = {
    num: 0,
    str: '',
    boolean: true,
    unf: undefined,
    nul: null,
    obj: { name: 'I am an object', id: 1 },
    arr: [0, 1, 2],
    func: function() { console.log('I am a function') },
    date: new Date(0),
    reg: new RegExp('/I am a regular expression/ig'),
    [Symbol('key')]: 'I am a symbol',
    error: new Error('I am an error object'),
    map: new Map([['name', 'I am a map']]),
    set: new Set([1, 'I am a set'])
};

const clonedObj = deepClone(obj);

console.log(clonedObj);
console.log(clonedObj.obj !== obj.obj);  // true,说明确实进行了深拷贝

注意:这个示例并不完整,例如对于某些特殊对象(如 FunctionDateRegExpErrorMapSet 等),可能还需要进行特定的处理。此外,不同的应用场景可能对深拷贝的需求有所不同,因此需要根据具体情况对上述代码进行适当的调整或扩展。

2.typeof 和 instanceof的区别

在JavaScript中,typeofinstanceof 用于确定变量的类型,但它们在应用和行为上有所不同。

  1. typeof

    • 对于原始类型(如'string', 'number', 'boolean'等),typeof运行得很好。
    • 但对于数组和普通对象,typeof都返回'object',这会引起一些混淆。
    • 对于nulltypeof也返回'object',这是一个著名的JavaScript错误。
  2. instanceof

    • 它基于原型链来工作,检查左侧的对象是否是右侧构造函数的实例。
    • 对于自定义对象或者其他内置对象如Array、Date等,使用instanceof更为准确。

3.数组多种去重方法

数组去重的5种方法:
1、用“[…new Set(arr)]”语句去重;
2、用“Array.from(new Set(arr))”语句去重;
3、利用indexOf()去重;
4、利用includes()去重;
5、利用filter()去重。

数组去重的方法

1、[…new Set(arr)]

const arr = [1, 2, 3, 2, 3];

[...new Set(arr)]; // [1, 2, 3]

2、Array.from(new Set(arr))
加粗样式

const arr = [1, 2, 3, 2, 3];

Array.from(new Set(arr)); // [1, 2, 3]

由于 Set 中的元素是唯一的,无论是原始值或者是对象引用,所以可以通过将数组转换成 Set 对象来实现去重

Array.from方法可以将 Set 对象转换成数组

3、利用indexOf去重

function unique(arr) {
    var array = [];
    for (var i = 0; i < arr.length; i++) {
        if (array .indexOf(arr[i]) === -1) {
            array .push(arr[i])
        }
    }
    return array;
}

新建一个空的结果数组,for 循环原数组,判断结果数组是否存在当前元素,如果有相同的值则跳过,不相同则push进数组。

4、利用includes

function unique(arr) {
    var array =[];
    for(var i = 0; i < arr.length; i++) {
            if( !array.includes( arr[i]) ) {//includes 检测数组是否有某个值
                    array.push(arr[i]);
              }
    }
    return array
}

5、利用filter

function unique(arr) {
  return arr.filter(function(item, index, arr) {
    //当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
    return arr.indexOf(item, 0) === index;
  });
}

描述map、reduce 和 filter 方法。

如何检查一个变量是否是数组?

slice是干嘛的,splice是否会改变原数组

forEach和map有什么区别,使用场景?

区别
forEachmap 都是数组的常用方法,但它们有不同的目的和用法。下面是它们之间的主要区别以及各自的使用场景:

  1. 目的:

    • forEach: 主要用于迭代数组并执行某些操作,但不返回一个新数组
    • map: 迭代数组,并对每个元素执行某些操作,然后返回一个新数组,该数组包含与原始数组相同数量的元素(可能已被修改)。
  2. 返回值:

    • forEach: 无返回值(返回undefined)。
    • map: 返回一个新数组。
  3. 是否改变原始数组:

    • forEach: 不直接改变原始数组,但可以在回调中更改原始数组(第二个参数index索引可以用来修改)。
    • map: 不改变原始数组,但新数组的元素可能已被修改。
  4. 使用场景:

    • forEach: 当你只是想对数组的每个元素执行操作,而不关心结果时,如打印每个元素。
    • map: 当你想基于现有数组创建一个新数组时,如将每个数字元素乘以2
  5. 示例:

    const arr = [1, 2, 3, 4, 5];
    
    // 使用 forEach 打印每个元素
    arr.forEach(item => {
        console.log(item);
    });
    
    // 使用 map 创建一个新数组,其中每个数字都乘以2
    const doubled = arr.map(item => item * 2);
    console.log(doubled); // [2, 4, 6, 8, 10]
    

什么意思?forEach: 不直接改变原始数组,但可以在回调中更改原始数组。

当你使用 forEach 遍历数组时,它不会自动更改数组的内容。但是,在 forEach 的回调函数中,你可以手动修改原始数组。

让我们通过代码来解释:

  1. forEach 不会自动更改数组:

    const arr = [1, 2, 3];
    arr.forEach(item => item * 2);
    
    console.log(arr);  // 输出:[1, 2, 3]
    

    尽管我们尝试将每个项乘以2,但原始数组 arr 并没有更改。

  2. forEach 的回调中手动修改原始数组:

    const arr = [1, 2, 3];
    arr.forEach((item, index) => {
        arr[index] = item * 2;
    });
    
    console.log(arr);  // 输出:[2, 4, 6]
    

    在这个例子中,我们明确地使用了数组的索引来修改原始数组 arr。因此,数组的内容已经更改。

这就是所说的"forEach 不直接改变原始数组,但可以在回调中通过索引更改原始数组"的意思。

3. 函数与作用域:

  • 闭包
  • 柯里化
  • call、apply、bind
  • this指向+原型
  • 什么是立即执行的函数表达式(IIFE)?
  1. 对象与原型:

    • 原型链+原型
    • 继承
    • js取整的方法,parseInt第二个参数是什么
  2. 优化与问题解决:

    • 递归、优化
    • 扁平化
    • 内存泄漏的场景
    • 循环引用?如何解决
  3. 事件与异步编程:

    • 什么是事件委托?为什么它是有用的?

此分类只是为了将相关的概念归入相同的组中,但实际上,不同的面试官可能会采用不同的分类方式,或根据他们想测试面试者的哪一方面的知识来调整问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值