成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

js中的值類型、引用類型、堆、棧、函數(shù)參數(shù)傳遞方式、連續(xù)賦值等概念的學(xué)習(xí)

hizengzeng / 1861人閱讀

摘要:值類型基本類型和棧內(nèi)存值類型也稱為原始數(shù)據(jù)或原始值這類值存儲(chǔ)在棧內(nèi)存中基本類型的值不可以修改。目前中的基本類型一共有六種。堆的使用規(guī)則當(dāng)創(chuàng)建數(shù)組時(shí),就會(huì)在堆內(nèi)存中創(chuàng)建一個(gè)數(shù)組對(duì)象,并且在棧內(nèi)存中創(chuàng)建一個(gè)對(duì)數(shù)組的引用。

值類型(基本類型)和棧內(nèi)存

值類型也稱為原始數(shù)據(jù)或原始值(primitive value).這類值存儲(chǔ)在棧(stack)內(nèi)存中, 基本類型的值不可以修改。每當(dāng)我們定義一個(gè)變量,并賦給它一個(gè)基本類型的值時(shí),可以理解為,我們?yōu)檫@個(gè)變量綁定了一個(gè)內(nèi)存空間,這個(gè)內(nèi)存空間存放的就是變量的值。因此。基本類型數(shù)據(jù)是存放在棧內(nèi)存中的簡(jiǎn)單數(shù)據(jù)段,數(shù)據(jù)大小確定,內(nèi)存空間大小可以分配。
目前js中的基本類型一共有六種:null,undefined,boolean,number,,string,symbol。其中symbol是es6中新加的數(shù)據(jù)類型。這些類型在內(nèi)存中分別占用固定大小的空間,他們的值保存在??臻g,我們通過(guò)按值來(lái)訪問(wèn)的、
讓我們看一個(gè)基本類型的值不可以修改的示例

var a = 4;
a = 3; //注意,這里是覆蓋,不是修改

var num1 = 5;
var num2 = num1;

num1+=1;
consol.log(num1);//6
console.log(num2);//5

從上面第二個(gè)例子可以看到,從一個(gè)變量向另一個(gè)變量復(fù)制基本類型的值,我們會(huì)在變量對(duì)象上重新創(chuàng)建一個(gè)新值,然后把值復(fù)制到新變量分配的位置上,這兩個(gè)值是完全獨(dú)立的,對(duì)著兩個(gè)變量進(jìn)行操作是互不影響的。

引用類型

這類值存儲(chǔ)在堆內(nèi)存中,堆是內(nèi)存中的動(dòng)態(tài)區(qū)域,相當(dāng)于自留空間,在程序運(yùn)行期間會(huì)動(dòng)態(tài)分配給代碼和堆棧。對(duì)中存儲(chǔ)的一般都是對(duì)象,然后在棧內(nèi)存中存儲(chǔ)一個(gè)變量指針,計(jì)算機(jī)通過(guò)這個(gè)變量指針,找到堆中的數(shù)據(jù)塊并進(jìn)行操作。這種訪問(wèn)方式,我們叫它按引用訪問(wèn)。如圖所示。

堆的使用規(guī)則
var fruit_1 = "apple";
var fruit_2 = "orange";
var fruit_3 = "banana";
var oArray = [fruit_1,fruit_2,fruit_3];
var newArray = oAarray;

當(dāng)創(chuàng)建數(shù)組時(shí),就會(huì)在堆內(nèi)存中創(chuàng)建一個(gè)數(shù)組對(duì)象,并且在棧內(nèi)存中創(chuàng)建一個(gè)對(duì)數(shù)組的引用。變量fruit_1、fruit_2、fruit_3為基本數(shù)據(jù)類型,他們的值直接存放在棧中;newArray、oArray為符合數(shù)據(jù)類型(引用類型),他們的引用變量存放在棧中,指向于存放在堆中的實(shí)際對(duì)象。
此時(shí)我們改變oAarray中的值,對(duì)應(yīng)的newArray也會(huì)改變,因?yàn)樗鼈兊拇鎯?chǔ)的指針指向同一個(gè)堆地址。

