摘要:因此,出于性能的考慮,在拷貝的方式選擇上,應該結合具體的業務環境來進行選擇參考專題之深淺拷貝深入剖析的深復制
值類型基本數據類型是按值訪問的,因為可以操作保存在變量中的實際的值;
引用類型的值是保存在內存中的對象,在操作對象時,實際上是在操作對象的引用而不是實際的對象;
如果一個變量存儲的是值的本身那么就是一個值類型number / string / Boolean / Null / Undefined —值類型的變量本身就是含有賦予給它的數值的,它的變量本身及保存的數據都存儲在棧的內存塊當中,當聲明一個值類型時,必須對它初始化(給變量賦值)才能使用
var num1 = 123, num2 = num1; num1 = 456; console.log(num2);// 123
將值類型復制給另外一個值時(num2=num1),也就是num2重新再棧上開辟了一塊空間,然后將num1中的內容復制一份放在num2中,當改變其中一個變量的值時,不會影響另外一個變量的值
引用類型如果一個變量存儲的是引用(地址),那么就是一個引用類型object—引用類型的值的存儲與值類型不同,它分別存儲在內存的堆和棧中,棧中存放的是指向堆中內容的地址,堆中存放的引用類型的地址(鍵值對)
var obj1 = {name: "xyc"}; var obj2 = obj1; obj1.name = "lxy"; console.log(obj2.name); // "lxy"
obj2=obj1表示的是將棧上的地址復制一份給另一個對象,他們同時指向堆中的內容,當修改內容時,兩個對象中的值都會發生改變
一個面試題var o = new Object(); function foo(obj) { obj.name = "xyc"; obj = new Object(); obj.name = "lxy"; } foo(o); console.log(o.name); // ???
圖解:
(1)新建對象var o = new Object();
(2)在foo的環境下執行obj.name = "xyc"
由于是參數傳遞,在局部作用域內相當于執行了obj = o
(3)在局部作用域內新建對象,并賦值相同的屬性值
obj = new Object(); obj.name = "lxy";
(4)foo()執行完畢,局部作用域出棧,obj聲明周期結束
此時,新建的對象依然存在,等待下一次內存自動回收機制將堆中的無引用對象銷毀
什么是深淺拷貝?在mac電腦中我們可以對某個文件夾創建替身或者復制粘貼某個文件/文件夾,這兩種方式都實現了對某個文件/文件夾的拷貝,但是,前者在文件中修改文件內容時,源文件也會修改,而后者的操作在修改文件內容時不會對源文件有影響
上面的例子,“創建替身” ==> 淺拷貝,“復制粘貼” ==> 深拷貝
從內存角度說明:淺拷貝只會在棧內存中開辟空間存放指向源文件的變量,而深拷貝會在堆內存也拷貝文件
深淺拷貝僅是針對于數組和對象而言,不嚴謹的說法,基本類型的拷貝都屬于深拷貝
(1)淺拷貝(最為常見)
var obj1 = {name: "xyc"}; var obj2 = obj1; obj1.name = "lxy"; console.log(obj2.name); // "lxy"
僅將棧內存復制,堆內存中的指向依然相同,obj2對象改變后,會影響obj1對象
(2)拷貝對象及對象下一層的屬性和方法
var shallowCopy = function(obj) { // 只拷貝對象 if (typeof obj !== "object") return; // 根據obj的類型判斷是新建一個數組還是對象 var newObj = obj instanceof Array ? [] : {}; // 遍歷obj,并且判斷是obj的屬性才拷貝 for (var key in obj) { if (obj.hasOwnProperty(key)) { newObj[key] = obj[key]; } } return newObj; }
var obj1 = { name: "xyc", features: { say: "hello", eat: "something" } } var obj2 = shallowCopy(obj1); obj2.features.eat = "anything"; console.log(obj1.features.eat); // "anything" console.log(obj1 === obj2); // false console.log(obj1.features === obj2.features); // true
上面這個例子可以看出上述方式的復制在對象內嵌套對象是不能夠實現“類深拷貝”的,下面有一個進階型的
(3)遞歸調用對象中嵌套的對象
var deepCopy = function(obj) { // 只拷貝對象 if (typeof obj !== "object") return; // 根據obj的類型判斷是新建一個數組還是對象 var newObj = obj instanceof Array ? [] : {}; // 遍歷obj,并且判斷是obj的屬性才拷貝 for (var key in obj) { if (obj.hasOwnProperty(key)) { // 當obj中嵌套對象時,再次調用該方法 newObj[key] = typeof obj[key] !== "object" ? obj[key] : deepCopy(obj[key]); } } return newObj; }
(4)一維數組技巧性的拷貝
通過slice()和concat()來實現
var arr = ["old", 1, true, null, undefined]; var new_arr = arr.concat(); // var new_arr = arr.slice(); new_arr[0] = "new"; console.log(arr) // ["old", 1, true, null, undefined] console.log(new_arr) // ["new", 1, true, null, undefined]
一如上面的對象嵌套,多維數組使用上面的方式拷貝不徹底
(5)粗暴的拷貝方式
通過JSON.parse( JSON.stringify() )實現
var arr = ["old", 1, true, ["old1", "old2"], {old: 1}] var new_arr = JSON.parse( JSON.stringify(arr) ); console.log(new_arr === arr);// false console.log(new_arr[3] === arr[3]);// false
但是這種方式無法實現函數的拷貝
var arr = [function(){ console.log(a) }, { b: function(){ console.log(b) } }] var new_arr = JSON.parse(JSON.stringify(arr)); console.log(new_arr);// [null, object]
函數通過這個方式會被轉換成null
(6)補全深拷貝
var deepCopy = function(obj){ var str, newobj = obj.constructor === Array ? [] : {}; if(typeof obj !== "object"){ return; } else if(window.JSON){ str = JSON.stringify(obj), //系列化對象 newobj = JSON.parse(str); //還原 } else { for(var i in obj){ newobj[i] = typeof obj[i] === "object" ? cloneObj(obj[i]) : obj[i]; } } return newobj; };
徹底的深拷貝理論上是將對象的整個原型鏈拷貝(無論原型屬性是否為enumerable均應拷貝),遍歷的次數越多,性能消耗越大。因此,出于性能的考慮,在拷貝的方式選擇上,應該結合具體的業務環境來進行選擇
參考:
JavaScript專題之深淺拷貝
深入剖析 JavaScript 的深復制
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/87269.html
摘要:因此,所有在方法中定義的變量都是放在棧內存中的當我們在程序中創建一個對象時,這個對象將被保存到運行時數據區中,以便反復利用因為對象的創建成本通常較大,這個運行時數據區就是堆內存。 上一篇:《javascript高級程序設計》筆記:繼承近幾篇博客都會圍繞著圖中的知識點展開 showImg(https://segmentfault.com/img/bVY0C4?w=1330&h=618);...
摘要:上一篇你不知道的筆記寫在前面這是年第一篇博客,回顧去年年初列的學習清單,發現僅有部分完成了。當然,這并不影響年是向上的一年在新的城市穩定連續堅持健身三個月早睡早起游戲時間大大縮減,學會生活。 上一篇:《你不知道的javascript》筆記_this 寫在前面 這是2019年第一篇博客,回顧去年年初列的學習清單,發現僅有部分完成了。當然,這并不影響2018年是向上的一年:在新的城市穩定、...
摘要:局部變量只在函數執行過程中存在。此時,局部變量就沒有存在的必要了,因此可以釋放他們所占的內存以供他們使用。引用計數的含義是跟蹤記錄每個值被引用的次數。這一做法適合于大多數全局變量和局部變量的屬性。 基本類型和引用類型的值 ECMAScript變量可能包含兩種不同數據類型的值:基本類型值和引用類型值。基本類型值指的是簡單的數據段,而引用類型的值指那些可能有多個值構成的對象。 動態的屬性 ...
摘要:作用域鏈中的下一個變量對象來自包含外部環境,而再下一個變量對象則來自下一個包含環境。這樣,一直延續到全局執行環境全局執行環境的變量對象始終都是作用域鏈中的最后一個對象標識符解析沿作用域鏈一級一級搜索標識符。 一、寫在前面 最近研究了創建Android虛擬機、vscode結合weex開發Android APP、Vmware裝MAC虛擬機的事,看的內容不夠多,接下來加油 二、變量、作用域和...
摘要:解耦優勢代碼復用,單元測試。常用比較誤區可同時判斷,可用來判斷對象屬性是否存在。使用作判斷無法進行充分的類型檢查。文件中應用常量參考文檔高級程序設計作者以樂之名本文原創,有不當的地方歡迎指出。 showImg(https://segmentfault.com/img/bVburXw?w=500&h=400); 編寫可維護性代碼 可維護的代碼遵循原則: 可理解性 (方便他人理解) 直觀...
閱讀 669·2023-04-25 15:49
閱讀 3121·2021-09-22 15:13
閱讀 1259·2021-09-07 10:13
閱讀 3482·2019-08-29 18:34
閱讀 2565·2019-08-29 15:22
閱讀 513·2019-08-27 10:52
閱讀 690·2019-08-26 18:27
閱讀 3025·2019-08-26 13:44