1、什么是深拷贝和浅拷贝? 如何实现深浅拷贝?
首先我们先来理解一下什么是浅拷贝和深拷贝
什么是深拷贝和浅拷贝?
- 深拷贝
- 创建一个
新对象
, 拷贝对象的所有属性, 如果属性是基本数据
, 拷贝的就是基本数据
的值; 如果是引用数据
, 则需要重新分配一块内存, 拷贝该引用数据
的所有属性, 然后将引用地址
赋值给对应的属性, 如果该引用数据
中某个属性也是引用数据
则需要继续一层层递归拷贝……
- 创建一个
- 浅拷贝
- 会新建一个对象, 拷贝对象的所有属性值, 对于
基本数据
来说就是拷贝一份对应的值, 但是对于引用数据
则是拷贝一份引用数据
的引用地址 。
- 会新建一个对象, 拷贝对象的所有属性值, 对于
那么我们应该怎么去实现呢?
-
实现深拷贝
-
简单版本
-
// 对象的深克隆 function cloneObj(obj,type){ type = type || "object" var clone = (type === "object" ? {}: []); for(var attr in obj){ // 判断数组项是不是对象 // 1. typeof对象必须是object // 2. instanceof不能是Array if(typeof obj[attr] === "object" && !obj[attr] instanceof Array){ // 这是对象类型 clone[attr] = cloneObj(obj[attr]); }else if(typeof obj[attr] === "object" && obj[attr] instanceof Array){ // 这是数组类型 clone[attr] = cloneObj(obj[attr],"array"); }else { // 这是基本类型 clone[attr] = obj[attr] } } return clone }
-
复杂版本
-
// map 用于记录出现过的对象, 解决循环引用 const deepClone = (target, map = new WeakMap()) => { // 1. 对于基本数据类型(string、number、boolean……), 直接返回 if (typeof target !== 'object' || target === null) { return target } // 2. 函数 正则 日期 MAP Set: 执行对应构造器函数, 返回新的对象 const constructor = target.constructor if (/^(Function|RegExp|Date|Map|Set)$/i.test(constructor.name)) { return new constructor(target) } // 3. 借用WeakMap来记录每次复制过的对象, 在递归过程中, 如果遇到已经复制过的对象, 则直接使用上次拷贝的对象, 不重新拷贝 if (map.get(target)) { return map.get(target) } // 4. 创建新对象 const cloneTarget = Array.isArray(target) ? [] : {} map.set(target, cloneTarget) // 5. 循环 + 递归处理 Object.keys(target).forEach(key => { cloneTarget[key] = deepClone(target[key], map); }) // 6. 返回最终结果 return cloneTarget }
-
JSON.parse(JSON.stringify())
-
利用
JSON.stringify
将对象转成JSON
字符串, 再用JSON.parse
把字符串解析成对象, 如此一来一去就能够实现引用数据
的一个深拷贝 -
const obj = { age: 18, name: 'lucky', } const res = JSON.parse(JSON.stringify(obj))
-
注意事项:
NaN
Infinity
-Infinity
会被序列化为null
Symbol
undefined
function
会被忽略(对应属性会丢失)Date
将得到的是一个字符串- 拷贝
RegExp
Error
对象,得到的是空对象{}
-
使用的是 lodash 这个库里面的一个 _.cloneDeep()
-
jQuery.extend() 通过jquery 这个可以实现
-
使用structuredClone()方法,这是一个新的
API
可用于对数据进行深拷贝
, 同时还支持循环引用 。
-
-
实现浅拷贝
- 通过Object.assign()实现
- 展开运算符
- 对于数组可以使用, 数组的一些方法进行拷贝, 比如:
Array.prototype.concat()
Array.prototype.slice()
Array.from
等方法, 它们的特点都是不改变原数组、同时返回一个新的数组 - 可以使用一些第三方库提供的工具方法来实现拷贝, 比如: lodash 中的
_clone
方法
2、js中原始数据类型有哪些?
Undefined、Null、Boolean、Number、String、Object、Symbol、BigInt
3、什么是原型和原型链?
- 原型
- 在js中,每个构造函数内部都有一个prototype属性,该属性的值是个对象,该对象包含了该构造函数所有实例共享的属性和方法。当我们通过构造函数创建对象的时候,在这个对象中有一个指针,这个指针指向构造函数的prototype的值,我们将这个指向prototype的指针称为原型。
- 原型链
- 实例对象在查找属性时,如果查找不到,就会沿着__ proto __ 去与对象关联的原型上查找,如果还查找不到,就去找原型的原型,直至查到最顶层,这也就是原型链的概念。
4、什么是JavaScript?
JavaScript是一门跨平台、面向对象的脚本语言(不需要编译,直接解释运行即可),来控制网页的行为,它能使网页可交互
W3C标准:网页主要由三部分构成
- 结构:HTML
- 表现:CSS
- 行为:JavaScript
5、null和undefined有什么区别?
首先 undefined 和 Null 都是基本数据类型,undefined 代表的含义是未定义,null 代表的含义是空对象。一般变量声明了但还没有定义的时候会返回 undefined,null主要用于赋值给一些可能会返回对象的变量,作为初始化。