javascript中的对象拷贝

js中的数据类型

在介绍javascript中的对象的拷贝以前,我先介绍一个基础的东西,javascript中的数据类型。javascript

咱们作前端的应该都知到在es6 以前,javascript中的数据类型BooleanNumberStringUndefinedObjectNull,后来在es6 中又引入了一种新的数据类型为:Symbol。而这些数据类型又被分为基本数据类型和引用数据类型,基本数据类型存储在栈中;引用数据类型存储在堆中。其中基本数据类型包括有:BooleanNumberStringUndefinedNull以及刚刚提到的新的数据类型Symbol,引用类型包括:ObjectFunctionArray。我为甚么会提到FunctionArray主要是由于我么在对象的深拷贝过程当中须要对这两种数据类型进行特殊处理。前端

什么是对象的拷贝

介绍完了js中的数据类型以后,来看一下拷贝,什么是对象的拷贝,说白了就是复制,将原来的东西给复制一份。就好比说,将磁盘上的一个文件给拷贝一份,就是将磁盘中的文件给复制了一个如出一辙的新的文件。就好比说下面的例子;java

var a = 123;
var b = a;
var c = {name: 'zhangsan', age: 18};
var d = c;

var e = {};
for (var key in c) {
  if (c.hasOwnProperty(key)) {
    e[key] = c[key];
  }
}

对象的拷贝又被分为深拷贝和浅拷贝。es6

浅拷贝

就上面的代码来解释,咱们看最后一个拷贝,就是咱们将变量c中全部的属性而后赋值给变量e,这种状况不出问题的前提就是咱们定义的对象a中的全部的成员都为基本类型,而非引用类型,一旦存在有引用类型的成员,这个时候的拷贝是将成员变量的地址赋给拷贝过去了,而,成员变量地址指向的真实的引用依旧是同一引用,所以,当指向中的引用内容发生变化时,一样会两个对象中的成员也会发生一样的改变;函数

好比说下面的这个例子:oop

var a = {
  name: 'zhangsan',
  age: 28,
  children: [1,2,3,4,5],
  son: {
    name: 'zhangsi',
    age: 1
  }
}

var shallowCopy = function (obj) {
  var newObj = {};
  for (var key in obj) {
    if (obj.hasOwnProperty(key)) {
      newObj[key] = obj[key];
    }
  }
  return newObj;
}

var b = shallowCopy(a);
console.log(a.children[0]);
console.log(a.son.name);
console.log(b.children[0]);
console.log(b.son.name);
a.children[0] = 22;
a.son.name = 'name';

console.log(b.children[0]);
console.log(b.son.name);

在上面的这个例子中,我在后面改变了achildren以及a.som.name,咱们会发现这个状况下面,b相对应的内容也发生了变化。并未发生实际上的拷贝,这就是浅拷贝。优化

深拷贝

在了解了浅拷贝的基础上,咱们再来深刻的了解一下深拷贝,有些时候咱们是须要进行深拷贝的,这个时候复制的就不单单是一个引用地址,而是一个真实的引用内容。基于这个理论,就能够在复制的过程当中加入一个判断,判断所要复制的量是引用类型仍是基本类型,若是是基本类型的话,就直接赋值过去,若是是引用类型的话,就须要继续对引用类型进行拷贝。this

所以咱们将对上面浅拷贝的代码进修改以下:spa

var a = {
  name: 'zhangsan',
  age: 28,
  children: [1,2,3,4,5],
  son: {
    name: 'zhangsi',
    age: 1
  }
}

var deepCopy = function(obj) {
  if (typeof obj !== 'object') return;
  var newObj = obj instanceof Array ? [] : {};
  for (var key in obj) {
      if (obj.hasOwnProperty(key)) {
          newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key];
      }
  }
  return newObj;
}

var b = deepCopy(a);
console.log(a.children[0]);
console.log(a.son.name);
console.log(b.children[0]);
console.log(b.son.name);
a.children[0] = 22;
a.son.name = 'name';

console.log(b.children[0]);
console.log(b.son.name);

咱们可以看到,最后的输出和浅拷贝的输出是不同的,输出的依旧是以前的以前的值,而不是发生变化的值。固然上面的这个深拷贝的例子还仅仅制止一个基础的,还不够完善,仅仅可以完成基本的对象拷贝。具体的实现咱们能够参考的还有不少。prototype

实现深拷贝的经常使用方法

  • jQuery 中的实现

    在jQuery中,深浅拷贝是使用的同一个方法就是extend,这个函数的第一个参数是表示此次的拷贝是深拷贝仍是浅拷贝,若是要进行深拷贝的话,则传入true,不然不传或者是传false。具体的实现内容以下:

    jQuery.extend = jQuery.fn.extend = function() {
    	var options, name, src, copy, copyIsArray, clone,
    		target = arguments[ 0 ] || {},
    		i = 1,
    		length = arguments.length,
    		deep = false;
    
    	// Handle a deep copy situation
    	if ( typeof target === "boolean" ) {
    		deep = target;
    
    		// Skip the boolean and the target
    		target = arguments[ i ] || {};
    		i++;
    	}
    
    	// Handle case when target is a string or something (possible in deep copy)
    	if ( typeof target !== "object" && typeof target !== "function" ) {
    		target = {};
    	}
    
    	// Extend jQuery itself if only one argument is passed
    	if ( i === length ) {
    		target = this;
    		i--;
    	}
    
    	for ( ; i < length; i++ ) {
    
    		// Only deal with non-null/undefined values
    		if ( ( options = arguments[ i ] ) != null ) {
    
    			// Extend the base object
    			for ( name in options ) {
    				copy = options[ name ];
    
    				// Prevent Object.prototype pollution
    				// Prevent never-ending loop
    				if ( name === "__proto__" || target === copy ) {
    					continue;
    				}
    
    				// Recurse if we're merging plain objects or arrays
    				if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
    					( copyIsArray = Array.isArray( copy ) ) ) ) {
    					src = target[ name ];
    
    					// Ensure proper type for the source value
    					if ( copyIsArray && !Array.isArray( src ) ) {
    						clone = [];
    					} else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) {
    						clone = {};
    					} else {
    						clone = src;
    					}
    					copyIsArray = false;
    
    					// Never move original objects, clone them
    					target[ 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;
    };

    具体的解析变就不作过于深刻的介绍,这里的思路其实就是咱们上面所介绍的思路的一个优化,将各类状况都帮咱们给考虑清楚了。

相关文章
相关标签/搜索