js的基礎(平民理解的執行上下文/調用堆棧/內存棧/值類型/引用類型)


  

  與以前的切圖比較,現在的前端開發對js的要求似乎越來越高,在開發中,我們不僅僅是要知道如何運用現有的框架(react/vue/ng),

而且我們對一些基礎的知識的依賴越來越大。

  

  現在我們就用平民的方法講解下執行上下文/調用堆棧/內存棧。

  理解下 javascript 在執行中,javascript 引擎(v8) 對我們加載的代碼做了寫什么?

 

  我們整一段非常簡單的 js 代碼來分析 v8 引擎和執行上下文/調用堆棧/內存棧的關系。

 

<script>
  var a = 1;
  function say() {
    var c = 4
    console.log(c)
   console.log(a)
   var d = 5 } console.log(b)
console.log(a) say() var b = 2; </script>

  

  1、在 v8 引擎加載到這段代碼

  2、引擎解析代碼,創建一個 全局執行上下文(調用堆棧/內存棧)

   3、解析代碼,將全局的所有的變量/聲明函數添加到全局執行上下文的內存棧中。

  這里會將 變量 a 和 b / 函數 say 添加到內存棧中。所有的全局的變量和函數都會添加進去

  開始執行的樣子:

4.將全局的執行方法,都 push 到調用堆棧中

 

  步驟 1 :

  調用 console.log(b) 方法,執行到 console.log(b) 這一步的時候,全局執行上下文樣子:

  在從開始執行到這里的過程中,我們對一些變量做了賦值 a = 1 , b 並為賦值,仍是 undefined 。

  這里打印的是一個 undefined 

  這里就是為什么會出現 變量提升 的原因

  這里 console.log(b) 執行完成后,會直接 pop 彈出 調用堆棧。

 

 

   步驟 2 :

   調用 console.log(a) , 在console.log(b) 到 console.log(a) 並未有任何賦值操作。

   所以內存棧中未有任何變化。

  但是調用堆棧里面會有變化,在這里面console.log(b)已經執行完成,會被 pop 推出棧,然后到了console.log(a),試圖樣子:

 

 

  這里打印 a 是 1

  結束完成之后,console.log(a) 會被彈出棧。

 

 

  步驟 3:

  調用 say() 函數,將 say() push 的調用堆棧中。

  創建一個新的 say 的執行上下文。

  這里為 say 重新創建了一個 執行上下文 。並且將它內部的 變量和函數推入它對應的 內存棧 中。

  將 c : 4 , d : 5 推到 內存棧中。

 

 

   步驟 4 :

  解析 say 函數,並且執行內部的 console.log(c) , console.log(a) 。

  

  這里堆棧一次執行 console.log(c) , console.log(a) .

  執行完成之后會被彈出來。

  打印 4 , 5 。

  

 

  步驟 5 :

  當 say () 執行完畢后,回收內存棧已經調用棧。

  完成!!

 

  

 

  前端 javascript的 堆棧理解 

 

  在理解這些時候,我們可以先看下 值類型 和 引用類型,值類型 和 引用類型 的存儲方式來區分前端的堆棧。

  值類型/原始類型 : undefined 、null 、number 、string 、boolean

  引用類型/堆類型 : object 、function 、array 

 

  值類型 :

  保存在內存棧中,分配固定的空間,所以保存的值是不可變的。

  js 代碼執行的或者某個方法執行的時候,都會有自己獨立的執行上下文,其中包括內存棧和調用棧,該執行上下文中定義的值類型就會保存在該內存棧中。

  當該方法執行結束,這個內存棧就會銷毀,值類型也隨之移除

  

  值類型的特點:

  1、任何方法都無法改變值類型的

var name = 'xiao';
name.toUpperCase(); // 輸出 'XIAO'
console.log(name); // 輸出  'xiao'

  

  該字符串調用了 toUpperCase() 方法,但是並未改變 name 的值

  

  2、相同類型的值類型的比較是它們值的比較

  相同類型的值類型在比較時候,只有它們的值相等,則它們才相等。

 

  3、值類型都存放在內存棧中

  上面已經介紹過了。

 

  4、保存的是值本身的值,不是指向的地址。

 

  引用類型:

  值保存在堆內存中,占用的空間也不固定,所以保存的值是可以變的

  當我們創建一個對象的時候,我們會將該對象保存在運行的內存堆中,以便與反復利用。

  當被某一個變量引用的時候,會給變量該內存堆中的該對象的地址,並且把該對象的地址保存在內存棧中,而不是該對象的本身

  當引用該變量所在的方法或者是執行上下文執行結束的時候,並不會銷毀,只有在沒有被任何引用的時候,該對象才會被銷毀。

  這里的這種銷毀方法就是垃圾回收中的一種,叫做標記回收

  標記回收( 以對象為例,當這個對象每被引用一次,就在該對象的標記上面 +1 ,當某一個引用該對象的變量不引用了就 -1 ,當

  標記為 0 的時候,被系統檢測到之后,就會銷毀。 )

 

  引用類型的特點:

  1、引用類型的值是可變的

var obj = {a:1}
obj.b = 2

  

  2、引用類型在內存棧中保存對象地址,在堆內存中保存對象的本身

 

  3、引用類型的比較(===),是比較它們保存的對象的堆內存地址

 

  4、引用類型的賦值,當給某一個變量復制的時候,實際是將該對象的堆內存地址復制給變量

 

 

    

  總結 :

  前端的基礎(閉包,原型,作用域),在這里我們只要理解了執行上下文,我們就可以很好的區分作用域和閉包,

  因為它們都相當於一個方法,當它們執行的時候,都會相應的創建自己的執行上下文,並且有自己的內存棧和調用棧。

  當它們執行的時候都會對應的運用自己的內存中的變量。

 

  值類型和引用類型的區分,我們能很快的作出類型的比較。

  通過變量的存儲方式來區分每一個變量的種類和功能。  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM