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

JavaScript010

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)得到的新对象是深拷贝对象;如果属性值为对象或其他引用类型,得到的新对象为浅拷贝对象。

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 }

先说一下浅拷贝的实现方式

ES6 中 Object 的一方法,可以是来合并多个JS对象(能用来实现浅拷贝)

第一个参数拷贝的目标对象,后面的参数是拷贝的来源对象

利用扩展运算法,可以实现浅拷贝的的功能。

手写浅拷贝的思路:

将一个对象从内存中完整的拷贝出来给目标独对象,并新开辟一个全新的内存空间存放对象,新对象的修改并不会改变原对象,实现真正的分离。

最简单的深拷贝的方法,就是把一个对象序列化成为 JSON 的字符串,并将对象里面的内容转成字符串,最后用 JSON.parse() 将 JSON 字符串生成一个新的对象。

但是 JSON.stringify 实现深拷贝有些地方需要注意:

这只是简版的,利用递归的方式实现深拷贝,同 JSON.stringify 效果一样,还是有些问题没有解决

即使修改 obj.data 下的属性,也是跟之前的没有关系