深拷贝、浅拷贝的实现
深拷贝、浅拷贝实现
JavaScript的数据类型分为基本数据类型和引用数据类型。
5种基本数据类型Undefined、Null、Boolean、Number 和 String,变量是直接按值存放的,存放在栈内存中的简单数据段,可以直接访问。
检测对象的判断
function isObject(obj) { return Object.prototype.toString.call(obj) === '[object Object]'; }
不太适合本文,本文包括数组
[]和{} ,null的都是object
可以用
function isObject(obj) { return typeof obj === 'object' && obj != null; }
也可以用如果值是对象用下面
source[keys] && typeof source[keys] === 'object'
对于基本数据类型的拷贝,并没有深浅拷贝的区别,我们所说的深浅拷贝都是对于引用数据类型而言的。
浅拷贝的意思就是只复制引用,而未复制真正的值。
const originArray = ['火影忍着','海贼王','名侦探柯南']; const originObj = { name: 'sophia', age: 18, hobby: ['swimming', 'shopping', [1, 2, [ 'a', 'b']]], book: { title: "Node.js实战", price: "69", content: ['1章', '2章'] }, print: function(){ console.log('--print-->', this.name) }, t1: undefined, t2: null, t3: 123 };
一、浅拷贝的实现方式
1、模拟一个浅拷贝函数
遍历对象,然后把属性和属性值都放在一个新的对象
var shallowCopy = function(obj) { // 只拷贝对象 if (typeof obj !== 'object') return; // 根据obj的类型判断是新建一个数组还是对象 var newObj = obj instanceof Array ? [] : {}; // 遍历obj,并且判断是obj的属性才拷贝 for (var key in obj) { if (obj.hasOwnProperty(key)) { newObj[key] = obj[key]; } } return newObj; }
二、深拷贝实现
目前实现深拷贝的方法不多,主要是两种:
1、利用 JSON 对象中的 parse 和 stringify
利用递归来实现每一层都重新创建对象并赋值
const originArray = [1,2,3,4,5]; const cloneArray = JSON.parse(JSON.stringify(originArray)); console.log(cloneArray === originArray); // false const originObj = {a:'a',b:'b',c:[1,2,3],d:{dd:'dd'}}; const cloneObj = JSON.parse(JSON.stringify(originObj)); console.log(cloneObj === originObj); // false cloneObj.a = 'aa'; cloneObj.c = [1,1,1]; cloneObj.d.dd = 'doubled'; console.log(cloneObj); // {a:'aa',b:'b',c:[1,1,1],d:{dd:'doubled'}}; console.log(originObj); // {a:'a',b:'b',c:[1,2,3],d:{dd:'dd'}};
但是有缺陷
undefined、function、symbol 会在转换过程中被忽略,就是说如果对象中含有一个函数时(很常见),就不能用这个方法进行深拷贝
const originObj = { name:'axuebin', sayHello:function(){ console.log('Hello World'); } } console.log(originObj); // {name: "axuebin", sayHello: ƒ} const cloneObj = JSON.parse(JSON.stringify(originObj)); console.log(cloneObj); // {name: "axuebin"}
另外数组有两个方法 concat 和 slice 是可以实现对原数组的拷贝的,这两个方法都不会修改原数组,而是返回一个修改后的新数组,同时,ES6 中 引入了 Object.assgn 方法和 ... 展开运算符也能实现对对象的拷贝。但他们对于多层数组不适用,只拷贝第一层数组
2、递归的方法
递归的思想就很简单了,就是对每一层的数据都实现一次 创建对象->对象赋值 的操作,简单粗暴上代码
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; }