免費學習推薦:#javascript學習教學
#一、什麼是JavaScript
1-1、JavaScript實作
在基本層面,描述這門語言:語法,類型,語句,關鍵字,保留字,操作符,全局對象
ECMAScript只是對實現這個規範描述的所有方面的一門語言的稱
ECMAScript。
1-2、DOM點,包含不同的資料。
DOM透過建立表示文件的樹,讓開發者可以隨心所欲地控制網頁的內容和結構。使用DOM API,可以輕鬆刪除、新增、取代、修改節點。
1-3、BOMIE3和Netscape Navigator 3提供了
瀏覽器物件模型(BOM) API,用於支援存取和操作瀏覽器的視窗。使用BOM,開發者可以操控瀏覽器顯示頁面之外的部分。而BOM真正獨一無二的地方,當然也是問題最多的地方,就是它是唯一沒有相關標準的JavaScript實作。 HTML5改變了這個局面,這個版本的HTML以正式規範的形式涵蓋了盡可能多的BOM特性。由於HTML5的出現,之前許多與BOM有關的問題都迎刃而解了。
二、HTML中的JavaScript
2-1、script
將JavaScript插入HTML的主要方法是使用script 元素。這
個元素是由網景公司創造出來,並最早在Netscape Navigator 2中實現
src :可選。表示包含要執行的程式碼的外部文件。
type :可選。取代 language ,表示程式碼區塊中腳本語言的內容類型(也稱為MIME類型)。
包含在script 內的程式碼會被從上到下解釋
#2-2、
##使用了src屬性的script 元素不應該再在script 和/script 標籤中再包含其他JavaScript程式碼。如果兩者都提供的話,則瀏覽器只會下載並執行腳本文件,從而忽略行內程式碼。 script 元素的一個最強大、同時也備受爭議的特性是,它可以包含來自外部網域的JavaScript檔案。跟img 元素很像, script 元素的src 屬性可以是一個完整的URL,而且這個URL指向的資源可以跟包含它的HTML頁面不在同一個域中2-3、文檔模式
可以使用doctype 切換文件模式。最初的文件模式有兩種:混雜模式(quirks mode)和標準模式(standards mode)。前者讓IE像IE5一樣(支援一些非標準的特性),後者讓IE有相容標準的行為。雖然這兩種模式的主要差異只體現在透過CSS渲染的內容方面,但對JavaScript也有一些關聯影響,或稱為副作用。本書會常常提到這些副作用。
IE初次支援文件模式切換以後,其他瀏覽器也跟著實現了。隨著瀏覽器的普遍實現,又出現了第三種文件模式:準標準模式(almost standards mode)。這種模式的瀏覽器支援許多標準的特性,但是沒有標準規定得那麼嚴格。主要差異在於如何對待圖片元素周圍的空白(在表格中使用圖片時最明顯)。
2-4、noscript 元素
針對早期瀏覽器不支援JavaScript的問題,需要一個頁面優雅降級的處理方案。最終, 元素出現,用來提供不支援JavaScript的瀏覽器替代內容。雖然如今的瀏覽器已經100%支援JavaScript,但對於停用JavaScript的瀏覽器來說,這個元素仍然有它的用處。 元素可以包含任何可以出現在中的HTML元素,三、語法基礎
3-1、語法
首先要知道的是,ECMAScript中一切都區分大小寫。無論是變數、函數名或操作符,都區分大小寫。換句話說,變數 test 和變數 Test 是兩個不同的變數。類似地, typeof 不能當函數名,因為它是一個關鍵字(後面會介紹)。但 Typeof 是一個完全有效的函數名稱。3-2、標識符
所謂標識符,就是變數、函數、屬性或函數參數的名稱。標識符可以由一個或多個下列字元組成:3-3、嚴格模式
ECMAScript 5增加了嚴格模式(strict mode)的概念。嚴格模式是一種不同的JavaScript解析和執行模型,ECMAScript 3的一些不規範寫法在這種模式下會被處理,對於不安全的活動將拋出錯誤3-4 、關鍵字與保留字
ECMA-262描述了一組保留的關鍵字,這些關鍵字有特殊用途,例如表示控制語句的開始和結束,或執行特定的操作ECMA-262第6版規定的所有關鍵字:
break do in typeof case else instanceof var catch export new void class extends return while const finally super with continue for switch yield debugger function this default if throw delete import try
3-5、變數
ECMAScript變數是鬆散類型的,意思是變數可以用來保存任何類型的資料。每個變數只不過是一個用來保存任意值的命名佔位符。有3個關鍵字可以宣告變數: var 、 const 和 let 。其中, var 在CMAScript的所有版本中都可以使用,而const 和let 只能在ECMAScript 6及更晚的版本中使用3-6、var關鍵字
var宣告作用域
關鍵的問題在於,使用 var 運算子定義的變數會成為包含它的函數的局部變數。例如,使用var 在一個函數內部定義一個變量,就意味著該變量將在函數退出時被銷毀
var 聲明提升
使用var 時,下面的程式碼不會報錯。這是因為使用這個關鍵字宣告的變數會自動提升到函數作用域頂部
#3-7、let 宣告
let 跟var 的作用差不多,但有著非常重要的差異。最明顯的差異是, let 宣告的範圍是區塊作用域,而 var 宣告的範圍是函數作用域。
暫時性死區
let 與 var 的另一個重要的區別,就是 let 宣告的變數不會在作用域中被提升。
全域宣告
與var 關鍵字不同,使用let 在全域作用域中宣告的變數不會成為window 物件的屬性( var 宣告的變數則會)
條件宣告
在使用var 宣告變數時,由於宣告會被提升,JavaScript引擎會自動將多餘的宣告在作用域頂端合併為一個宣告。因為let 的作用域是區塊,所以不可能檢查前面是否已經使用let 宣告過同名變量,同時也不可能在沒有宣告的情況下宣告它
for 迴圈中的let 宣告
在let 出現之前, for 迴圈定義的迭代變數會滲透到迴圈體外部
const 宣告
const 的行為與let 基本相同,唯一一個重要的區別是用它宣告變數時必須同時初始化變量,且嘗試修改const 宣告的變數會導致執行時期錯誤。
3-8、聲明風格及最佳實踐
ECMAScript 6增加let 和const 從客觀上為這門語言更精確地聲明作用域和語意提供了更好的支援。行為怪異的 var 所造成的各種問題,已經讓JavaScript社群為此苦惱了很多年。隨著這兩個新關鍵字的出現,新的有助於提升程式碼品質的最佳實踐也逐漸顯現。
四、資料型別
ECMAScript有6種簡單資料型別(也稱為原始型別):Undefined 、 Null 、 Boolean 、 Number 、 String 和Symbol 。 Symbol (符號)是ECMAScript 6新增的。還有一種複雜資料型別叫 Object (物件)。 Object 是一種無序名值對的集合。因為在ECMAScript中不能定義自己的資料類型,所有值都可以用上述7種資料類型之一來表示。只有7種資料類型似乎不足以表示全部資料。但ECMAScript的資料型別很靈活,一種資料型別可以當作多種資料型別來使用
4-1、typeof 運算子
##因為ECMAScript的型別系統是鬆散的,所以需要一種手段來確定任意變數的資料類型。 typeof 操作符就是為此而生的。對一個值使用typeof 運算元會傳回下列字串之一: “undefined” 表示值未定義;
“boolean” 表示值為布林值;
“string” 表示值為字串;
“number” 表示值為數值;
“object” 表示值為物件(而非函數)或null ;
“function” 表示值為函數;
“symbol” 表示值為符號。
4-2、Undefined 類型
Undefined 類型只有一個值,就是特殊值 undefined 。當使用var 或let 宣告了變數但沒有初始化時,就相當於給變數了undefined 值即使未初始化的變數會被自動賦予undefined 值,但我們仍然建議在宣告變數的同時進行初始化。這樣,當 typeof 回傳 “undefined” 時,你就會知道那是因為給定的變數尚未聲明,而不是聲明了但未初始化。4-3、Null 類型
Null 類型同樣只有一個值,即特殊值 null 。邏輯上講,null 值表示一個空物件指針,這也是給typeof 傳一個null 會返回「object」 的原因在定義將來要保存物件值的變數時,建議使用null 來初始化,不要使用其他值。這樣,只要檢查這個變數的值是不是 null 就可以知道這個變數是否在後來被重新賦予了一個物件的引用用等於運算元( == )比較 null 和 undefined 總是傳回true 。但要注意,這個運算子會為了比較而轉換它的運算元。
即使 null 和 undefined 有關係,它們的用途也是完全不一樣的。如前所述,永遠不必明確地將變數值設為 undefined 。但null 不是這樣的。任何時候,只要變數要保存對象,而當時又沒有那個對象可保存,就要用 null 來填入該變數。這樣就可以保持null 是空物件指標的語義,並進一步將其與 undefined 區分開來。
null 是一個假值。因此,如果需要,可以用更簡潔的方式檢測它。不過要記住,也有很多其他可能的值同樣是假值。所以一定要明確自己想偵測的就是null 這個字面上值,而不僅僅是假值
4-4、Boolean 類型
Boolean (布林值)類型是ECMAScript中使用最頻繁的類型之一,有兩個字面值: true 和false 。這兩個布林值不同於數值,因此true 不等於1, false 不等於0
注意:布林值字面量true 和false 是區分大小寫的,因此True 和False (及其他大小混合形式)是有效的標識符,但不是布林值。
4-5、Number 類型
ECMAScript中最有意思的資料型態或許就是 Number 了。 Number 類型使用IEEE 754格式表示整數和浮點值(在某些語言中也叫雙精度值)。不同的數值類型對應地也有不同的數值字面量格式
浮點值
要定義浮點數值,數值中必須包含小數點,而小數點後面必須至
少有一個數字。雖然小數點前面不是必須有整數,但建議加上。
值的範圍
由於記憶體的限制,ECMAScript並不支援表示這個世界上的所有數值。 ECMAScript可以表示的最小數值保存在Number.MIN_VALUE 中,這個值在多數瀏覽器中是5e-324;可以表示的最大數值保存在Number.MAX_VALUE 中,這個值在多數瀏覽器中是1.797 693 134 862 315 7e 308。如果某個計算得到的數值結果超出了JavaScript可以表示的範圍,那麼這個數值就會自動轉換成一個特殊的 Infinity (無限)值。任何無法表示的負數以 -Infinity (負無窮大)表示,任何無法表示的正數以 Infinity (正無窮大)表示。如果計算回傳正 Infinity 或負 Infinity ,則該值將無法再進一步用於任何計算。這是因為 Infinity 沒有可用於計算的數值表示形式。要確定一個值是不是有限大(即介於JavaScript能表示的最小值和最大值之間),可以使用isFinite() 函數
NaN
有一個特殊的數值叫NaN ,意思是「不是數值」(Not a Number),用來表示本來要回傳數值的操作失敗了(而不是拋出錯誤)。例如,用0除任意數值在其他語言中通常都會導致錯誤,從而中止程式碼執行。但在ECMAScript中,0、 0或-0相除會傳回NaN
#數值轉換
有3個函數可以將非數值轉換為數值: Number() 、parseInt () 和parseFloat() 。 Number() 是轉型函數,可用於任何資料類型。後兩個函數主要用於將字串轉換為數值。對於同樣的參數,這3個函數執行的操作也不同。
4-6、 NaN
有一個特殊的數值叫NaN ,意思是「不是數值」(Not aNumber),用於表示本來要傳回數值的操作失敗了(而不是拋出錯誤)。
4-7、數值轉換
有3個函數可以將非數值轉換為數值: Number() 、 parseInt() 和 parseFloat() 。 Number() 是轉型函數,可用於任何資料類型。後兩個函數主要用於將字串轉換為數值。對於同樣的參數,這3個函數執行的操作也不同。 Number() 函數基於下列規則執行轉換。布林值, true 轉換為1, false 轉換為0。
數值,直接回傳。 null ,回傳0。 undefined ,返回 NaN 。
4-8、String 類型
String (字串)資料類型表示零或多個16位元Unicode字元序列。字串可以使用雙引號(")、單引號(')或反引號(`)標示,
字元字面量
字串資料型別包含一些字元字面量,用於表示非列印字元或有其他用途的字元
字串的特點
ECMAScript中的字串是不可變的(immutable),意思是一旦創建,它們的值就不能改變了。要修改某個變數中的字串值,必須先銷毀原始的字串,然後將包含新值的另一個字串儲存到該變數
轉換為字串
有兩種方式把一個值轉換為字串。首先是使用幾乎所有值都有的toString() 方法。這個方法唯一的用途就是傳回目前值的字串等價物
模板字面量
ECMAScript 6新增了使用模板字面量定義字串的能力。與使用單引號或雙引號不同,模板字面量保留換行字符,可以跨行定義字串
字串插值
模板字面量最常用的一個特性是支援字符串插值,也就是可以在一個連續定義中插入一個或多個值。技術上講,模板字面量不是字串,而是一種特殊的JavaScript句法表達式,只不過求值後得到的是字串。模板字面量在定義時立即求值並轉換為字串實例,任何插入的變數也會從它們最接近的作用域中取值
模板字面量也支援定義標籤函數(tag function),而透過標籤函數可以自訂插值行為。標籤函數會接收被插值記號分隔後的範本和對每個表達式求值的結果。標籤函數本身是一個常規函數,透過前綴到模板字面量來應用自訂行為,
使用模板字面量也可以直接取得原始的模板字面量內容(如換行符或Unicode字元),而不是被轉換後的字元表示。為此,可以使用預設的String.raw 標籤函數
五、操作符
ECMA-262描述了一組可用於操作資料值的操作符,包括數學操作符(如加、減)、位元操作符、關係運算子和相等操作符等。 ECMAScript中的運算元是獨特的,因為它們可用於各種值,包括字串、數值、布林值,甚至還有物件。在套用給物件時,操作符通常會呼叫 valueOf() 和 / 或 toString() 方法來取得可以計算的值。 3.5.1 一元運算子只操作一個值的運算子叫一元運算子(unary operator)。一元操作符是ECMAScript中最簡單的操作符。5-1、一元運算子
一元加和減運算子對大多數開發者來說並不陌生,它們在ECMAScript中跟在高中數學中的用途一樣。一元加由一個加號( )表示,放在變數前頭,對數值沒有任何影響
5-2、位元運算子
位元非運算子以波浪符( ~ )表示,它的作用是傳回數值的一補數。位元非是ECMAScript中為數不多的幾個二進位數學運算子之一
位元與運算子用和號( & )表示,有兩個操作數。本質上,位元與就是將兩個數的每一個位元對齊,然後基於真值表中的規則,對每一位執行相應的與操作。
按位或運算子以管道符( | )表示,同樣有兩個運算元。
以位元異或以脫字元( ^ )表示,同樣有兩個運算元
左移運算子以兩個小於號( << )表示,會依照指定的位元數將數值的所有位元向左移動。例如,如果數值2(二進位10)向左移5位,就會得到64(二進位1000000)
邏輯非運算子由嘆號( ! )表示,可套用至ECMAScript中的任何值。這個操作符始終返回布林值,無論應用到的是什麼資料類型。邏輯非運算子先將運算元轉換為布林值,然後再對其取反
六、語句
ECMA-262描述了一些語句(也稱為流控制語句),而ECMAScript中的大部分語法都體現在語句中。語句通常會使用一或多個關鍵字完成既定的任務。語句可以簡單,也可以複雜。簡單的如告訴函數退出,複雜的如列出一堆要重複執行的指令。
6-1、do-while 語句
do-while 語句是一種後測試循環語句,即循環體中的程式碼執
行後才會對退出條件進行求值。換句話說,循環體內的程式碼至少執行
一次
6-2、while 語句
while 語句是先測試迴圈語句,也就是先偵測退出條件,再執行循環體內的程式碼。因此, while 迴圈體內的程式碼有可能不會執行
七、函數
函數對任何語言來說都是核心元件,因為它們可以封裝語句,然後在任何地方、任何時間執行。 ECMAScript中的函數使用function 關鍵字聲明,後面跟著一組參數,然後是函數體。
ECMAScript中的函數不需要指定是否傳回值。任何函數在任何時間都可以使用 return 語句來傳回函數的值,用法是後面要傳回的值
函數 sum() 會將兩個值相加並傳回結果。請注意,除了return語句之外沒有任何特殊聲明表明該函數有返回值
嚴格模式對函數也有一些限制:
函數不能以eval 或arguments 作為名稱;
函數的參數不能叫eval 或arguments ;
兩個函數的參數不能叫同一個名稱。
八、變量,作用域與記憶體
4-1、原始值與引用值
ECMAScript變數可以包含兩種不同類型的資料:原始值和參考值。原始值(primitive value)就是最簡單的數據,而引用值(referencevalue)則是由多個值所構成的物件。
在把一個值賦給變數時,JavaScript引擎必須確定這個值是原始值還是引用值。上一章討論了6種原始值: Undefined 、 Null 、Boolean 、 Number 、 String 和 Symbol 。保存原始值的變數是按值(by value)存取的,因為我們操作的就是儲存在變數中的實際值。
引用值是保存在記憶體中的物件。與其他語言不同,JavaScript不允許直接存取記憶體位置,因此也無法直接操作物件所在的記憶體空間。在操作物件時,實際上操作的是對該物件的參考(reference)而非實數
際的物件本身。為此,保存引用值的變數是按引用(by reference)訪問的
注意:在許多語言中,字串是使用物件表示的,因此被認為是引用類型。 ECMAScript打破了這個慣例。
動態屬性
原始值和參考值的定義方式很類似,都是建立一個變量,然後給它一個值。不過,在變數保存了這個值之後,可以對這個值做什麼,則大有不同。對於引用值而言,可以隨時新增、修改和刪除其屬性和方法
複製值
除了儲存方式不同,原始值和引用值在透過變數複製時也有所不同。在透過變數把一個原始值賦值到另一個變數時,原始值會被複製到新變數的位置。
在按值傳遞參數時,值會被複製到一個局部變數(即一個命名參數,或用ECMAScript的話說,就是arguments 物件中的一個插槽)。在按引用傳遞參數時,值在記憶體中的位置會被保存在一個局部變量,這意味著對本地變數的修改會反映到函數外部
依照定義,所有參考值都是Object 的實例,因此透過instanceof 操作子偵測任何參考值和Object 建構子都會傳回true 。類似地,如果用 instanceof 檢測原始值,則始終會傳回 false ,因為原始值不是物件。
注意 typeof 運算子在用於偵測函數時也會傳回 “function” 。當在Safari(直到Safari 5)和Chrome(直到Chrome 7)中用於檢測正規表示式時,由於實作細節的原因,typeof 也會傳回 “function” 。 ECMA-262規定,任何實作內部 [[Call]] 方法的物件都應該在 typeof 偵測時傳回 “function” 。因為上述瀏覽器中的正規表示式實作了這個方法,所以 typeof 對正規表示式也回傳 “function” 。在IE和Firefox中, typeof 會對正規表示式傳回 “object” 。
4-2、執行上下文與作用域
執行上下文(以上簡稱「上下文」)的概念在JavaScript中是相當重要的。變數或函數的上下文決定了它們可以存取哪些數據,以及它們的行為。每個上下文都有一個關聯的變數物件(variable object),而
這個上下文中定義的所有變數和函數都存在於這個物件上。雖然無法透過程式碼存取變數對象,但後台處理資料會用到它。
全域上下文是最外層的上下文。根據ECMAScript實現的宿主環境,表示全域上下文的物件可能不一樣。在瀏覽器中,全域上下文就是我們常說的 window 物件(第12章會詳細介紹),因此所有透過
var 定義的全域變數和函數都會成為 window 物件的屬性和方法。使用 let 和 const 的頂級聲明不會定義在全域上下文中,但在作用域鏈解析上效果是一樣的。上下文在其所有程式碼都執行完畢後會被銷毀,包括定義在它上面的所有變數和函數(全域上下文在應用程式退出前才會被銷毀,例如關閉網頁或退出瀏覽器)。
每個函數呼叫都有自己的上下文。當程式碼執行流進入函數時,函數的上下文被推到一個上下文堆疊上。在函數執行完之後,上下文堆疊會彈出該函數上下文,將控制權回饋給先前的執行上下文。 ECMAScript
程式的執行流程就是透過這個上下文堆疊來控制的。上下文中的程式碼在執行的時候,會建立變數物件的一個作用域鏈(scope chain)。這個作用域鏈決定了各級上下文中的程式碼在存取變數和函數時的順序。程式碼正在執行的上下文的變數物件始終位於作用域鏈的最前端。如果上下文是函數,則其活動物件(activationobject)用作變數物件。活動物件最初只有一個定義變數:arguments 。 (全域上下文中沒有這個變數。)作用域鏈中的下一個變數物件來自包含上下文,再下一個物件來自再下一個包含上下文。以此類推直至全局上下文;全域上下文的變數物件始終是作用域鏈的最後一個變數物件
作用域鏈增強
雖然執行上下文主要有全域上下文和函數上下文兩種( eval()呼叫內部存在第三種上下文),但有其他方式來增強作用域鏈。某些語句會導致在作用域鏈前端暫時加入一個上下文,這個上下文會在程式碼
執行後被刪除。
變數宣告
ES6之後,JavaScript的變數宣告經歷了翻天覆地的變化。直到
ECMAScript 5.1, var 都是宣告變數的唯一關鍵字。 ES6不僅增加了
let 和 const 兩個關鍵字,而且還讓這兩個關鍵字壓倒性地超越
var 成為首選。
使用 var 的函數作用域宣告
在使用 var 宣告變數時,變數會自動加入到最接近的上下文。在函數中,最接近的上下文就是函數的局部上下文。在with 語句中,最接近的上下文也是函數上下文。如果變數未經宣告就被初始化了,那麼它就會自動被加入到全域上下文注意:未經宣告而初始化變數是JavaScript程式設計中一個非常常見的錯誤,會導致很多問題。為此,讀者在初始化變數之前一定要先宣告變數。在嚴格模式下,未經宣告就初始化變數會錯誤
var 宣告會被拿到函數或全域作用域的頂部,位於作用域中所有程式碼之前。這個現象叫做「提升」(hoisting)。提升讓同一作用域中的程式碼不必考慮變數是否已經宣告就可以直接使用。可是在實踐中,提升也會導致合法卻奇怪的現象,即在變數宣告之前使用變數。
使用let 的區塊級作用域宣告
ES6新增的let 關鍵字跟var 很相似,但它的作用域是區塊級的,這也是JavaScript中的新概念。區塊級作用域由最近的一對包含花括號 {} 界定。換句話說, if 區塊、 while 區塊、 function區塊,甚至連單獨的區塊也是 let 宣告變數的作用域。
使用 const 的常數宣告
除了 let ,ES6同時也增加了 const 關鍵字。使用 const 宣告的變數必須同時初始化為某個值。一經聲明,在其生命週期的任何時候都不能再重新賦予新值
#注意開發實踐表明,如果開發流程並不會因此而受很大影響,就應該盡可能地使用const 聲明,除非確實需要一個將來會重新賦值的變數。這樣可以從根本上保證提前發現重新賦值導致的bug
4-3、垃圾回收
JavaScript是使用垃圾回收的語言,也就是說執行環境負責在程式碼執行時管理記憶體。在C和C 等語言中,追蹤記憶體使用對開發者來說是個很大的負擔,也是許多問題的來源。 JavaScript為開發者卸下了這個負擔,透過自動記憶體管理實現記憶體分配和閒置資源回收。基本想法很簡單:確定哪個變數不會再使用,然後釋放它佔用的記憶體。這個過程是週期性的,即垃圾回收程序每隔一定時間(或說在程式碼執行過程中某個預定的收集時間)就會自動運行。垃圾回收過程是一個近似且不完美的方案,因為某塊記憶體是否還有用,屬於「不可判定的」問題,意味著靠演算法是解決不了的。
我們以函數中局部變數的正常生命週期為例。函數中的局部變數會在函數執行時存在。此時,堆疊(或堆疊)記憶體會分配空間以保存對應的值。函數在內部使用了變量,然後退出。此時,就不再需要那個局部變數了,它佔用的記憶體可以釋放,供後面使用。這種情況下顯然不再需要局部變數了,但並不是所有時候都會這麼明顯。垃圾回收程式必須追蹤記錄哪個變數還會使用,以及哪個變數不會再使用,以便回收記憶體。如何標記未使用的變數也許有不同的實作方式。不過,在瀏覽器的發展史上,用過兩種主要的標記策略:標記清理和引用計數。
標記清理
JavaScript最常用的垃圾回收策略是標記清理(mark-and-sweep)。當變數進入上下文,例如在函數內部宣告變數時,這個變數會被加上存在於上下文中的標記。而不在上下文中的變量,邏輯上講,永遠不應該釋放它們的內存,因為只要上下文中的程式碼在運行,就有可能用到它們。當變數離開上下文時,也會被加上離開上下文的標記。
給變數加標記的方式有很多種。例如,當變數進入上下文時,反轉某一位;或者可以維護「在上下文中」和「不在上下文中」兩個變數列表,可以把變數從一個列表轉移到另一個列表。標記過程的實作並不重要,關鍵是策略。
垃圾回收程式運行的時候,會標記記憶體中儲存的所有變數(記住,標記方法有很多種)。然後,它會將所有在上下文中的變量,以及被在上下文中的變量引用的變量的標記去掉。在此之後再被加上標記的變數就是待刪除的了,原因是任何在上下文中的變數都無法存取它們了。隨後垃圾回收程序做一次記憶體清理,銷毀帶標記的所有值並收回它們的記憶體。
到了2008年,IE、Firefox、Opera、Chrome和Safari都在自己的JavaScript實作中採用標記清理(或其變體),只是在運行垃圾回收的頻率上有所差異。
引用計數
另一種沒那麼常用的垃圾回收策略是引用計數(referencecounting)。其想法是對每個值都記錄它被引用的次數。宣告變數並給它賦一個引用值時,這個值的引用數為1。如果同一個值又被賦給另一個變量,那麼引用數加1。類似地,如果保存對該值引用的變數被其他值給覆蓋了,那麼引用數減1。當一個值的引用數為0時,就表示沒辦法再訪問到這個值了,因此可以安全地收回其記憶體了。垃圾回收程式
下次執行的時候就會釋放引用數為0的值的記憶體。引用計數最早由Netscape Navigator 3.0採用,但很快就遇到了嚴重的問題:循環引用。所謂循環引用,就是物件A有一個指標指向物件B,而物件B也引用了物件A。
待更新。 。 。
相關免費學習推薦:javascript(影片)
以上是記錄JavaScript的學習筆記的詳細內容。更多資訊請關注PHP中文網其他相關文章!