一、曾經在讀JQ源碼的時候,對深拷貝算是有了一點的理解。我們在項目中是不是經常會遇到這樣的問題呢? 後臺返回一個數組對象(引用類型).次數在頁面渲染中需要對部分數據進行處理 比如:銀行卡62345092534 (這麼長) 但在頁面顯示的時候, 只顯示中國銀行(3118)但是傳給後臺的時候。又要傳62 ...
一、曾經在讀JQ源碼的時候,對深拷貝算是有了一點的理解。我們在項目中是不是經常會遇到這樣的問題呢?
後臺返回一個數組對象(引用類型).次數在頁面渲染中需要對部分數據進行處理 比如:銀行卡62345092534 (這麼長) 但在頁面顯示的時候,
只顯示中國銀行(3118)但是傳給後臺的時候。又要傳623445242整個號碼,我們也許會把var oldData = res.data;
但是我們發現兩個數據都變了? 這是為什麼呢? 其實就是一個深淺拷貝的問題。
二、淺拷貝
比如數組,對象,這樣的引用類型。
var arr = ['js','html']; var oldArr = arr; oldArr[0] = 'css'; console.log(arr); // ['css','html'] /* 這其實是一個地址的引用。 相當於他們賦值的是指針。 */ //在數組的方式中可以有兩種方式來避免這種問題 //1.slice var b = ['vue','react']; var oldB = b.slice(0); b[0] = 'angular'; console.log(b); //['angular','react'] console.log(oldB); // ['vue','react'] //2.concat(); var c = ['d3','three','webgl']; var oldC = c.concat([]); c[0] = 'earthgL'; console.log(c,oldC); // "earthgL", "three", "webgl"] ["d3", "three", "webgl"]
對象的淺複製也是一個道理。是對地址的引用而已。
var obj = { name:"前端", age:"10" } var oldObj = obj; obj.name = "html + css + js"; console.log(oldObj); //{name: "html + css + js", age: "10"}
三、深拷貝。
1.簡單來說 深拷貝就事創建了一個新的記憶體空間。 他們不在會公用同一個記憶體空間。是兩個完全獨立的對象或數組。
var defaults = { name: 'quber', age: [1, 2, 3, 4], child: [ { name: 'qubernet', fun: function () { return 1; } }, { name: 'qubernet1', fun: function () { return 2; } } ] }; var newDefaults = $.extend(true, {},defaults );
console.log(JSON.stringify(newDefaults ));
2.JQ深拷貝源碼部分
jQuery.extend = jQuery.fn.extend = function() { var options, name, src, copy, copyIsArray, clone, target = arguments[0] || {}, i = 1, length = arguments.length, deep = false; /* 變數 options:指向某個源對象。 變數 name:表示某個源對象的某個屬性名。 變數 src:表示目標對象的某個屬性的原始值。 變數 copy:表示某個源對象的某個屬性的值。 變數 copyIsArray:指示變數 copy 是否是數組。 變數 clone:表示深度複製時原始值的修正值。 變數 target:指向目標對象,申明時先臨時用第一個參數值。 變數 i:表示源對象的起始下標,申明時先臨時用第二個參數值。 變數 length:表示參數的個數,用於修正變數 target。 變數 deep:指示是否執行深度複製,預設為 false。 ps:源對象指的是把自己的值付給別人的對象;目標對象指的是被源對象賦值的對象 */ // 如果第一個參數傳入的是布爾值 if ( typeof target === "boolean" ) { deep = target;//設置deep變數,確定是深拷貝還是淺拷貝 target = arguments[1] || {};//將目標對象設為第二個參數值。 i = 2;//源對象的起始下標設為2(即從第三個參數開始算源對象) } // Handle case when target is a string or something (possible in deep copy) //嗯,原英文解釋的很清楚 if ( typeof target !== "object" && !jQuery.isFunction(target) ) { target = {}; } // 如果沒有目標對象,那麼目標對象就是jquery對象 if ( length === i ) { target = this; --i; } 拷貝的核心部分代碼 for ( ; i < length; i++ ) {//遍歷源對象 // Only deal with non-null/undefined values if ( (options = arguments[ i ]) != null ) {//options就是源對象 // Extend the base object for ( name in options ) {//遍歷源對象的屬性名 src = target[ name ];//獲取目標對象上,屬性名對應的屬性 copy = options[ name ];//獲取源對象上,屬性名對應的屬性 // 如果複製值copy 與目標對象target相等, //為了避免深度遍歷時死迴圈,因此不會覆蓋目標對象的同名屬性。 if ( target === copy ) { continue; } //遞歸地將源對象上的屬性值合併到目標對象上 //如果是深拷貝,且待拷貝的對象存在,且是普通對象或是數組 //這一個判斷條件非常關鍵,這正是之前疑問的癥結 //首先,普通對象的定義是:通過 "{}" 或者 "new Object" 創建的 //回到之前的疑問,目標對象tobeCloned的屬性o對象的obj不是普通對象,也不是數組,所以程式不會走到下麵的分支 if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { if ( copyIsArray ) { //如果是數組 copyIsArray = false; clone = src && jQuery.isArray(src) ? src : []; } else { clone = src && jQuery.isPlainObject(src) ? src : {}; } // 遞歸地拷貝 target[ name ] = jQuery.extend( deep, clone, copy ); } else if ( copy !== undefined ) { //會走到這個分支,這個分支的處理很暴力,就是把源對象的屬性,直接賦給源對象。 //對於上文中tobeCloned對象的屬性o,沒有進一步遞歸地拷貝,而是直接把引用賦給源對象 //所以改變tobeCloned的o屬性時,目標對象的o屬性也被改變了。 target[ name ] = copy; } } } } // Return the modified object return target; };
3.SE6提供的深拷貝的方法Object.assign();
四、總結。
簡單來說:淺拷貝就是多個變數共用一個地址,深拷貝就是創建了多個記憶體空間。 (希望對你有幫助)