console.log(oArray[1]);// 返回 orange
newArray[1]="berry";
console.log(oArray[1]);// 返回 berry

例如,下面代碼將newArray賦值為null:

newArray = null;

注意:接觸一個(gè)值的引用并不意味著自動(dòng)回收改值所占用的內(nèi)存。解除引用的真正作用時(shí)讓值脫離執(zhí)行環(huán)境,以便垃圾收集器下次運(yùn)行時(shí)將其回收。

為什么會(huì)有棧內(nèi)存和堆內(nèi)存之分?

與垃圾回收機(jī)制有關(guān),為了使程序運(yùn)行時(shí)占用的內(nèi)存最小。
當(dāng)一個(gè)方法執(zhí)行時(shí),每個(gè)方法都會(huì)建立自己的內(nèi)存棧,在這個(gè)方法內(nèi)定義的變量會(huì)逐個(gè)放入這塊棧內(nèi)存里,隨著方法的執(zhí)行結(jié)束,這個(gè)方法的內(nèi)存棧也將自然銷毀了。因此,所有在方法中定義的變量都是放在棧內(nèi)存中的;
當(dāng)我們?cè)诔绦蛑袆?chuàng)建一個(gè)對(duì)象時(shí),這個(gè)對(duì)象將被保存到運(yùn)行時(shí)數(shù)據(jù)區(qū)中,以便反復(fù)理由(因?yàn)閷?duì)象的創(chuàng)建成本通常比較大),這個(gè)運(yùn)行時(shí)數(shù)據(jù)區(qū)就是堆內(nèi)存。堆內(nèi)存中的對(duì)象不會(huì)隨方法的結(jié)束而銷毀,即使方法結(jié)束后,這個(gè)對(duì)象還可能被另一個(gè)引用變量所引用(方法的參數(shù)傳遞時(shí)很常見(jiàn)),則這個(gè)對(duì)象依然不會(huì)被銷毀,只有當(dāng)一個(gè)對(duì)象沒(méi)有任何引用變量引用它時(shí),系統(tǒng)的垃圾回收機(jī)制才會(huì)在核實(shí)的時(shí)候回收它。

函數(shù)中參數(shù)傳遞方式,按值傳遞和按引用傳遞

按值傳遞:函數(shù)的形參時(shí)被調(diào)用時(shí)所傳實(shí)參的副本,修改形參并不會(huì)影響實(shí)參。

var num = 10;
function change(num){
    num = num * 10;
}
change(num)
console.log(num);       // 2

可以看到這里的變量num在運(yùn)行完函數(shù)以后,值并沒(méi)有發(fā)生改變。

按引用傳遞:函數(shù)的形參接收實(shí)參的內(nèi)存地址,而不再是副本。這意味著函數(shù)形參的值如果被修改,實(shí)參也會(huì)被修改。

var ab={
    x:1,
    y:2
}

function foo(obj){
    obj.x = 2
}
foo(ab)

console.log(ab);    //{x:2,y:2}

可以看到,原來(lái)的ab對(duì)象,在函數(shù)foo調(diào)用之后,其中的對(duì)象屬性發(fā)生變化。由上面的兩個(gè)例子,我們是不是可以推斷在js中,對(duì)于基本類型的數(shù)據(jù),在函數(shù)傳遞過(guò)程中使用的時(shí)按值傳遞,而對(duì)于引用類型數(shù)據(jù),在函數(shù)傳遞過(guò)程中使用的時(shí)是按引用傳遞方式呢?讓我們?cè)倏戳硗庖粋€(gè)例子。

var ab={
    x:1,
    y:2
}

function foo(obj){
    obj = {
        x:2,
        y:3
    }
}
foo(ab)

console.log(ab);    //{x:1,y:2}

這個(gè)示例,如果按照我們剛剛說(shuō)的結(jié)論,這里的函數(shù)運(yùn)行后,輸出的對(duì)象ab的值應(yīng)該是{x:2,y:3}。但是真正的結(jié)果確實(shí){x:1,y:2}。這就奇怪了,到底js中的函數(shù)參數(shù)的傳遞方式是按什么樣的方式呢。我在網(wǎng)上找了很多資料,發(fā)現(xiàn)有個(gè)說(shuō)法叫按共享傳遞。簡(jiǎn)單來(lái)說(shuō),就是對(duì)于基本類型,是按值傳遞,對(duì)于對(duì)象而言,直接修改形參對(duì)實(shí)參沒(méi)有效果,而修改形參的屬性卻可以同時(shí)修改實(shí)參的屬性。而我的理解是這樣的。看一張圖。

由我們上面對(duì)引用類型與堆內(nèi)存的介紹,我們知道,當(dāng)我們定義一個(gè)ab對(duì)象時(shí),系統(tǒng)會(huì)在堆中開(kāi)出一個(gè)空間用來(lái)存儲(chǔ)改對(duì)象,對(duì)應(yīng)的在棧內(nèi)存中開(kāi)出一個(gè)空間,用來(lái)存儲(chǔ)指向?qū)ο蟮牡刂?。如上圖,根據(jù)按引用傳遞的方式,我們知道,foo函數(shù)內(nèi)部的obj。在函數(shù)編譯的時(shí)候,是指向ab所指向的對(duì)象的,二者共用一個(gè)地址。當(dāng)函數(shù)執(zhí)行后,obj指向了新的對(duì)象地址,但是之前的ab所指向的對(duì)象屬性,依舊被ab所引用,沒(méi)有任何改變。所以,這里輸出的ab的值依舊未變。
而對(duì)于函數(shù)內(nèi)部,改變形參的屬性值這個(gè)情況,我們也可以很容易的清楚,因?yàn)楹瘮?shù)內(nèi)部的obj和ab對(duì)象共同指向同一個(gè)對(duì)象空間,所以改變前者的對(duì)象屬性的值,自然會(huì)影響后者

一道經(jīng)典的面試題
var a = {n:1};
var b = a;

a.x = a = {n:2};
console.log(a.x);
console.log(b.x)

這道題考察了很多東西,js中的運(yùn)算符的優(yōu)先級(jí),比如賦值運(yùn)算,是從右到左的。js中的對(duì)象存儲(chǔ)的問(wèn)題。但是這道題很容易根據(jù)賦值運(yùn)算是從右到左的順序運(yùn)行的來(lái)得到錯(cuò)誤的結(jié)果

a = {n:2}
a.x = a

然后得到了錯(cuò)誤的答案。a.x = {n:2}.實(shí)際上,要解出這道題,我們至少要知道兩個(gè),第一個(gè)就是運(yùn)算符的優(yōu)先級(jí)問(wèn)題,點(diǎn)運(yùn)算符的優(yōu)先級(jí)高于賦值運(yùn)算級(jí)。所以這里的執(zhí)行順序第一步肯定是a.x,然后才是從右到左的賦值運(yùn)算;第二個(gè),我們要知道的是,js對(duì)象在內(nèi)存中是如何存儲(chǔ)的。知道這兩點(diǎn),那這道題就不難解決了。同樣的,我們繼續(xù)看幾張圖。
。
通過(guò)該題的前兩行代碼的聲明a和b,結(jié)合上面所說(shuō)的對(duì)象存儲(chǔ)的原理,可以很容易看明白。a和b指向同一個(gè)對(duì)象空間。

a.x = a = {n:2}

這行代碼,首先會(huì)運(yùn)行a.x。這樣便會(huì)在{n:1}對(duì)象所存儲(chǔ)的空間上添加一個(gè)x屬性名,并且等待賦值。即原來(lái)的{n:1}變成{n:1,x:undefined}。然后,按照賦值運(yùn)算的順序,先將變量a的指向變?yōu)閧n:2},但是這里要注意,{n:1,x:undefined}由于被b引用,所以依舊存在在內(nèi)存當(dāng)中。然后執(zhí)行 ax.x = a,這里要注意,這里的a已經(jīng)是{n:2}了。而a.x則是b指向的{n:1,x:undefined}中的x屬性,所以最終b指向的{n:1,x:undefined}變?yōu)閧n:1,x:{n:2}}。
所以最后的結(jié)果??聪聢D。

console.log(a.x);   //undefined
console.log(b.x);   //{n:2}


