4.1 基本類型和引用類型的值前端
再將一個值賦給變量時,解析器必須肯定這個值是基本類型值仍是引用類型值。web
基本數據類型:undefined、null、Boolean、Number和String。這5種基本數據類型是按值訪問的,由於能夠操做保存在變量中的實際的值。數組
引用類型的值是保存在內存中的對象。JS不容許直接訪問內存中的位置,也就是說不能直接操做對象的內存空間。在操做對象時,其實是在操做對象的引用而不是實際的對象。爲此,引用類型的只是按引用訪問的。瀏覽器
4.1.1 動態的屬性安全
對於引用類型的值,咱們能夠爲其添加屬性和方法,也能夠改變和刪除其屬性和方法。不能給基本類型的只添加屬性閉包
4.1.2 複製變量值函數
複製基本類型:若是從一個變量向另外一個變量複製基本類型的值,會在變量對象上建立一個新值,兩個變量參與任何操做而不會相互影響。性能
複製引用類型:兩個變量將引用同一個對象。改變其中一個變量,就會影響另外一個變量。因此一個指向存儲在堆中的一個對象最好只有一個變量。優化
4.1.3 傳遞參數spa
ECMAScript中全部函數的參數都是按值傳遞的。把函數外部的值複製給函數內部的參數,就和把值從一個變量複製到另外一個變量同樣。基本類型值的傳遞如同基本類型變量的複製同樣。而引用類型值的傳遞,則如同引用類型變量的複製同樣。
在向參數傳遞引用類型的值時,會把這個值在內存中的地址複製給一個局部變量,所以這個局部變量的變量會反映在函數的外部。
在函數內部重寫obj = new Object(),這個變量引用的就是一個局部對象了,而這個局部對象會在函數執行完畢後被銷燬。
4.1.4 檢測類型
ECMAScript提供了instanceof操做符來判斷某個值是什麼類型的對象
result = variable instanceof constructor
若是變量是給定引用類型(根據它的原型鏈來識別)的實例,那麼instance操做符就會返回true
根據規定,全部引用類型的值都是Object的實例。所以,在檢測一個引用類型值和Object構造函數時,instanceof操做符始終會返回true。
4.2 執行環境及做用域
執行環境定義了變量或函數有權訪問的其餘數據,決定了它們各自的行爲。每一個執行環境都有一個與之關聯的變量對象,環境中定義的全部變量和函數都保存在這個對象中。雖然咱們編寫的代碼沒法訪問這個對象,但解析器在處理數據時會在後臺使用它。
全局執行環境是最外圍的一個執行環境。根據ECMAScript實現所在的宿主環境不一樣,表示執行環境的對象也不同。在web瀏覽器中,全局執行環境被認爲是window對象,所以全部全局變量和函數都是做爲window對象的屬性和方法建立的。某個執行環境中的全部代碼執行完畢後,該環境被銷燬,保存在其中的全部變量和函數定義也隨之銷燬(全局執行環境直到應用程序退出,例如:關閉網頁或瀏覽器時纔會被銷燬)。
每一個函數都有本身的執行環境。當執行流進入一個函數時,函數的環境就會被推入一個環境棧中。而在環境執行以後,棧將其環境彈出,把控制權返回給以前的執行環境。ECMAScript程序中的執行流正是有這個方便的機制控制着。
當代碼在一個環境中執行時,會建立變量對象的一個做用域鏈。做用域鏈的用途,是保證對執行環境有權訪問的全部變量和函數的有序訪問。做用域鏈的前端,始終都是當前執行的代碼所在環境的變量對象。若是這個環境是函數,則將其活動對象做爲變量對象,活動對象在最開始時只包含一個變量,即arguments對象(這個對象在全局環境中是不存在的)。做用域鏈中的下一個變量對象來自包含環境……。一直延續到全局執行環境;全局執行環境的變量對象始終都是做用域鏈中的最後一個對象。
標識符解析是沿着做用域鏈一級一級地搜索標識符的過程。搜索過程始終從做用域鏈的前端開始,而後逐級地向後回溯,直至找到標識符爲止。(若是找不到標識符,一般會致使錯誤發生。)
4.2.1 延長做用域鏈
有寫語句能夠在做用域鏈的前端臨時增長一個變量對象,該變量對象會在代碼執行後被移除。也就是說當執行流進去下列任何一個語句時,做用域鏈就會獲得加長:
a. try-catch語句的catch塊;
b. with語句
對with語句來講,會將指定的對象添加到做用域鏈中。對catch語句來講,會建立一個新的變量對象,其中包含的是被跑出的錯誤對象的聲明。
4.2.2 沒有塊級做用域
在JS中,控制流語句(包括with)中的變量聲明會將變量添加到當前的執行環境中。
a. 聲明變量
使用var聲明的變量會自動被添加到最接近的環境中。在函數內部,最接近的環境就是函數的局部環境;在with語句中,最接近的環境是函數環境。若是初始化變量時沒有使用var 聲明,該變量會自動被添加到全局環境。
b.查詢標識符
當在某個環境中爲了讀取或寫入而引用一個標識符時,必須經過搜索來肯定該標識符實際表明什麼。若是在局部環境中找到了該標識符,搜索過程中止,變量就緒。若是在局部環境中沒有找到該變量名,則繼續沿做用域鏈向上搜索。搜索過程將一直追溯到全局環境的變量對象。若是在全局環境中也沒有找到這個標識符,則意味着該變量還沒有聲明
4.3 垃圾收集
JS具備自動垃圾收集機制,也就是說執行環境會負責管理代碼執行過程當中使用的內存。這種垃圾收集機制的原理其實很簡單:找出那些不能繼續使用的變量,而後釋放其佔用的內存。爲此,垃圾收集器會按照固定的時間間隔,週期性地執行這一操做。
咱們來分析一下函數中局部變量的正常生命週期。局部變量值在函數執行的過程當中存在。而在這個過程當中,會爲局部變量在棧(或堆)內存上分配相應的空間,以便存儲它們的值。而後再函數中使用這些變量,直至函數執行結束。此時局部變量就沒有存在必要了,所以能夠釋放它們的內存以供未來使用。垃圾收集器必須跟蹤哪一個變量有用哪一個變量沒用,對於再也不有用的變量打上標記,以備未來收回其佔用的內存。
用於標識無用變量的策略可能會因實現而異,但具體到瀏覽器中的實現,則一般有兩個策略。
4.3.1 標記清除
絕大多數瀏覽器使用的都是標記清除式的垃圾回收策略。當變量進入環境時,就將這個變量標記爲「進入環境」。從邏輯上講,永遠不能釋放進入環境的變量所佔用的內存,由於只要執行流進入相應的環境,就可能會用到它們。而當變量離開環境時,則將其標記爲「離開環境」。
垃圾收集器在運行的時候會給存儲在內存中的全部變量都加上標記(「進入環境」)。而後,它會去掉環境中的變量以及被環境中的變量引用的變量的標記(閉包做用域裏的變量爲何還能夠訪問的緣由?就是這個吧)。而在此以後再被加上標記(「離開環境」)的變量將會被視爲準備刪除的變量,緣由是環境中變量已經沒法訪問到這些變量了。最後,垃圾收集器完成內存清楚工做,銷燬那些帶標記的值並回收它們所佔用的內存空間
4.3.2 引用計數
引用計數不太常見。引用計數的含義是跟蹤記錄每一個值被引用的次數。當聲明瞭一個變量並將一個引用類型值賦給該變量時,則這個值得引用次數就是1。若是同一個值又被賦給另外一個變量,則該值的引用次數加1。相反,若是包含對這個值引用的變量又取得了另一個值,則這個值的的引用次數減1。當這個值得引用次數變成0時,則說明沒有辦法在訪問這個值了,於是就能夠將其佔用的內存空間回收回來。
4.3.3 性能問題
垃圾收集的時間間隔是一個重要的問題。IE6的垃圾收集器是根據內存分配量運行的,具體一點就是256個變量、4096個對象字面量和數組元素或者64KB的字符串。達到上述任何一個臨界值,垃圾收集器就會運行。這種實現方式的問題在於, 若是一個腳本中包含那麼多變量,那麼改腳本極可能會在其生命週期中一致保有那麼多的變量。而這樣一來,垃圾收集器就不得不頻繁地運行,會引起嚴重的性能問題。
隨着IE7的發佈,其JS引擎的垃圾收集歷程改變了工做方式:觸發垃圾收集的變量分配、字面量或數組元素的臨界值倍調整爲動態修正。IE7中的各項臨界值在初始與IE6相等。若是垃圾收集歷程回收的內存分配率低於15%,則臨界值就會加倍。若是歷程回收了85%的內存分配量,則將各類臨界值重置回默認值。這一看似簡單的調整,極大地提高了IE在運行包含大量JS的頁面的性能。
4.3.4 管理內存
使用具有垃圾收集機制的語言編寫程序,開發人員通常沒必要操做內存管理的問題。可是JS在進行內存管理及垃圾收集時面臨的問題仍是有點不同凡響。其中最主要的一個問題,就是分配給Web瀏覽器的可用內存數量一般要比分配給桌面應用程序的少。這樣作的目的主要是出於安全方面的考慮,目的是防止運行JS的網頁耗盡所有系統內存而致使系統崩潰。內存限制問題不只會影響給變量分配內存,同時還會影響調用棧以及在一個線程中可以同時執行的語句數量。
優化內存佔用的最佳方式,就是爲執行中的代碼只保存必要的數據。一旦數據再也不有用,最好經過將其值設置爲null來釋放其引用——這個作法叫作解除引用。這一作法適用於大多數全局變量和全局對象的屬性。局部變量會在它們離開執行環境時自動被解除引用。不過解除一個值的引用並不意味着自動回收該值所佔用的內存。解除引用的真正做用是讓值脫離執行環境,以便垃圾收集器運行時將其回收。
4.4 小結
不一樣數據類型值的變量的複製
執行環境
垃圾回收機制