js Object.assign()函数以及深、浅拷贝

JavaScript07

js Object.assign()函数以及深、浅拷贝,第1张

object.assign():用于将源对象(source)中可枚举的属性复制到目标属性(target)中,并返回目标对象。

let a={a:1}let b={b:2}let c={c:3}

console.log(Object.assign(a,b,c))

//{ a: 1, b: 2, c: 3 }

浅拷贝:也叫引用拷贝,公用一块内存地址,一个改变另一个也改变;

深拷贝:创建新的内存地址保存值,与原对象完全独立。

深拷贝和浅拷贝只针对复杂的复杂的对象有差别;

注意:针对object.assign()而言,如果属性值是简单类型(number,string),通过object.assign({},src)得到的新对象是深拷贝对象;如果属性值为对象或其他引用类型,得到的新对象为浅拷贝对象。

对象类型在赋值的过程中实际上是复制了地址,从而导致了其中一方被改变其他也都被改变的情况,在开发中我们通常不希望出现这样的问题,这里可以使用浅拷贝来解决这个情况。

首先我们可以通过Object.assign来实现浅拷贝,该函数只会拷贝所有的属性值到新的对象中,如果属性值是对象的话,拷贝的是地址,即为浅拷贝而不是深拷贝。

以下为Object.assign浅拷贝的简单实现:

还可以通过展开运算符...来实现浅拷贝:

但是,浅拷贝只解决了第一层的问题,如果对象下还有对象的话,那么又回到最开始的问题了,第二层的对象拷贝过来的只是地址,两者享有相同的地址,这时就需要用到深拷贝了。

我们通常使用JSON.parse(JSON.stringify(object))来解决:

但是该方法具有以下局限性:

遇到函数、undefined和symbol时,会直接忽略掉他们,该对象不能正常的序列化,此时我们需要实现一个更为完善的深拷贝。

小结:以上深拷贝的方法依然只是较为简易的,要想实现一个比较完美的深拷贝其实是很困难的,需要我们考虑很多种边界情况,比如原型链如何处理、DOM如何处理等。该deepClone函数就有两个较为明显的问题,一是没有解决对象的循环引用的问题(参考方案:用弱映射做一个哈希表,存储原对象,若缓存命中,则过滤本次拷贝,直接使用记忆化数据,否则惰性拷贝。一般不是为了解决IE的兼容性问题,都没有问题,考虑兼容性则按需垫片。生产环境其实还需要考虑其它类型的拷贝,一般直接使用辅助工具库。总而言之,按需拷贝);二是无法实现函数的拷贝。

1.浅拷贝:复制一份引用,所有引用对象都指向一份数据,并且都可以修改这份数据。

2.深拷贝(复杂):复制变量值,对于非基本类型的变量,则递归至基本类型变量后,再复制。

一、数组的深浅拷贝

在使用JavaScript对数组进行操作的时候,我们经常需要将数组进行备份,事实证明如果只是简单的将它赋予其他变量,那么我们只要更改其中的任何一个,然后其他的也会跟着改变,这就导致了问题的发生。

var arr = ["One","Two","Three"] var arrto = arr arrto[1] = "test" document.writeln("数组的原始值:" + arr + "<br />")//Export:数组的原始值:One,test,Three document.writeln("数组的新值:" + arrto + "<br />")//Export:数组的新值:One,test,Three

像上面的这种直接赋值的方式就是浅拷贝,很多时候,这样并不是我们想要得到的结果,其实我们想要的是arr的值不变,不是吗?

方法一:js的slice函数

var arr = ["One","Two","Three"] var arrtoo = arr.slice(0) arrtoo[1] = "set Map" document.writeln("数组的原始值:" + arr + "<br />")//Export:数组的原始值:One,Two,Three document.writeln("数组的新值:" + arrtoo + "<br />")//Export:数组的新值:One,set Map,Three

方法二:js的concat方法

var arr = ["One","Two","Three"] var arrtooo = arr.concat() arrtooo[1] = "set Map To" document.writeln("数组的原始值:" + arr + "<br />")//Export:数组的原始值:One,Two,Three document.writeln("数组的新值:" + arrtooo + "<br />")//Export:数组的新值:One,set Map To,Three

二、对象的深浅拷贝

var a={name:'yy',age:26} var b=new Object() b.name=a.name b.age=a.age a.name='xx' console.log(b)//Object { name="yy", age=26} console.log(a)//Object { name="xx", age=26}

就是把对象的属性遍历一遍,赋给一个新的对象。

var deepCopy= function(source) { var result={} for (var key in source) { result[key] = typeof source[key]==='object'? deepCoyp(source[key]): source[key] }return result }

举一个jQuery中的例子:

jQuery.extend = jQuery.fn.extend = function() {//1.将extend方法扩展到JQ(函数)下边:扩展静态方法 //2. jQuery.fn.extend 把extend扩展到jq.fn下 且jQuery.fn = jQuery.prototype 扩展实例方法 // 1.2.功能相似 var options, name, src, copy, copyIsArray, clone, //定义一些变量 target = arguments[0] || {},//目标元素是【0】第一个元素$.extend( a , { name : 'hello' } , { age : 30 } ) i = 1, //第一个元素的位置 length = arguments.length,//第一个个对象的元素 deep = false//是否是深拷贝 默认 false不是 // Handle a deep copy situation 看是不是深拷贝情况 if ( typeof target === "boolean" ) { //是布尔值deep = target target = arguments[1] || {}//目标元素是第二个$.extend( true , a , b ) // skip the boolean and the target i = 2 } // Handle case when target is a string or something (possible in deep copy) 看参数正确不 if ( typeof target !== "object" &&!jQuery.isFunction(target) ) { // 当目标不是对象或者不是函数的时候 target = {}//变成一个空的jason } // extend jQuery itself if only one argument is passed看是不是插件情况 if ( length === i ) { //只写了一个对象 要把这个对象扩展到jq源码上 静态方法 或者是实例方法 target = this//this 是$ 或者 $() --i } // 可能有多个对象情况 for ( i <lengthi++ ) { // Only deal with non-null/undefined values if ( (options = arguments[ i ]) != null ) {//看后边的对象是否都有值 // Extend the base object for ( name in options ) { src = target[ name ] copy = options[ name ]// Prevent never-ending loop if ( target === copy ) {//防止循环引用 continue//跳出本次循环继续执行// $.extend( a , { name : a } ) )循环引用 a也是一个对象 } // Recurse if we're merging plain objects or arrays深拷贝 if ( deep &&copy &&( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {// 是深拷贝 且需有var b = { name : { age : 30 } }且b必须是对象自变量(jason) 或者是个数组//递归if ( copyIsArray ) { //数组copyIsArray = false clone = src &&jQuery.isArray(src) ? src : []//定义一个空数组 } else {//jasonclone = src &&jQuery.isPlainObject(src) ? src : {}//看原有的属性有没有且是不是jason定义一个空jason}// var a = { name : { job : 'it' } }看有没有原有的属性 有的话在原有的上边添加// var b = { name : {age : 30} } // $.extend( true , a , b )//a继承b// console.log( a )a{ name:{ job : 'it' ,age : 30}} 如果只有一个{} 则只有,age : 30// Never move original objects, clone(a) themtarget[ name ] = jQuery.extend( deep, clone, copy )//调用函数本身进行进一步的递归处理 // Don't bring in undefined values浅拷贝 } else if ( copy !== undefined ) {target[ name ] = copy//直接复制因为里边没有对象 } } } } // Return the modified object return target }