參考鏈接:
https://www.imooc.com/article...
https://blog.csdn.net/lxiang2...
https://blog.csdn.net/xdd1991...
https://segmentfault.com/a/11...
https://blog.csdn.net/u012860...

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/97561.html

相關(guān)文章

  • 第二章 一切都是對(duì)象

    摘要:此內(nèi)存區(qū)域的唯一目的就是存放對(duì)象實(shí)例,幾乎所有的對(duì)象實(shí)例都在這里分配內(nèi)存。不過(guò),無(wú)論如何劃分,都與存放內(nèi)容無(wú)關(guān),無(wú)論哪個(gè)區(qū)域,存儲(chǔ)的都仍然是對(duì)象實(shí)例,進(jìn)一步劃分的目的是為了更好地回收內(nèi)存,或者更快地分配內(nèi)存。 一、對(duì)象和類的存儲(chǔ) 根據(jù)java虛擬機(jī)規(guī)范第七版的規(guī)定,Java虛擬機(jī)所管理的內(nèi)存將包括以下幾個(gè)運(yùn)行時(shí)數(shù)據(jù)區(qū)域:程序計(jì)數(shù)器、方法區(qū)、堆、虛擬機(jī)棧、本地方法棧。(詳見(jiàn)深入理解jav...

    孫吉亮 評(píng)論0 收藏0
  • 前端進(jìn)擊巨人(二):、、隊(duì)列、內(nèi)存空間

    摘要:中有三種數(shù)據(jù)結(jié)構(gòu)棧堆隊(duì)列。前端進(jìn)擊的巨人一執(zhí)行上下文與執(zhí)行棧,變量對(duì)象中解釋執(zhí)行棧時(shí),舉了一個(gè)乒乓球盒子的例子,來(lái)演示棧的存取方式,這里再舉個(gè)栗子搭積木。對(duì)于基本類型,棧中存儲(chǔ)的就是它自身的值,所以新內(nèi)存空間存儲(chǔ)的也是一個(gè)值。 面試經(jīng)常遇到的深淺拷貝,事件輪詢,函數(shù)調(diào)用棧,閉包等容易出錯(cuò)的題目,究其原因,都是跟JavaScript基礎(chǔ)知識(shí)不牢固有關(guān),下層地基沒(méi)打好,上層就是豆腐渣工程,...

    edgardeng 評(píng)論0 收藏0
  • ?搞不懂JS賦值·淺拷貝·深拷貝請(qǐng)看這里

    showImg(https://segmentfault.com/img/bVbvpCA); 前言 為什么寫拷貝這篇文章?同事有一天提到了拷貝,他說(shuō)賦值就是一種淺拷貝方式,另一個(gè)同事說(shuō)賦值和淺拷貝并不相同。我也有些疑惑,于是我去MDN搜一下拷貝相關(guān)內(nèi)容,發(fā)現(xiàn)并沒(méi)有關(guān)于拷貝的實(shí)質(zhì)概念,沒(méi)有辦法只能通過(guò)實(shí)踐了,同時(shí)去看一些前輩們的文章總結(jié)了這篇關(guān)于拷貝的內(nèi)容,本文也屬于公眾號(hào)【程序員成長(zhǎng)指北】學(xué)習(xí)路線...

    lauren_liuling 評(píng)論0 收藏0
  • JavaScript學(xué)習(xí)總結(jié)(一)基礎(chǔ)部分

    摘要:前綴規(guī)范每個(gè)局部變量都需要有一個(gè)類型前綴,按照類型可以分為表示字符串。例如,表示以上未涉及到的其他對(duì)象,例如,表示全局變量,例如,是一種區(qū)分大小寫的語(yǔ)言。布爾值與字符串相加將布爾值強(qiáng)制轉(zhuǎn)換為字符串。 基本概念 javascript是一門解釋型的語(yǔ)言,瀏覽器充當(dāng)解釋器。js執(zhí)行時(shí),在同一個(gè)作用域內(nèi)是先解釋再執(zhí)行。解釋的時(shí)候會(huì)編譯function和var這兩個(gè)關(guān)鍵詞定義的變量,編譯完成后從...

    AlanKeene 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<