当前位置: 首页 > news >正文

前端的面试笔记——JavaScript篇(二)

一、instanceof

在 JavaScript 里,instanceof 是一个相当实用的运算符,它的主要功能是检查某个对象是否属于特定构造函数的实例。这里需要明确的是,判断的依据并非对象的类型,而是其原型链。下面为你详细介绍它的用法和特点:

基础语法

object instanceof constructor
objectconstructor 的实例,或者说在其原型链上能找到 constructor.prototype,该表达式就会返回 true,反之则返回 false

主要作用

1. 判定对象类型

你可以借助 instanceof 来判断一个对象是否为特定类的实例。

class Person {}
const person = new Person();console.log(person instanceof Person); // true
2. 验证内置对象类型

对于 JavaScript 的内置对象,同样可以使用 instanceof 来验证其类型。

const arr = [];
const num = 5;console.log(arr instanceof Array); // true
console.log(arr instanceof Object); // true(因为数组本质上也是对象)
console.log(num instanceof Number); // false(基本类型通过装箱转换才会成为对象)
console.log(new Number(5) instanceof Number); // true
3. 检查原型链关系

instanceof 还能用于检查对象的原型链上是否存在某个构造函数的原型。

function Animal() {}
function Dog() {}Dog.prototype = Object.create(Animal.prototype);const dog = new Dog();console.log(dog instanceof Dog); // true
console.log(dog instanceof Animal); // true
console.log(dog instanceof Object); // true

需留意的特殊情形

1. 基本类型与 instanceof

基本类型(像 numberstringboolean 等)直接使用 instanceof 会返回 false,除非它们通过构造函数(如 new Number())被转换为对象。

const str = "hello";
const strObj = new String("hello");console.log(str instanceof String); // false
console.log(strObj instanceof String); // true
2. 跨窗口(Cross-Window)问题

在浏览器环境中,不同窗口(比如 iframe)的全局对象是相互独立的。这就导致,从一个窗口创建的对象和另一个窗口的构造函数使用 instanceof 比较时,结果会是 false

// 在 iframe 中执行
const iframe = document.createElement('iframe');
document.body.appendChild(iframe);const arr = iframe.contentWindow.Array();
console.log(arr instanceof Array); // false(当前窗口的 Array 与 iframe 的 Array 不同)
3. 手动修改原型

要是手动对对象的原型(__proto__Object.setPrototypeOf)进行修改,instanceof 的结果可能会与预期不符。

const obj = {};
Object.setPrototypeOf(obj, Array.prototype);console.log(obj instanceof Array); // true(尽管 obj 并非通过 new Array() 创建)

替代方案

1. Object.prototype.toString.call()

这种方法可以更精准地判断对象类型,而且能处理基本类型的情况。

const arr = [];
console.log(Object.prototype.toString.call(arr)); // [object Array]
2. Array.isArray()

专门用于判断一个值是否为数组,并且能解决跨窗口的问题。

console.log(Array.isArray([])); // true

总结

instanceof 运算符在检查对象与构造函数的继承关系时非常有用,但在使用过程中要特别注意基本类型、跨窗口对象以及原型修改等特殊情况可能带来的影响。在实际的编程工作中,你可以根据具体的使用场景,将 instanceof 与其他类型检查方法结合起来使用。

二、深拷贝和浅拷贝

在 JavaScript 中,深拷贝和浅拷贝是处理对象和数组时的重要概念,也是面试中的高频考点。以下是对这两个概念的详细解释及相关面试题分析:

一)、基本概念

1. 浅拷贝(Shallow Copy)
  • 定义:创建一个新对象,复制原始对象的一层属性。如果属性是基本类型(如 number、string),则复制值;如果属性是引用类型(如对象、数组),则复制引用(内存地址)。
  • 特点:新对象和原始对象共享引用类型的属性,修改其中一个会影响另一个。
2. 深拷贝(Deep Copy)
  • 定义:创建一个新对象,递归复制原始对象的所有属性(包括嵌套的引用类型)。
  • 特点:新对象和原始对象完全独立,修改其中一个不会影响另一个。

二)、实现方式

浅拷贝方法

浅拷贝直接赋值的方式这里就不做讲解了,这里列举一些容易忽略的浅拷贝方式,在项目中可能会因此产生一些bug问题。

1. 手动遍历对象:
function shallowCopy(obj) {const newObj = {};for (let key in obj) {if (obj.hasOwnProperty(key)) {newObj[key] = obj[key];}}return newObj;
}
2. 展开语法(Spread):
const newObj = { ...oldObj };
const newArr = [...oldArr];
3. Object.assign():
const newObj = Object.assign({}, oldObj);
4. Array.prototype.slice() / Array.from():
const newArr = oldArr.slice();
const newArr = Array.from(oldArr);
深拷贝方法
1. JSON.parse(JSON.stringify()):
const newObj = JSON.parse(JSON.stringify(oldObj));
限制
  • 无法处理函数、正则、Date 等特殊对象。
  • 会忽略 undefinedsymbol 类型的属性。
  • 无法处理循环引用(对象引用自身)。
2. 递归实现(手动-推荐方式):
function deepCopy(obj) {if (obj === null || typeof obj !== 'object') return obj;const newObj = Array.isArray(obj) ? [] : {};for (let key in obj) {if (obj.hasOwnProperty(key)) {newObj[key] = deepCopy(obj[key]);}}return newObj;
}
3. 第三方库:
  • Lodash 的 _.cloneDeep()
    const newObj = _.cloneDeep(oldObj);
    
  • 结构化克隆(Structured Clone):
    浏览器原生 API,支持循环引用,但有兼容性限制
    const newObj = structuredClone(oldObj); // 浏览器原生 API,支持循环引用,但有兼容性限制
    

三)、面试题

1. 手写深拷贝函数

要求:实现一个能处理嵌套对象、数组、循环引用的深拷贝函数。
答案示例:

function deepCopy(obj, map = new WeakMap()) {// 处理基本类型和 nullif (obj === null || typeof obj !== 'object') return obj;// 处理循环引用if (map.has(obj)) return map.get(obj);// 处理特殊对象(Date、RegExp 等)if (obj instanceof Date) return new Date(obj.getTime());if (obj instanceof RegExp) return new RegExp(obj);// 创建新对象/数组const newObj = Array.isArray(obj) ? [] : {};map.set(obj, newObj); // 记录已处理的对象// 递归复制所有属性for (let key in obj) {if (obj.hasOwnProperty(key)) {newObj[key] = deepCopy(obj[key], map);}}return newObj;
}
2. 浅拷贝和深拷贝的区别

回答要点:

  • 浅拷贝:只复制一层属性,引用类型共享内存地址。
  • 深拷贝:完全独立的新对象,递归复制所有层级。
  • 使用场景:浅拷贝适用于简单对象,深拷贝适用于需要完全隔离的复杂对象。
3. JSON.stringify () 的局限性

回答要点:

  • 无法处理函数、正则、Symbol、Date 等特殊对象。
  • 忽略 undefined 和循环引用。
  • 示例:
    const obj = {func: () => {},date: new Date(),nested: { prop: undefined }
    };
    const copy = JSON.parse(JSON.stringify(obj));
    console.log(copy); // { date: "2023-01-01T00:00:00.000Z", nested: {} }
    
4. 如何处理循环引用?

回答要点:

  • 使用 WeakMap 记录已处理的对象,避免递归时无限循环。
  • 示例代码(见手写深拷贝函数中的 map 参数)。
5. 实际应用场景
  • 浅拷贝:状态管理库(如 Redux)中的不可变数据更新、对象合并。
  • 深拷贝:游戏状态复制、复杂表单数据备份、避免副作用。

四)、总结

  • 浅拷贝:适用于单层对象,使用 Object.assign()、展开语法等。
  • 深拷贝:适用于复杂嵌套对象,推荐使用成熟库(如 Lodash)或手动递归实现。
  • 面试注意点:处理循环引用、特殊对象(如 Date、RegExp)、性能优化(避免过度递归)。

相关文章:

  • 口播视频怎么剪!利用AI提高口播视频剪辑效率并增强”网感”
  • MATLAB安装全攻略:常见问题与解决方案
  • Linux | Linux系统安装虚拟机教程(超级简单)
  • 以项目的方式学QT开发(一)——超详细讲解(120000多字详细讲解,涵盖qt大量知识)逐步更新!
  • 比亚迪跨界降维打击!将正式宣布跨界,进入两三轮电动车电池市场
  • 解决xxx.jar中没有主清单属性的问题
  • ubuntu18 设置静态ip
  • Milvus Docker 部署教程
  • 内网互通原则详解!
  • 设计并应用一个IIR-ButterWorth-Filter的例子
  • LLM笔记(三)位置编码(1)
  • 【Matlab】最新版2025a发布,深色模式、Copilot编程助手上线!
  • list简单模拟实现
  • AI时代的弯道超车之第十四章:AI与生活和生命的改变
  • 山火中的动态坐标:北斗头盔B3为逆行者铺就生命通道
  • CSRF 和 XSS 攻击分析与防范
  • 02、基础入门-Spring生态圈
  • 解决:npm install报错,reason: certificate has expired
  • go-zero(十八)结合Elasticsearch实现高效数据检索
  • 在线文档管理系统 spring boot➕vue|源码+数据库+部署教程
  • 再现五千多年前“古国时代”:凌家滩遗址博物馆今开馆
  • 泽连斯基启程前往土耳其
  • 订婚不等于性同意!山西订婚强奸案入选最高法案例
  • 违法违规收集使用个人信息,爱奇艺、轻颜等65款App被点名
  • 大英博物馆展歌川广重:他是梵高最钟爱的浮世绘名家
  • 微软宣布将裁员3%