对象的拷贝在js中比较重要,因为js不同与C++等,没有指针操作,基本类型都是没有引用的,只有对象和函数才有引用,这就造成当我们在拷贝一个对象时,可能会有深浅拷贝之分。
浅拷贝的意思就是只复制引用(指针),而未复制真正的值。而深拷贝则是改变对象的引用,深拷贝形成的新对象和原来的对象有不一样的地址。
比如一个对象的定义如下:
var a = {
b: 3
}
这时这个对象只有一层,我们可以用很多中方法都可以实现对象的拷贝,比如Object.assign({}, a)来实现对象的拷贝,因为对象第一层中的值没有引用。
Object.assign()
方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
但是如果遇到复杂对象,则必须要用到深拷贝,比如我们在react中的setState中的新状态必须要重新生成一个新的对象(地址),不能有副作用(不能改变原来对象的值)。
此时有很多种方法都可以实现对象的拷贝:
1. JSON.parse(JSON.stringify(obj))
序列化能解决大部分对象拷贝的问题,但是有很多缺点:
(1)会忽略undefined
(2)不能序列化函数
(3)不能解决循环引用的对象
2.MessageChannel
Channel Messaging API的MessageChannel
接口允许我们创建一个新的消息通道,并通过它的两个MessagePort
属性发送数据。
我们可以根据这个新的消息通道拷贝一个新的对象,能解决序列化不能忽略undefined和循环引用的问题。
唯一缺点是:不能序列化函数。
function cloneDeep(obj){
retrun Promise(resolve => {
const {port1, port2} = new MessageChannel();
port2.onmessage = ev => resolve)(ev.data);
prot1.postMessage(obj);
})
}
//注意这是异步函数
var obj = {
a: 1,
b: {
c: b
}
}
const cloneObject = await cloneDeep(obj)
3.使用lodash库中的cloneDeep函数
loadsh cloneDeep,在webpack中需要按需引入,否则打包过大,
使用方式有三种:
(1)使用时按需引入
import { deepClone } from 'lodash/deepClone';
(2)使用插件优化
lodash-webpack-plugin
安装后在webpack中配置
var LodashModuleReplacementPlugin = require('lodash-webpack-plugin');
var webpack = require('webpack');
module.exports = {
module: {
rules: [
{
use: 'babel-loader',
test: /\.js$/,
exclude: /node_modules/,
options: {
plugins: ['lodash'],
presets: [['env', {modules: false, targets: {node: 4}}]]
}
}
]
},
plugins: [new LodashModuleReplacementPlugin(), new webpack.optimize.UglifyJsPlugin()]
};
(3)使用 lodash-es
tree-shaking 作为 rollup 的一个杀手级特性,能够利用 ES6 的静态引入规范,减少包的体积,避免不必要的代码引入,webpack2 也很快引入了这个特性。
要用到 tree-shaking,必然要保证引用的模块都是 ES6 规范的。lodash-es 是着具备 ES6 模块化的版本,只需要直接引入就可以。
import {isEmpty, isObject, cloneDeep} from 'lodash-es';
4.jQuery的extend深拷贝和浅拷贝
浅层复制(只复制顶层的非 object 元素)
var newObject = jQuery.extend({}, oldObject);
深层复制(一层一层往下复制直到最底层)
var newObject = jQuery.extend(true, {}, oldObject);
除此以外,还有很多其他优秀的库也实现了深拷贝,这里就不一一举例。
在gitHub上有人分析了lodash的源码中关于deepClone的实现,有兴趣的可以再深入了解。
https://github.com/moyui/BlogPosts/blob/master/2018/lodash%E6%B7%B1%E6%8B%B7%E8%B4%9D%E6%BA%90%E7%A0%81%E6%8E%A2%E7%A9%B6.md