學了堆棧內存空間,應該就理解了什麼叫簡單數據類型存在棧內存,複雜數據類型存在堆內存了。javascript
而後面試中常常會問、業務中也常常會遇到的問題就是深淺拷貝的問題了。html
棧內存中簡單數據類型直接拷貝就能獲得一個副本,可是複雜數據類型的拷貝若是也想獲得一個副本,就須要深拷貝了。java
var a = 1; var b = a;
這就是淺拷貝了,雖然你視覺上看上去a = b;可是修改b的值,a不會收影響。由於b是a的一個副本,就像你拷貝了一個文件夾副本同樣。修改副本,源文件夾不會受影響。面試
但這種拷貝狀況只侷限在簡單類型的拷貝:數組
string、number、boolean、null、undefiend數據結構
若是你拷貝一個數組/對象(以數組爲例):函數
var c = [1,2,3]; var d = c; d.push(4); console.log(c)
c也被改變了。this
可是若是你改變d的整個數據,讓他等於一個新的數組(甚至對象)c這時返倒不受影響了。spa
很奇怪很費解吧?prototype
這是由於:
數組、對象這類複雜類型數據結構,在棧內存裏存放的只是指向堆內存中存放數據的地址,
你直接d = c; 拷貝的也是一個副本,但這個副本區別之處是,他並不是數據的副本,而是棧內存地址的副本。
這個副本d的地址改變,對c沒有影響,而你給d從新指向一個新的數組,就是改變地址了。此時只有d指向了新數組[5,6,7],可是c不受影響,因此打印出來的不變。
可是雖然有兩個地址,一個是原、一個是副本。可他們同時指向同一個堆內存的數據。
這樣看來,你拷貝出來的d和c用的是同一個數組。
因此d.push執行之後,並非c也跟着push了,而是c指向的數組和被d.push的是同一個數組。
你能夠想象成,同一個房間,d和c都拿着鑰匙,這倆鑰匙是不一樣的,可是他們都是同一間屋子的鑰匙。d用鑰匙打開門往屋子裏放了一個東西。那麼c打開這個屋子,這個東西也還會在。
以上這些,就是基礎知識的解讀。
具體深拷貝就是要理解了複雜類型拷貝的缺點,而後再進行彌補。
既然想要複雜類型也像簡單類型那樣拷貝一個新數據的話,就不僅僅是拷貝地址了。而是要從新創建一個新數據存放空間,而後挨個把想要拷貝的東西搬進來才行。
因此拷貝前咱們得判斷一個數據他究竟是什麼類型的,好「對症下藥」:
getType: function (target) { /* * @Author: guojufeng@ * @Date: 2017-12-20 15:07:06 * @purpose 獲取一個值的類型 * @param {variateName} target: 要獲取類型的變量名或對象 * @output {string} result || "null": 返回的參數 - result或者null,爲字符串形式的 */ if (target === null) { console.log("getType類型判斷爲: " + target) return "null"; } let result = typeof (target); if (result == "object") { if (target instanceof Array) { result = "array"; } else if (target instanceof Object) { let target = Object.prototype.toString.call(target); if (target == "[object Object]") { result = "object"; } else { result = target;//構造類 } } } console.log("getType類型判斷爲: " + result) return result; //返回類型值的字符串形式 }
以上代碼,判斷一個值的類型,輸入值自己,返回字符串形式的類型描述。
若是是簡單類型,返回typeof方法返回的對應值便可。
這裏特殊處理了null,由於他用typeof返回object。
而後對於複雜類型的數據,再深刻判斷其實array類型仍是object類型。
對object類型中,還有構造類須要區分。直接返回[object String]這樣類型的。但其實在深拷貝階段,直接將其放到object形式處理了。
而後就是深拷貝的代碼:
deepClone: function (origin) { /* * @Author: guojufeng@ * @Date: 2018-10-30 20:48:44 * @purpose 深度克隆 * @param {variateName} origin: 要克隆的對象變量名 * @output {對應值} 根據origin的類型返回的具體值 */ let type = this.getType(origin), target; if (type == "array") { target = []; /* 數組 */ origin.forEach(el => { // console.log("ele",el) target.push(this.deepClone(el)); }); } else if (type == "object") { /* 對象 */ target = {}; for (const key in origin) { if (origin.hasOwnProperty(key)) { /* 注意,只拷貝元素身上的,而不拷貝其原型上的值 */ const el = origin[key]; target[key] = this.deepClone(el); } } } else if (type == "function") { /* 函數 */ target = function () {}; target = origin; } else { /* 原始值 */ target = origin; } return target; }
對於簡單類型,直接進行拷貝
對於函數,新建一個function,而後拷貝
對於數組,新建一個數組,而後 forEach 遍歷拷貝。若是循環過程當中,數組中嵌套複雜類型,再次遞歸調用深拷貝方法。
對於對象,新建一個對象,而後for in遍歷拷貝非原型值。若是循環過程當中,對象中嵌套複雜類型,再次遞歸調用深拷貝方法。
以上,就是整個邏輯。
聲明:
請尊重博客園原創精神,轉載或使用圖片請註明:
博主:xing.org1^
出處:http://www.cnblogs.com/padding1015/