深拷贝和浅拷贝问题的本质还是不同数据类型的存储方式差异,尤其是引用数据类型的特殊。
现分别对赋值、浅拷贝、深拷贝做深入研究:
1.赋值
原理:直接将对象指针直接赋值给另一个变量
代码:
let developer = { title: 'Frontend', basic: { html: '5', css: '3', js: 'es6' }, frameworks: ['React', 'Vue', 'AngularJS'], summary: function(){ console.log('I am FE developer'); }};let newDeveloper = developer;console.log(newDeveloper);//基本类型:改变原对象newDeveloper.title = 'Frontend Leader';console.log(developer.title); // Frontend Leader//对象:改变原对象newDeveloper.basic.http = '2.0';console.log(developer.basic.http); // 2.0newDeveloper.basic.js = 'es5';console.log(developer.basic.js); // es5//数组:改变原对象newDeveloper.frameworks.push('Angular');console.log(developer.frameworks); // [ 'React', 'Vue', 'AngularJS', 'Angular' ]//函数:改变原对象newDeveloper.summary = function () { console.log('I like FE development');};developer.summary(); // I like FE development
2.浅拷贝
原理:遍历对象的每个属性进行逐个拷贝
实现方式:
- 方式1:遍历并复制
- 方式2:Object.assign()
代码:
let developer = { title: 'Frontend', basic: { html: '5', css: '3', js: 'es6' }, frameworks: ['React', 'Vue', 'AngularJS'], summary: function(){ console.log('I am FE developer'); }};/** 方式1:逐个复制** */function cloneInShallow(source) { let target = {}; for (prop in source){ target[prop] = source[prop]; } return target}let newDeveloper = cloneInShallow(developer);/** 方式2:Object.assign()** */// let newDeveloper = Object.assign({}, developer);console.log(newDeveloper);//基本类型:不改变原对象newDeveloper.title = 'Frontend Leader';console.log(developer.title); // Frontend// 对象:改变原对象newDeveloper.basic.http = '2.0';console.log(developer.basic.http); // 2.0newDeveloper.basic.js = 'es5';console.log(developer.basic.js); // es5//数组:改变原对象newDeveloper.frameworks.push('Angular');console.log(developer.frameworks); // [ 'React', 'Vue', 'AngularJS', 'Angular' ]//函数:不改变原对象newDeveloper.summary = function () { console.log('I like FE development');};developer.summary(); // I am FE developer
3.深拷贝
原理:使用递归,遍历每一个对象属性进行拷贝
实现方式:
- 方式1: 纯手工打造回调函数
- 方式2: JSON.parse(JSON.stringify(obj))
- 方式3: 借助jQuery
- 方式4: 借助lodash
代码:
let developer = { title: 'Frontend', basic: { html: '5', css: '3', js: 'es6' }, frameworks: ['React', 'Vue', 'AngularJS', {node: 'express'}], summary: function(){ console.log('I am FE developer'); }};/** 方式1: 纯手工打造* */function cloneInDeep(source) { if(source && typeof source === 'object'){ let target = {}; for (let prop in source){ let value = source[prop]; if(Array.isArray(value)){ let newArray = []; value.forEach(function (item, index) { if(Array.isArray(item) || Object.getPrototypeOf(item) === Object.prototype){ newArray.push(cloneInDeep(item)) }else{ newArray.push(item) } }); target[prop] = newArray; }else if(Object.getPrototypeOf(value) === Object.prototype){ target[prop] = cloneInDeep(value); }else{ target[prop] = value; } } return target }else{ throw new Error('source is not object!') }}let newDeveloper = cloneInDeep(developer);/** 方式2: JSON.parse(JSON.stringify(obj))* 弊端:会抛弃对象的constructor* 适用:能够被json直接表示的数据结构,对象中只包含number、string、boolean、array、扁平对象* 不适用:含有function、regexp* */// let newDeveloper = JSON.parse(JSON.stringify(developer));/** 方式3: jQuery* */let $ = require('jquery');// let newDeveloper = $.extend({}, developer);/** 方式4: lodash* */let _ = require('lodash');// let newDeveloper = _.cloneDeep(developer);console.log(newDeveloper);//基本类型:不改变原对象newDeveloper.title = 'Frontend Leader';console.log(developer.title); // Frontend// 对象:不改变原对象newDeveloper.basic.http = '2.0';console.log(developer.basic.http); // undefinednewDeveloper.basic.js = 'es5';console.log(developer.basic.js); // es6//数组:不改变原对象newDeveloper.frameworks.push('Angular');console.log(developer.frameworks); // [ 'React', 'Vue', 'AngularJS' , { node: 'express' } ]newDeveloper.frameworks[3].node = 'koa';console.log(developer.frameworks); // [ 'React', 'Vue', 'AngularJS' , { node: 'express' } ]//函数:不改变原对象newDeveloper.summary = function () { console.log('I like FE development');};developer.summary(); // I am FE developer
涉及的知识点:
- 数据类型及存储机制
- for...in...遍历,枚举属性
- 递归
- 对象和数组的判断