Skip to content

JavaScript专题之深浅拷贝#32

@mqyqingfeng

Description

@mqyqingfeng

前言

拷贝也是面试经典呐!

数组的浅拷贝

如果是数组,我们可以利用数组的一些方法比如:slice、concat 返回一个新数组的特性来实现拷贝。

比如:

vararr=['old',1,true,null,undefined];varnew_arr=arr.concat();new_arr[0]='new';console.log(arr)// ["old", 1, true, null, undefined]console.log(new_arr)// ["new", 1, true, null, undefined]

用 slice 可以这样做:

varnew_arr=arr.slice();

但是如果数组嵌套了对象或者数组的话,比如:

vararr=[{old: 'old'},['old']];varnew_arr=arr.concat();arr[0].old='new';arr[1][0]='new';console.log(arr)// [{old: 'new'}, ['new']]console.log(new_arr)// [{old: 'new'}, ['new']]

我们会发现,无论是新数组还是旧数组都发生了变化,也就是说使用 concat 方法,克隆的并不彻底。

如果数组元素是基本类型,就会拷贝一份,互不影响,而如果是对象或者数组,就会只拷贝对象和数组的引用,这样我们无论在新旧数组进行了修改,两者都会发生变化。

我们把这种复制引用的拷贝方法称之为浅拷贝,与之对应的就是深拷贝,深拷贝就是指完全的拷贝一个对象,即使嵌套了对象,两者也相互分离,修改一个对象的属性,也不会影响另一个。

所以我们可以看出使用 concat 和 slice 是一种浅拷贝。

数组的深拷贝

那如何深拷贝一个数组呢?这里介绍一个技巧,不仅适用于数组还适用于对象!那就是:

vararr=['old',1,true,['old1','old2'],{old: 1}]varnew_arr=JSON.parse(JSON.stringify(arr));console.log(new_arr);

是一个简单粗暴的好方法,就是有一个问题,不能拷贝函数,我们做个试验:

vararr=[function(){console.log(a)},{b: function(){console.log(b)}}]varnew_arr=JSON.parse(JSON.stringify(arr));console.log(new_arr);

我们会发现 new_arr 变成了:

不能拷贝函数

浅拷贝的实现

以上三个方法 concat、slice、JSON.stringify 都算是技巧类,可以根据实际项目情况选择使用,接下来我们思考下如何实现一个对象或者数组的浅拷贝。

想一想,好像很简单,遍历对象,然后把属性和属性值都放在一个新的对象不就好了~

嗯,就是这么简单,注意几个小点就可以了:

varshallowCopy=function(obj){// 只拷贝对象if(typeofobj!=='object')return;// 根据obj的类型判断是新建一个数组还是对象varnewObj=objinstanceofArray ? [] : {};// 遍历obj,并且判断是obj的属性才拷贝for(varkeyinobj){if(obj.hasOwnProperty(key)){newObj[key]=obj[key];}}returnnewObj;}

深拷贝的实现

那如何实现一个深拷贝呢?说起来也好简单,我们在拷贝的时候判断一下属性值的类型,如果是对象,我们递归调用深拷贝函数不就好了~

vardeepCopy=function(obj){if(typeofobj!=='object')return;varnewObj=objinstanceofArray ? [] : {};for(varkeyinobj){if(obj.hasOwnProperty(key)){newObj[key]=typeofobj[key]==='object' ? deepCopy(obj[key]) : obj[key];}}returnnewObj;}

性能问题

尽管使用深拷贝会完全的克隆一个新对象,不会产生副作用,但是深拷贝因为使用递归,性能会不如浅拷贝,在开发中,还是要根据实际情况进行选择。

下期预告

难道到这里就结束了?是的。然而本篇实际上是一个铺垫,我们真正要看的是 jquery 的 extend 函数的实现,下一篇,我们会讲一讲如何从零实现一个 jquery 的 extend 函数。

专题系列

JavaScript专题系列目录地址:https://github.com/mqyqingfeng/Blog

JavaScript专题系列预计写二十篇左右,主要研究日常开发中一些功能点的实现,比如防抖、节流、去重、类型判断、拷贝、最值、扁平、柯里、递归、乱序、排序等,特点是研(chao)究(xi) underscore 和 jQuery 的实现方式。

如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果喜欢或者有所启发,欢迎 star,对作者也是一种鼓励。

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions