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;
    };

    具體的解析變就不作過於深刻的介紹,這裏的思路其實就是咱們上面所介紹的思路的一個優化,將各類狀況都幫咱們給考慮清楚了。

相關文章
相關標籤/搜索