深拷贝
Notion – The all-in-one workspace for your notes, tasks, wikis, and databases.
var a = {name:1, sya:function(){ console.log("打印")},age:undefined}; // {name:1,sya:f}var b = JSON.parse(JSON.stringify(a)); // {name:1}
function Obj() {this.func = function () {alert(1)};this.obj = {a: 1};this.arr = [1, 2, 3];this.und = undefined;this.reg = /123/;this.date = new Date(0);this.NaN = NaN;this.infinity = Infinity;this.sym = Symbol(1);}let obj1 = new Obj();Object.defineProperty(obj1, 'innumerable', {enumerable: false,value: 'innumerable'});console.log('obj1', obj1);let str = JSON.stringify(obj1);let obj2 = JSON.parse(str);console.log('obj2', obj2);
// 深拷贝
var sourceObj = {name: 'tt',age: 18,job: 'web',friends: ['t1', 't2']
}// util作为判断变量具体类型的辅助模块
var util = (function() {var class2Type = {}var objTypes = ["Null","Undefined","Number","Boolean","String","Object","Function","Array","RegExp","Date”]objTypes.forEach(function(item) {class2Type['[object ' + item + ']'] = item.toLowerCase()})function isType(obj, type) {return getType(obj) === type}function getType(obj) {return class2type[Object.prototype.toString.call(obj)] || 'object'}return {isType: isType,getType: getType}
})()// deep参数用来判断是否是深度复制
function copy(obj, deep){// 如果obj不是对象,那么直接返回值就可以了if(obj == null || typeof obj !== 'object'){return obj}// 定义需要的局部变量,根据obj的类型来调整target的类型var i,target = util.isType(obj,"array") ? [] : {},value,valueTypefor(i in obj){value = obj[i]valueType = util.getType(value)// 只有在明确执行深复制,并且当前的value是数组或对象的情况下才执行递归复制if(deep && (valueType === "array" || valueType === "object")){target[i] = copy(value)}else{target[i] = value}}return target
}var targetObj = copy(sourceObj, true);
targetObj.friends.push ('t3');
console.log(sourceObj) // ['t1', 't2']
// 深拷贝
Object.prototype.clone = function() {var Constructor = this.constructorvar obj = new Constructor()for (var attr in this) {if (this.hasOwnProperty(attr)) {if (typeof this[attr] !== 'function') {if (this[attr] === null) {obj[attr] = null} else {obj[attr] = this[attr].clone()}}}}return obj
}/* Method of Array*/
Array.prototype.clone = function() {var thisArr = this.valueOf()var newArr = []for (var i = 0; i < thisArr.length; i++) {newArr.push(thisArr[i].clone())}return newArr
}
/* Method of Boolean, Number, String*/
Boolean.prototype.clone = function() {return this.valueOf()
}
Number.prototype.clone = function() {return this.valueOf()
}
String.prototype.clone = function() {return this.valueOf()
}
/* Method of Date*/
Date.prototype.clone = function() {return new Date(this.valueOf())
}
/* Method of RegExp*/
RegExp.prototype.clone = function() {var pattern = this.valueOf()var flags = ''flags += pattern.global ? 'g' : ''flags += pattern.ignoreCase ? 'i' : ''flags += pattern.multiline ? 'm' : ''return new RegExp(pattern.source, flags)
}
function deepClone(source){// 判断复制的目标是数组还是对象const targetObj = source.constructor === Array ? [] : {}; for(let keys in source){ // 遍历目标if(source.hasOwnProperty(keys)){if(source[keys] && typeof source[keys] === 'object'){ targetObj[keys] = source[keys].constructor === Array ? [] : {};targetObj[keys] = deepClone(source[keys]);}else{ // 如果不是,就直接赋值targetObj[keys] = source[keys];}} }return targetObj;
}
//测试一下
var a = {name:1, sya:function(){ console.log("打印")},age:undefined};
var b = deepClone(a);
b.name=2;
console.log(a)//{name:1,sya:f,age:undefined}
console.log(b)//{name:2,sya:f,age:undefined}// 深拷贝函数并不能复制不可枚举的属性以及 Symbol 类型;
// 这种方法只是针对普通的引用类型的值做递归复制,而对于 Array、Date、RegExp、Error、Function 这样的引用类型并不能正确地拷贝;
// 对象的属性里面成环,即循环引用没有解决。
针对能够遍历对象的不可枚举属性以及 Symbol 类型,我们可以使用 Reflect.ownKeys 方法;
当参数为 Date、RegExp 类型,则直接生成一个新的实例返回;
利用 Object 的 getOwnPropertyDescriptors 方法可以获得对象的所有属性,以及对应的特性,顺便结合 Object 的 create 方法创建一个新对象,并继承传入原对象的原型链;
利用 WeakMap 类型作为 Hash 表,因为 WeakMap 是弱引用类型,可以有效防止内存泄漏,作为检测循环引用很有帮助,如果存在循环,则引用直接返回 WeakMap 存储的值。
const isComplexDataType = obj => (typeof obj === 'object' || typeof obj === 'function') && (obj !== null);const deepClone = function (obj, hash = new WeakMap()) {if (obj.constructor === Date) return new Date(obj) // 日期对象直接返回一个新的日期对象if (obj.constructor === RegExp) return new RegExp(obj) // 正则对象直接返回一个新的正则对象if (hash.has(obj)) return hash.get(obj) // 如果循环引用了就用 weakMap 来解决let allDesc = Object.getOwnPropertyDescriptors(obj); // 遍历传入参数所有键的特性 // ts报错// (Object).getOwnPropertyDescriptors(obj); // 遍历传入参数所有键的特性// (Object as any).getOwnPropertyDescriptors(obj); // 遍历传入参数所有键的特性let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc); //继承原型链hash.set(obj, cloneObj);for (let key of Reflect.ownKeys(obj)) {cloneObj[key] = (isComplexDataType(obj[key]) && typeof obj[key] !== 'function') ? deepClone(obj[key], hash) : obj[key]}return cloneObj}// 下面是验证代码let obj = {num: 0,str: '',boolean: true,unf: undefined,nul: null,obj: {name: '我是一个对象',id: 1},arr: [0, 1, 2],func: function () {console.log('我是一个函数')},date: new Date(0),reg: new RegExp('/我是一个正则/ig'),[Symbol('1')]: 1,};Object.defineProperty(obj, 'innumerable', {enumerable: false,value: '不可枚举属性'});obj = Object.create(obj, Object.getOwnPropertyDescriptors(obj))obj.loop = obj // 设置loop成循环引用的属性let cloneObj = deepClone(obj)cloneObj.arr.push(4)console.log('obj', obj)console.log('cloneObj', cloneObj)
// 深拷贝 lodash _.cloneDeep()var _ = require("lodash");var obj1 = {a: 1,b: { f: { g: 1 } },c: [1, 2, 3],};var obj2 = _.cloneDeep(obj1);console.log(obj1.b.f === obj2.b.f);
// 深拷贝 jQuery.extend()
// $.extend(deepCopy, target, object1, [objectN])//第一个参数为true,就是深拷贝var $ = require("jquery");var obj1 = {a: 1,b: { f: { g: 1 } },c: [1, 2, 3],};var obj2 = $.extend(true, {}, obj1);console.log(obj1.b.f === obj2.b.f);
MessageChannel 方式
// 深度拷贝 使用 MessageChannel 方式function structuralClone(obj) {return new Promise((resolve) => {const { port1, port2 } = new MessageChannel();port2.onmessage = (ev) => resolve(ev.data);port1.postMessage(obj);});}const obj = {name: 'SeriousLose',obj: {name: 'fly',},};structuralClone(obj).then((res) => {console.log(res);});
History API 方式
// history.replaceState 方式function structuralClone(obj) {const oldState = history.state;history.replaceState(obj, document.title);const copy = history.state;history.replaceState(oldState, document.title);return copy;}var obj = {};var b = { obj };obj.b = b;var copy = structuralClone(obj);console.log(copy);
Notification API
// Notification APIfunction structuralClone(obj) {return new Notification('', { data: obj, silent: true }).data;}var obj = {};var b = { obj };obj.b = b;var copy = structuralClone(obj);console.log(copy);
考验能力
基础编码能力
递归编码能力
代码的严谨性
代码抽象能力
js编码能力
熟练掌握WeakMap的能力
引用类型各种AP的熟练程度
准确判断JS各种数据类型的能力
考虑问题全面性
边界情况
解决循环引用
// 练习题1
var a = {n: 1}
var b = a;
a.x = a = {n: 2}
console.log(a.x); // undefined
console.log(b.x); // {n: 2}
堆内存 | ||
---|---|---|
var a = {n: 1} | a | {n: 1} |
var b = a; | a、b | {n: 1} |
.的优先级高于=号,先计算. a.x = a = {n:2} | ||
a.x = a = {n: 2} | a、b | {n: 1,x=a={n:2}} |
a={n:2} 开辟新堆内存,a引用地址改变;b引用地址未改变 | ||
x=a={n:2} | a | {n:2} |
b | {n:1,x:{n:2}} |
// 练习题2
var a = {n: 1}
var b = a;
b.n = a.f = {n: 2};
console.log(a); // {n:{n:2},f:{n:2}}
console.log(b); // {n:{n:2},f:{n:2}}
堆内存 | ||
---|---|---|
var a = {n: 1} | a | {n: 1} |
var b = a; | a、b | {n: 1} |
.的优先级高于=号,先计算. b.n = a.f = {n:2} | ||
b.n = a.f = {n:2} | a、b | {n=a.f={n:2}} |
a.f = {n:2} | a、b | {n:{n:2},f:{n:2}} |
一文读懂 javascript 深拷贝与浅拷贝