<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          面試官問(wèn):什么變量保存在堆/棧中?

          共 3680字,需瀏覽 8分鐘

           ·

          2021-04-27 01:33

          面試官也在看的前端面試資料

          什么變量保存在堆/棧中?

          看到這個(gè)問(wèn)題,第一反應(yīng)表示很簡(jiǎn)單,基本類型保存在棧中,引用類型保存到堆中??????,但僅僅就如此簡(jiǎn)單嗎?我們接下來(lái)詳細(xì)看一看

          JS 數(shù)據(jù)類型

          我們知道 JS 就是動(dòng)態(tài)語(yǔ)言,因?yàn)樵诼暶髯兞恐安⒉恍枰_認(rèn)其數(shù)據(jù)類型,所以 JS 的變量是沒(méi)有數(shù)據(jù)類型的,值才有數(shù)據(jù)類型,變量可以隨時(shí)持有任何類型的數(shù)據(jù)

          JS 值有 8 種數(shù)據(jù)類型:

          • Boolean:有 truefalse
          • Undefined:沒(méi)有被賦值的變量或變量被提升時(shí)的,都會(huì)有個(gè)默認(rèn)值 undefined
          • Null:只有一個(gè)值 null
          • Number:數(shù)字類型
          • BigInt(ES10):表示大于 253 - 1 的整數(shù)
          • String:字符類型
          • Symbol(ES6)
          • Object:對(duì)象類型

          其中前 7 種數(shù)據(jù)類型稱為基本類型,把最后一個(gè)對(duì)象類型稱為引用類型

          JS中的變量存儲(chǔ)機(jī)制

          JS 內(nèi)存空間分為棧(stack)空間、堆(heap)空間、代碼空間。其中代碼空間用于存放可執(zhí)行代碼。

          棧空間

          棧是內(nèi)存中一塊用于存儲(chǔ)局部變量和函數(shù)參數(shù)的線性結(jié)構(gòu),遵循著先進(jìn)后出 (LIFO / Last In First Out) 的原則。棧由內(nèi)存中占據(jù)一片連續(xù)的存儲(chǔ)空間,出棧與入棧僅僅是指針在內(nèi)存中的上下移動(dòng)而已。

          JS 的棧空間就是我們所說(shuō)的調(diào)用棧,是用來(lái)存儲(chǔ)執(zhí)行上下文的,包含變量空間與詞法環(huán)境,var、function保存在變量環(huán)境,let、const 聲明的變量保存在詞法環(huán)境中。

          var a = 1
          function add(a{
            var b = 2
            let c = 3
            return a + b + c
          }

          // 函數(shù)調(diào)用
          add(a)

          這段代碼很簡(jiǎn)單,就是創(chuàng)建了一個(gè) add 函數(shù),然后調(diào)用了它。

          下面我們就一步步的介紹整個(gè)函數(shù)調(diào)用執(zhí)行的過(guò)程。

          在執(zhí)行這段代碼之前,JavaScript 引擎會(huì)先創(chuàng)建一個(gè)全局執(zhí)行上下文,包含所有已聲明的函數(shù)與變量:

          從圖中可以看出,代碼中的全局變量 a 及函數(shù) add 保存在變量環(huán)境中。

          執(zhí)行上下文準(zhǔn)備好后,開始執(zhí)行全局代碼,首先執(zhí)行 a = 1  的賦值操作,

          賦值完成后 a 的值由 undefined 變?yōu)?1,然后執(zhí)行 add 函數(shù),JavaScript 判斷出這是一個(gè)函數(shù)調(diào)用,然后執(zhí)行以下操作:

          • 首先,從全局執(zhí)行上下文中,取出 add 函數(shù)代碼
          • 其次,對(duì) add 函數(shù)的這段代碼進(jìn)行編譯,并創(chuàng)建該函數(shù)的執(zhí)行上下文和可執(zhí)行代碼,并將執(zhí)行上下文壓入棧中
          • 然后,執(zhí)行代碼,返回結(jié)果,并將 add 的執(zhí)行上下文也會(huì)從棧頂部彈出,此時(shí)調(diào)用棧中就只剩下全局上下文了。

          至此,整個(gè)函數(shù)調(diào)用執(zhí)行結(jié)束了。

          上面需要注意的是:函數(shù)add)在存放在棧區(qū)的數(shù)據(jù),在函數(shù)調(diào)用結(jié)束后,就已經(jīng)自動(dòng)的出棧,換句話說(shuō):棧中的變量在函數(shù)調(diào)用結(jié)束后,就會(huì)自動(dòng)回收。

          所以,通常棧空間都不會(huì)設(shè)置太大,而基本類型在內(nèi)存中占有固定大小的空間,所以它們的值保存在棧空間,我們通過(guò) 按值訪問(wèn) 。它們也不需要手動(dòng)管理,函數(shù)調(diào)時(shí)創(chuàng)建,調(diào)用結(jié)束則消失。

          堆數(shù)據(jù)結(jié)構(gòu)是一種樹狀結(jié)構(gòu)。它的存取數(shù)據(jù)的方式與書架和書非常相似。我們只需要知道書的名字就可以直接取出書了,并不需要把上面的書取出來(lái)。

          在棧中存儲(chǔ)不了的數(shù)據(jù)比如對(duì)象就會(huì)被存儲(chǔ)在堆中,在棧中只是保留了對(duì)象在堆中的地址,也就是對(duì)象的引用 ,對(duì)于這種,我們把它叫做 按引用訪問(wèn)

          舉個(gè)例子幫助理解一下:

          var a = 1
          function foo({
            var b = 2
            var c = { name'an' } // 引用類型
          }

          // 函數(shù)調(diào)用
          foo()

          所以,堆空間通常很大,能存放很多大的數(shù)據(jù),不過(guò)缺點(diǎn)是分配內(nèi)存和回收內(nèi)存都會(huì)占用一定的時(shí)間

          JS中的變量存儲(chǔ)機(jī)制與閉包

          對(duì)以上總結(jié)一下,JS 內(nèi)存空間分為棧(stack)空間、堆(heap)空間、代碼空間。其中代碼空間用于存放可執(zhí)行代碼

          • 基本類型:保存在棧內(nèi)存中,因?yàn)檫@些類型在存中分別占有固定大小的空間,通過(guò)按值來(lái)訪問(wèn)。
          • 引用類型:保存在堆內(nèi)存中,因?yàn)檫@種值的大小不固定,因此不能把它們保存到棧內(nèi)存中,但內(nèi)存地址大小的固定的,因此保存在堆內(nèi)存中,在棧內(nèi)存中存放的只是該對(duì)象的訪問(wèn)地址。當(dāng)查詢引用類型的變量時(shí), 先從棧中讀取內(nèi)存地址, 然后再通過(guò)地址找到堆中的值。對(duì)于這種,我們把它叫做按引用訪問(wèn)。

          閉包

          那么閉包喃?既然基本類型變量存儲(chǔ)在棧中,棧中數(shù)據(jù)在函數(shù)執(zhí)行完成后就會(huì)被自動(dòng)銷毀,那執(zhí)行函數(shù)之后為什么閉包還能引用到函數(shù)內(nèi)的變量?

          function foo({
            let num = 1 // 創(chuàng)建局部變量 num 和局部函數(shù) bar
            function bar(// bar()是函數(shù)內(nèi)部方法,是一個(gè)閉包
                num++
                console.log(num) // 使用了外部函數(shù)聲明的變量,內(nèi)部函數(shù)可以訪問(wèn)外部函數(shù)的變量
            }
            return bar // bar 被外部函數(shù)作為返回值返回了,返回的是一個(gè)閉包
          }

          // 測(cè)試
          let test = foo()
          test() // 2
          test() // 3

          在執(zhí)行完函數(shù) foo 后,foo 中的變量 num 應(yīng)該被彈出銷毀,為什么還能繼續(xù)使用喃?

          這說(shuō)明閉包中的變量沒(méi)有保存在棧中,而是保存到了堆中:

          console.dir(test)

          所以 JS 引擎判斷當(dāng)前是一個(gè)閉包時(shí),就會(huì)在堆空間創(chuàng)建換一個(gè)“closure(foo)”的對(duì)象(這是一個(gè)內(nèi)部對(duì)象,JS 是無(wú)法訪問(wèn)的),用來(lái)保存 num 變量

          注意,即使不返回函數(shù)(閉包沒(méi)有被返回):

          function foo({
            let num = 1 // 創(chuàng)建局部變量 num 和局部函數(shù) bar
            function bar(// bar()是函數(shù)內(nèi)部方法,是一個(gè)閉包
                num++ 
                console.log(num) // 使用了外部函數(shù)聲明的變量,內(nèi)部函數(shù)可以訪問(wèn)外部函數(shù)的變量
            }
            bar() // 2
            bar() // 3
            console.dir(bar)
          }

          foo()

          總結(jié)

          JS 就是動(dòng)態(tài)語(yǔ)言,因?yàn)樵诼暶髯兞恐安⒉恍枰_認(rèn)其數(shù)據(jù)類型,所以 JS 的變量是沒(méi)有數(shù)據(jù)類型的,值才有數(shù)據(jù)類型,變量可以隨時(shí)持有任何類型的數(shù)據(jù).

          JS 值有 8 種數(shù)據(jù)類型,它們可以分為兩大類——基本類型和引用類型。其中,基本類型的數(shù)據(jù)是存放在棧中,引用類型的數(shù)據(jù)是存放在堆中的。堆中的數(shù)據(jù)是通過(guò)引用和變量關(guān)聯(lián)起來(lái)的。

          閉包除外,JS 閉包中的變量值并不保存中棧內(nèi)存中,而是保存在堆內(nèi)存中。

          來(lái)自:https://github.com/Advanced-Frontend/Daily-Interview-Question

          最后

          歡迎關(guān)注「三分鐘學(xué)前端」,回復(fù)「交流」自動(dòng)加入前端三分鐘進(jìn)階群,每日一道編程算法題(第二天解答),助力你成為更優(yōu)秀的前端開發(fā)!

          》》面試官也在看的前端面試資料《《
          “在看和轉(zhuǎn)發(fā)”就是最大的支持
          瀏覽 24
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  激情婷婷亚洲 | 国产肏屄视频在线免费看 | 日韩精品在线播放 | 日本一线视频在线观看 | 三级片网站在线观看 |