js-由深拷貝淺拷貝,傳值與傳址-引發(fā)的關(guān)于堆(“heap”)棧(“stack”)的思考!

前端面試一定會都遇到輸出值的問題,并且不止一道題而是幾乎兩頁紙。這類問題大多都是看一個前端開發(fā)者的js基礎(chǔ),數(shù)據(jù)類型和變量作用域。今天我們就堆棧的概念來看看js中的變量是如何存儲的。

Js的數(shù)據(jù)類型可分為?值類型(基本類型):字符串(String)、數(shù)字(Number)、布爾(Boolean)、對空(Null)、未定義(Undefined)。和?引用數(shù)據(jù)類型:對象(Object)、數(shù)組(Array)、函數(shù)(Function)。

由于棧和堆的緩存設(shè)計各異所以存儲的數(shù)據(jù)也有所不同。

棧使用的是一級緩存,內(nèi)置在CPU內(nèi)部并與CPU同速運行,效率高,但存儲空間小。適合存儲簡單的數(shù)據(jù)段,占據(jù)固定空間大小的基本數(shù)據(jù)類型(String,Number,Boolean,Null,Undefined)。

堆存放在二級緩存中,是位于CPU與內(nèi)存之間的臨時存儲器,比一級緩存容量更大,但處理速度更慢。適合存儲大小不定,構(gòu)造復(fù)雜的引用類型值(Function,Array,Object)。

傳值&傳址

傳值就是變量之間值的傳遞與引用,傳址是相對于引用類型而言。如例:


會輸出什么呢?

按照js自上而下的執(zhí)行順序我們期望得到<5,1,[2],[0],false>而事實卻是這樣的<5,1,[2],[2],true>。

例子1:a為Number基本類型,存在棧內(nèi)存中。將a賦值給b也就是將a的值賦值給b,所以b=1;改變a的值對b不會有影響。

例子2:arr為Array引用類型,存儲在堆內(nèi)存中,棧內(nèi)存儲的是arr(Array)在堆內(nèi)存空間的引用指針。將arr賦值給arr1也就是將arr在堆內(nèi)存空間的引用指針賦值給arr1,所以他們都指向了同一個存儲空間的相同數(shù)據(jù),固 arr === arr1。


那么同樣是存儲為什么會有存值和存址之分?這就要從棧和堆的存儲方式說起。

前面提到棧使用的是一級緩存,一級緩存被內(nèi)置在CPU內(nèi)部并與CPU同速運行,容量小,速度快,更適合占據(jù)固定空間的簡單數(shù)據(jù)段,故而存值。而堆使用的是二級緩存,二級緩存比一級緩存速度慢,容量大,是一級緩存和內(nèi)存之間臨時交換數(shù)據(jù)的地方。適合存儲大小不固定,構(gòu)造復(fù)雜的引用類型值。所以當(dāng)我們在程序中調(diào)用引用類型時,是通過棧內(nèi)存中的引用指針在堆內(nèi)存中查找到相應(yīng)的存儲數(shù)據(jù)。

現(xiàn)在回看arr的例子,先var申明arr,再申明arr1并賦值為arr,再改變arr的值,然后分別調(diào)用輸出了arr和arr1。從執(zhí)行順序考慮只是改變了arr的值,arr1是不受影響的,但是在arr1 = arr的時候我們只是傳遞了引用類型再堆內(nèi)存中的引用指針。所以都變了...

假如兩個例子同時出現(xiàn)在面試題中,是不是一點疑惑都沒有了呢。

深拷貝&淺拷貝

我們經(jīng)常會遇到將對象賦值給另一對象的場景,有時候也會費力去寫一些clone的方法,為什么呢?

如上面例子2中我們看到,對象的傳遞是傳址是通過指針的引用,例子中的傳遞也就是表面上的傳址這是淺拷貝。而深拷貝就是申請新的存儲空間,將源對象的值循環(huán)遍歷賦值給目標(biāo)對象,存入到新的存儲空間。

function clone(origin){

? ? var target = null;

? ? if(origin instanceof Array){

? ? ? ? target = origin.concat();

? ? }else{

? ? ? ? target = {};

? ? ? ? for(var item in origin){

? ? ? ? ? ? var val = origin[item];

? ? ? ? ? ? target[item] = typeof val == 'object'?clone(val):val;

? ? ? ? }

? ? }

? ? return target;

}

而ES6也給我們提供了更好用的方法 Object.assign(target, ...source)用于將所有可枚舉屬性的值從一個或多個源對象復(fù)制到目標(biāo)對象。它返回目標(biāo)對象。這里需要注意假如源對象的屬性值是一個對象的引用,那么它也只指向那個引用。所以要慎用。


當(dāng)然還有一種方法也不得不提就是jQuery的$.extend()與Object.assign有著異曲同工之妙,但$.extend多了首個參數(shù)deep從而實現(xiàn)了深拷貝如列:


?第一次試著寫這種長篇大論,可能漏洞百出,還請多多指正。文章沒有深層意義,僅供參考,以免將您帶入歧途。一些堆棧,一級緩存,二級緩存的知識也都是參照百度百科,如果感興趣的可以自己去研究研究。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容