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