JavaScript 誕生于 1995 年。
一個完整的 JavaScript 實現由下列三個不同的部分組成:
ECMA-262 標準沒有參照 web 瀏覽器,它規定了這門語言的下列組成部分:語法、類型、語句、關鍵字、保留字、操作符、對象。
文檔對象模型(DOM,Document Object Model)是針對 XML 但經過擴展用于 HTML 的應用程序編程接口(API)。
瀏覽器對象模型(BOM,Browser Object Model)從根本上將,BOM 只處理瀏覽器窗口和框架,但人們習慣上也把所有針對瀏覽器的 JavaScript 擴展算作 BOM 的一部分,下面就是一些這樣的擴展:
JavaScript 是一種專門與網頁交互而設計的腳本語言,由下列三個部分組成:
向 HTML 頁面中插入 JavaScript 的主要方法就是使用 <script>
元素。
使用 <script>
元素的方式有兩種:直接在頁面中嵌入 JavaScript 代碼和包含外部 JavaScript 文件。
注意:使用 <script>
引入外部 JavaScript 文件時,不要在標簽內添加額外的 JS 代碼,如果添加了將會被忽略。
按照傳統的做法,所有 <script>
元素都應該放在頁面中 <head>
元素中,這種做法的目的就是把所有外部文件(CSS文件和JavaScript文件)的引用都放在相同的地方。
可是這種做法有一種缺點就是,必須要等到全部 JS 代碼都被下載、解析和執行完成以后,才能開始出現頁面的內容(瀏覽器在遇到 <body>
標簽時才開始出現內容)。對于需要很多 JS 代碼的頁面來說,這會導致頁面出現明顯的延遲,而在延遲期間頁面是一片空白。所以,為了避免這個問題,我們應該把 JS 引用放在 <body>
結束標簽前面,即:
<body>
<!-- html代碼 -->
<script type="text/javascript" src="index.js"></script>
</body>
<script>
標簽的 defer 屬性用途是表明腳本在執行時不會影響頁面的構造。也就是說,腳本會被延遲到整個頁面都解析完畢后再運行。因此,在 <script>
元素中設置 defer 屬性,相當于告訴瀏覽器立即下載,但延遲執行。
假設把 <script>
元素放在 <head>
元素之中,并加上 defer 屬性,但其中包含的腳本將延遲到瀏覽器遇到 </html>
標簽后再執行。HTML5 規范要求腳本按照它們出現的先后順序執行,因此第一個延遲腳本會先于第二個延遲腳本執行,而這兩個腳本會先于 DOMContentLoaded 事件執行。但在現實中,延遲腳本不一定會按順序執行,也不一定會在 DOMContentLoaded 事件觸發前執行,因此最好只包含一個延遲腳本。
defer 屬性只適用于外部腳本文件。
HTML5 為 <script>
元素定義了 async 屬性。與 defer 屬性類似,都用于改變處理腳本的行為,也只適用于外部腳本文件,并告訴瀏覽器立即下載文件。但不同的是,標記為 async 的腳本并不保證按照指定它們的先后順序執行。
例如,在 <head>
元素中放兩個 <script>
元素。第二個腳本文件可能會在第一個腳本文件前執行。指定 async 屬性的目的是不讓頁面等待兩個腳本下載和執行,從而異步加載頁面其他內容。因此,建議異步腳本不要在加載期間修改 DOM。
異步腳本一定會在頁面的 load 事件前執行,但可能會在 DOMContentLoaded 事件觸發前或后執行。
使用外部文件的優點:
兩種文檔模式:混雜模式(quirks mode)、標準模式(standards mode)。
這個元素用以在不支持 JS 的瀏覽器中顯示替代的內容。
包含 <noscript>
元素中的內容只有在下面的情況才會顯示出來:
ECMAScript 中的一切(變量、函數名和操作符)都區分大小寫。
標識符就是指變量、函數、屬性的名字,或函數的參數。
標識符按照以下規則組合:
不能把關鍵字、保留字、true、false、null 用作標識符。
// 單行注釋
/*
這是多行注釋
這是多行注釋
*/
嚴格模式是為 JS 定義了一種不同的解析與執行模型。在嚴格模式下,一些不確定的行為將得到處理,對某些不安全的操作會拋出錯誤。
要在整個腳本中啟用嚴格模式,可以在頂部添加下列代碼:
"use strict";
它是一個編譯指示(pragma),用來告訴支持 JS 引擎切換到嚴格模式。
可以指定函數在嚴格模式下執行:
function doSomething(){
"use strict";
//函數體
}
ECMAScript 中語句以一個分號結尾。如果省略分號,則由解析器確定語句的結尾。
雖然語句結尾的分號并不是必須的,但最好還是不要省略。因為加上分號可以避免很多錯誤,也會在某些情況下增進代碼性能。
關鍵字可用于表示控制語句的開始或結束,或用于執行特定操作等。關鍵字不能用作標識符。
ECMA-262 還描述了一組不能用作標識符的保留字。
ECMAScript 的變量是松散類型的,就是可以用來保存任何類型的數據。
定義變量時要使用 var 操作符(var是一個關鍵字),后跟變量名(即標識符)。
如果省略 var 操作符可以定義全局變量,但這也不是我們所推薦的。因為在局部作用域中定義的全局變量很難維護,而且如果有意地忽略了 var 操作符,也會由于相應變量不會馬上就有定義而導致不必要的混亂。給未經聲明的變量賦值在嚴格模式下會拋出 ReferenceError 錯誤。
簡單數據類型(基本數據類型):undefined、null、boolean、number、string、symbol(ES 6新引入的數據類型)。
復雜數據類型:object。
typeof 用來檢測給定變量的數據類型,只適用于基本數據類型的類型判斷。
對一個值使用 typeof 操作符可能返回下列某個字符串:
undefined 類型只有一個值,即特殊的 undefined。在使用 var 聲明變量但未對其加以初始化時,這個變量的值就是 undefined。
對未初始化的變量執行 typeof 操作符會返回 undefined 值,而對未聲明的變量執行 typeof 操作符同樣也會返回 undefined 值。
null 類型也只有一個值,這個特殊的值是 null。
使用 typeof 操作符檢測 null 值時會返回 “object” :
var car = null
alert(typeof car); // "object"
boolean 類型只有兩個值:true、false。
注意:boolean 類型的值是區分大小寫的,True、False(以及其他的混合大小寫形式)都不是 boolean 的值,只是標識符。
要將一個值轉換為其對應的 boolean 值,可以調用轉型函數 Boolean()。
八進制字面值的第一位必須是零(0),然后是八進制數字序列(0~7)。如果字面值中的數值超出了范圍,那前導零將被忽略,后面的數值將被當作十進制數解析。
十六進制字面值的前兩位必須是0x,后面跟任何十六進制數字(09及AF),其他A~F可以大寫,也可以小寫。
在進行算術計算時,所有以八進制、十六進制表示的數值最后將被轉換成十進制數值。
浮點數值就是該數值中必須包含一個小數點,并且小數點后面必須至少有一位數字。
ECMAScript 能夠表示的最小數值存在 Number.MIN_VALUE 中,在大多數瀏覽器中,這個值是 5e-324。
ECMAScript 能夠表示的最大數值存在 Number.MAX_VALUE 中,在大多數瀏覽器中,這個值是 1.7976931348623157e+308。
如果計算結果超出了 JavaScript 數值范圍時,那么這個數值將被自動轉換成 Infinity 值。如果這個數值為負數,將被轉換成 -Infinity(負無窮),如果這個數值為正數,將被轉換成 Infinity(正無窮)。
即非數值是一個特殊的數值,這個數值用于表示一個本來要返回數值的操作數未返回數值的情況(這樣就不會拋出錯誤)。
有兩個特點。首先,任何涉及 NaN 的操作都會返回 NaN,這個特點在多步計算中有可能導致問題。其次,NaN 與任何值都不相等,包括 NaN 本身。
alert(NaN == NaN); // false
針對這兩個特點,ECMAScript 定義了 isNaN() 函數。這個函數接受一個參數,該參數可以是任何類型,而函數會幫我們確定這個參數是否“不是數值”。某些不是數值的值會直接轉換為數值。
alert(isNaN(NaN)); // true
alert(isNaN(10)); // false(10是數值)
alert(isNaN("10")); // false(可以被轉換成數值10)
alert(isNaN("blue")); // true(不能轉換為數值)
alert(isNaN(true)); // false(可以被轉換成數值1)
有三個函數可以把非數值轉換為數值:Number()、parseInt()、parseFloat()。
Number() 函數可以用于任何數據類型,另外兩個函數則專門用于把字符串轉為數值。這三個函數對于同樣的輸入會有返回不同的結果。
Number() 函數的轉換規則:
string 類型用于表示由零或多個 16 位 Unicode 字符組成的字符序列,即字符串。
String 數據類型包含一些特殊的字符字面量,也叫轉義序列。
\n 換行
\t 制表
\b 退格
\r 回車
\f 進紙
\\ 斜杠
' 單引號(')
" 雙引號(")
ECMAScript 的字符串是不可變的,也就是說,字符串一旦創建,它們的值就不能改變。如果要改變某個變量保存的字符串,首先要摧毀原來的字符串,然后再用另一個包含新值得字符串填充該變量。
把一個值轉換為一個字符串有兩種方式。第一種是使用幾乎每個值都有得 toString() 方法。
在不知道要轉換的值是不是 null 或 undefined 的情況時,還可以使用轉型函數 String(),這個函數能夠將任何類型的值轉換為字符串。
String()函數遵循下列轉換規則:
ECMAScript 中的對象其實就是一組數據和功能的集合。
對象可以通過執行 new 操作符后跟要創建的對象類型的名稱來創建。而創建 Object 類型的實例并為其添加屬性和(或)方法,就可以創建自定義對象。
Object 的每個實例都具有下列屬性和方法:
操作符包括算術操作符(如加號和減號)、位操作符、關系操作符、相等操作符。
只能操作一個值的操作符叫一元操作符。
前置型、后置型。前置型應該位于要操作的變量之前,后置型應該位于要操作的變量之后。
執行前置遞增和遞減操作時,變量的值都是在語句被求值以前改變的。
后置的遞增和遞減操作是在包含它們的語句被求值之后才執行的。
在應用于不同的值時,遞增和遞減操作符遵循下列規則:
一元加操作符以一個加號(+)表示,放在數值前面,對數值不會產生任何影響。
一元減操作符主要用于表示負數。
位操作符不直接操作 64 位的值,而是先將 64 位的值轉換成 32 位的整數,然后執行操作,最后再將結果轉換回 64 位。
對于有符號的整數,32 位中的前 31 位用于表示整數的值。第 32 位表示數值的符號:0表示正數,1表示負數。這個表示符號的位叫符號位。
負數以二進制碼存儲,使用的格式是二進制補碼,計算一個數值的二進制補碼,經過以下步驟:
如果對非數值應用位操作符,會先使用 Number() 函數將該值轉換為一個數值,然后再應用位操作,得到的結果是一個數值。
按位非操作符由一個波浪線(~)表示。
執行按位非得結果就是返回數值得反碼。
按位非操作的本質:操作數的負值減 1。
按位非是在數值表示的最底層執行操作,因此速度更快。
按位與操作符由一個和號字符(&)表示,它由兩個操作符數。
按位與操作只在兩個數值的對應位都是 1 時才返回 1,任何一位是 0,結果都是 0。
按位或操作符由一個豎線(|)表示,也有兩個操作符。
按位或操作在有一個位是 1 的情況下就返回 1,而只有在兩個位都是 0 的情況下才返回 0。
按位異或操作符由一個插入符號(^)表示,也有兩個操作符。
按位異或操作在兩個數值對應位上只有一個 1 時才返回 1,如果對應的兩位都是 1 或 0,則返回 0。
左移操作符由兩個小于號(<<)表示,這個操作符會將數值的所有位向左移動指定的位數。
在向左移位后,原數值的右側就多出了空位,就用 0 來填充空位。
左移不會影響操作數的符號位。
有符號的右移操作符由兩個大于號(>>)表示,這個操作符會將數值向右移動,但保留符號位(即正負號標記)。
在右移過程中,原數值出現空位,用符號位的值來填充空位。
無符號右移操作符由 3 個大于號(>>>)表示,這個操作符會將數值的所有 32 位都向右移動。對正數來說,無符號右移的結果與有符號右移相同。
對于負數來說,無符號右移是以 0 來填充空位。
布爾操作符一共有 3 個:非(NOT)、與(AND)、或(OR)。
邏輯非操作符由一個嘆號(!)表示。邏輯非操作符首先會將它的操作數轉換為一個布爾值,然后再對其求反。
邏輯非操作符遵循以下規則:
邏輯非操作符也可以用于將一個值轉換為與其對應的布爾值,用同時使用兩個邏輯非操作符(!!)。
邏輯與操作符由兩個和號(&&)表示,有兩個操作數。
在有一個操作數不是布爾值的情況下,邏輯與操作不一定返回布爾值,此時遵循下列規則:
邏輯與操作屬于短路操作,即第一個操作數能夠決定結果,那么就不會再對第二個操作數求值。
邏輯或操作符由兩個豎線符號(||)表示,也有兩個操作數。
遵循以下規則:
邏輯或操作符也是短路操作符,如果第一個操作數的求值結果為 true,就不會對第二個操作數求值。
3 個乘性操作符:乘法、除法、求模。
乘法操作符由一個星號(*)表示,用于計算兩個數值的乘積。
在處理特殊值情況下,遵循下列特殊規則:
除法操作符由一個斜線符號(/)表示,執行第二個操作數除第一個操作數的計算。
在處理特殊值情況下,遵循下列特殊規則:
求模(余數)操作符由一個百分號(%)表示。
在處理特殊值情況下,遵循下列特殊規則:
加法操作符(+)
如果兩個操作符都是數值,執行常規的加法計算,然后根據下列規則返回結果:
如果有一個操作符是字符串,就要應用如下規則:
如果有一個操作數是對象、數值、布爾值,則調用它們的 toString() 方法取得相應的字符串值,然后再應用關于字符串的規則。
對于 undefined 和 null,則分別調用 String() 函數并取得字符串“undefined”和“null”。
減法操作符(-)
一樣需要遵循如下特殊規則:
小于(<)、大于(>)、小于等于(<=)和大于等于(>=)這幾個關系操作符用于對兩個值進行比較,這幾個操作符都返回一個布爾值。
當關系操作符的操作數使用了非數值時,要進行數據轉換或完成某些操作。以下就是其規則:
ES 提供兩組操作符:相等和不相等——先轉換再比較,全等和不全等——僅比較不轉換。
相等操作符由兩個等于號(==)表示,如果兩個操作數相等,返回 true。
不相等操作符由嘆號后跟等于號(!=)表示,如果兩個操作數不相等,返回 false。
這兩個操作符都會先轉換操作數(通常稱為強制轉型),然后比較它們的相等性。
在轉換不同數據類型時,遵循下列規則:
這兩個操作符在比較時遵循下列規則:
全等操作符由 3 個等于號(===)表示,它只在兩個操作數未經轉換就相等的情況下返回 true。
不全等操作符由一個嘆號后跟兩個等于號(!==)表示,它在兩個操作數未經轉換就不相等的情況下返回 true。
注意:null == undefined 返回 true,因為它們是類似的值;但 null === undefined 會返回 false。
條件操作符遵循與 Java 中的條件操作符相同的語法形式。
簡單的賦值操作符由等于號(=)表示,其作用就是把右側的值賦給左側的變量。
如果在等于號(=)前面再添加乘性操作符、加性操作符或位操作符,就可以完成復合賦值操作。
每個主要算術操作符都有對應的復合賦值操作符,這些操作符如下:
使用逗號操作符可以在一條語句中執行多個操作。
逗號操作符多用于聲明多個變量,還可以用于賦值。用于賦值時,逗號操作符總會返回表達式中的最后一項。
語句通常使用一或多個關鍵字來完成給定任務。
語法如下:
if(condition){
statement1
}else{
statement2
}
其中的 condition(條件)可以是任意表達式。如果對 condition 求值的結果為 true,則執行 statement1(語句1),否則執行 statement2(語句2)。
do-while 語句是一種后測試循環語句,即只有在循環體中的代碼執行之后,才會測試出口條件。換句話說,在對條件表達式求值之前,循環體內的代碼至少會被執行一次。語法如下:
do{
statement
}while(expression);
while 語句屬于前測試循環語句,也就是說,在循環體內的代碼被執行之前,就會對出口條件求值。因此,循環體內的代碼有可能永遠不會被執行。語法如下:
while(expression){
statement
}
for 語句也是一種前測試循環語句,但它具有在執行循環之前初始化變量和定義循環后要執行的代碼能力。語法如下:
for(initialization;expression;post-loop-expression){
statement
}
在 for 循環的變量初始化表達式中,也可以不使用 var 關鍵字,該變量的初始化可以在外部執行。
for 語句中的初始化表達式、控制表達式和循環后表達式都是可選的,如果將三個表達式全部省略,就會創建一個無限循環。
for-in 語句是一種精準的迭代語句,可以用來枚舉對象的屬性。語法如下:
for(property in expression){
statement
}
如果表示要迭代的對象的變量值為 null 或 undefined,for-in 語句會拋出錯誤。ES5 更正了這一行為,對這種情況不再拋出錯誤,而只是不執行循環體。為了保證最大限度的兼容性,建議在使用 for-in 循環之前,先檢測該對象的值不是 null 或 undefined。
使用 label 語句可以在代碼中添加標簽,以便將來使用。語法如下:
label: statement
示例:
start:for(var i = 0; i < count; i++){
alert(i);
}
break 和 continue 語句用于在循環中精確地控制代碼的執行。
break 語句會立即退出循環,強制繼續執行循環后面的語句。continue 語句雖然也是立即退出循環,但退出循環后會從循環的頂部繼續執行。
with 語句的作用是將代碼的作用域設置到一個特定的對象中。語法如下:
with(expression) statement;
定義 with 語句的目的主要是為了簡化多次編寫同一個對象的工作,實例:
var qs = location.search.substring(1);
var hostName = location.hostname;
var url = location.href;
以上代碼可以使用 with 語句改寫成如下:
with(location){
var qs = search.substring(1);
var hostName = hostname;
var url = href;
}
注意:在嚴格模式下不允許使用 with 語句,否則將視為語法錯誤。
由于大量使用 with 語句會導致性能下降,同時也會給調試代碼造成困難,因此在開發大型應用程序時,不建議使用 with 語句。
switch 語句與 if 語句的關系最為密切。語法如下:
switch(expression){
case value:statement
break;
case value:statement
break;
case value:statement
break;
default:statement
}
含義為:如果表達式等于這個值(value),則執行后面的語句。而 break 關鍵字會導致代碼執行流跳出 switch 語句。如果省略 break,就會導致執行完當前 case 后,繼續執行下一個 case。最后的 default 關鍵字則用于在表達式不匹配前面任何一種情形時,執行機動代碼(也相當于 else 語句)。
switch 語句在比較值時使用的是全等操作符,因此不會發生類型轉換。
通過函數可以封裝任意多條語句,而且可以在任何地方、任何時候調用執行。
ES 中的函數使用 function 關鍵字來聲明,后跟一組參數以及函數體。語法如下:
function functionName(arg0,arg1,...,argN){
statements
}
ES 中的函數在定義時不必指定是否返回值。實際上,任何函數在任何時候都可以通過 return 語句后跟要返回的值來實現返回值。示例如下:
function sum(num1,num2){
return num1 + num2;
}
這個 sum() 函數的作用是把兩個值加起來返回一個結果。
調用這個函數的代碼如下:
var result = sum(5,10);
這個函數會在執行完 return 語句之后停止并立即退出,因此位于 return 之后的任何代碼將不會被執行。
另外,return 語句也可以不帶有任何返回值。函數在停止執行后將返回 undefined 值,這種用法一般用在需要提前停止函數執行而又不需要返回值的情況下使用。
嚴格模式對函數有一些限制,如下:
在函數體內可以通過 arguments 對象來訪問這個參數數組,從而獲取傳遞給函數的每一個參數。
ES 函數一個重要特定:命名的參數只提供便利,但不是必需的。
沒有傳遞值的命名參數將自動被賦予 undefined 值。
ES 中的所有參數傳遞的都是值,不可能通過引用傳遞參數。
ES 函數不能像傳統意義上那樣實現重載。ES 函數沒有簽名,因為其參數是由包含零或多個值的數組來表示的。而沒有函數簽名,真正的重載是不可能做到的。
如果在 ES 中定義了兩個名字相同的函數,則該名字只屬于后定義的函數。
下面簡要總結 ES 中的基本要素:
ES變量可能包含兩種不同數據類型的值:基本類型值和引用類型值
。
基本類型值指的是簡單的數據段,而引用類型值指那些可能由多個值構成的對象。
引用類型的值是保存在內存中的對象。JavaScript不允許直接訪問內存中的位置,也就是說不能直接操作對象的內存空間。在操作對象時,實際上是在操 作對象的引用而不是實際的對象。因此,引用類型的值是按引用訪問的。
定義基本類型值和引用類型值的方式是類似的:創建一個變量并為該變量賦值。
我們不能給基本類型的值添加屬性,只能給引用類型值動態地添加屬性。
除了保存的方式不同之外,在從一個變量向另一個變量復制基本類型值和引用類型值時,也存在不同。如果從一個變量向另一個變量復制基本類型的值,會在變量對象上創建一個新值,然后把該值復制到為新變量分配的位置上,例如:
var num1 = 5;
var num2 = num1;
以上代碼中,num1中保存的值為5.當使用num1的值來初始化num2時,num2中也保存了值5.但num2中的5與num1的5是完全獨立的,該值只是num1中5的一個副本。此后,這兩個變量可以參與任何操作而不會相互影響。
當從一個變量向另一個變量復制引用類型的值時,同樣也會將存儲在變量對象中的值復制一份到為新變量分配的空間中。不同的是,這個值的副本實際上是一個指針,而這個指針指向存儲在堆中的一個對象。復制操作結束后,兩個變量實際上將引用同一個對象。因此,改變其中一個變量,就會音響另一個變量,例如:
var obj1 = new Object();
var obj2 = obj1;
obj1.name = "zww"
alert(obj2.name); //"zww"
以上代碼中,變量obj1保存了一個對象的新實例。然后這個值被復制到了obj2中。obj1和obj2都指向同一個對象。這樣,當為obj1添加name屬性后,可以通過obj2來訪問這個屬性。
ES中所有函數的參數都是按值傳遞的。也就是說,把函數外部的值復制給函數內部的參數,就和把值從一個變量復制到另一個變量一樣。例子:
function addTen(num){
num += 10;
return num;
}
var count = 20;
var result = addTen(count);
alert(count); //20,沒有變化
alert(result); //30
以上代碼中,函數addTen()有一個參數num,而參數實際上是函數的局部變量。在調用函數時,變量count作用參數被傳遞給函數,這個變量值為20。于是數值20被復制給參數num以便在addTen()中使用。在函數內部,參數num值被加上了10,這一變化不會影響函數外部的count變量。
要檢測一個變量是不是基本數據類型,typeof操作符是最佳的工具。
typeof操作符是確定一個變量是字符串、數值、布爾值,還是undefined的最佳工具,如果變量值是null或對象,則會返回"object"。
var s = "zww";
var b = true;
var i = 19;
var u;
var n = null;
var o = new Object();
alert(typeof s); //string
alert(typeof b); //boolean
alert(typeof i); //number
alert(typeof u); //undefined
alert(typeof n); //object
alert(typeof o); //object
因此,ES提供了instanceof操作符,語法如下:
result = variable instanceof constructor
如果變量是給定引用類型的實例,那么instanceof操作符就會返回true。
執行環境
定義了變量或函數有權訪問的其他數據,決定了它們各自的行為。每個執行環境都有一個與之關聯的變量對象
,環境中定義的所有變量和函數都保存在這個對象中。
每個函數都有自己的執行環境。當執行流進入一個函數時,函數的環境就會被推入一個環境棧中。而在函數執行之后,棧將其環境彈出,把控制權返回給之前的執行環境。
當代碼在一個環境中執行時,會創建變量對象的一個作用域鏈。作用域鏈的用途是保證對執行環境有權訪問的所有變量和函數的有序訪問。
雖然執行環境的類型總共只有兩種:全局和局部(函數),但還是有其他辦法延長作用域鏈的。因為有些語句可以在作用域鏈的前端臨時增加一個變量對象,該變量對象會在代碼執行后被移除。在兩種情況下會發生這種情況。具體的說就是當執行流進入下列任何一個語句時,作用域鏈就會得到加長:
這兩個語句都會在作用域鏈的前端添加一個變量對象。對with語句來說,會將指定的對象添加到作用域鏈中。對catch來說,會創建一個新的變量對象,其中包含的是被拋出的錯誤對象的聲明。
使用var聲明的變量會自動被添加到最接近的環境中。在函數內部,最接近的環境就是函數的局部環境;在with語句中,最接近的環境是函數環境。如果初始化變量時沒有使用var聲明,該變量會自動被添加到全局環境。例如:
function add(num1,num2){
var sum = num1 + num2;
return sum;
}
var result = add(10,20); //30
alert(sum); //由于sum不是有效的變量,因此會導致錯誤
以上代碼中,函數add()定義了一個名為sum的局部變量,該變量包含加法的操作結果。雖然結果值從函數中返回了,但變量sum在函數外部是訪問不到的。如果省略例子中var關鍵字,那么add()執行完畢后,sum也將可以訪問到:
function add(num1,num2){
sum = num1 + num2;
return sum;
}
var result = add(10,20); //30
alert(sum); //30
當在某個環境中為了讀取或寫入而引用一個標識符時,必須通過搜索來確定該標識符實際代表什么。搜索過程從作用域鏈的前端開始,向上逐級查詢與給定名字匹配的標識符。如果在局部環境中找到了該標識符,就停止搜索,變量就緒。如果在局部環境中沒有找到,則繼續沿作用域鏈向上搜索。搜索過程將一直追溯到全局環境的變量對象。如果在全局環境中沒有找到標識符,則意味著該變量尚未聲明。
JavaScript具有自動垃圾收集機制,也就是說,執行環境會負責管理代碼執行過程中使用的內存。
JavaScript中最常用的垃圾收集方式是標記清除
。當變量進入環境時,就將這個變量標記為“進入環境”。從邏輯上講,永遠不能釋放進入環境的變量所占用的內存,因為只要執行流進入相應的環境,就可能會用到它們。而當變量離開環境時,則講其標記為“離開環境”。
另一種不太常見的垃圾收集策略叫引用計數
。含義是跟蹤記錄每個值被引用的次數。當聲明了一個變量并將一個引用類型值賦給該變量時,則這個值的引用次數就是1。如果同一個值又被賦給另一個變量,則該值的引用次數加1。相反,如果包含對這個值引用的變量又取得另外一個值,則這個值的引用次數減1。當這個值的引用次數變為0時,則說明沒有辦法再訪問這個值了,因而就可以將其占用的內存空間回收回來。這樣,當垃圾收集器下次再運行時,它就會釋放那些引用次數為零的值所占用的內存。
垃圾收集器是周期性運行的。
IE的垃圾收集器是根據內存分配量運行的,具體一點說就是256個變量、4096個對象(或數組)字面量和數組元素(slot)或者64KB的字符串,達到上述任何一個臨界值,垃圾收集器就會運行。
在IE中,調用window.CollectGarbage()方法會立即執行垃圾收集。
JavaScript在進行內存管理及垃圾收集時,最主要的一個問題是,分配給web瀏覽器的可用內存數量通常要比分配給桌面應用程序的少。這樣做的目的主要是出于安全方面考慮,目的是防止運行JavaScript的網頁耗盡全部系統內存而導致系統崩潰。內存限制問題不僅會影響給變量分配內存,同時還會影響調用棧以及在一個線程中能夠同時執行的語句數量。
因此,確保占用最少的內存可以讓頁面獲得更好的性能。而優化內存占用的最佳方式,就是為執行中的代碼只保存必要的數據。一旦數據不再有用,最好通過將其值設置為null來釋放其引用,這個做法叫做解除引用
。這一做法適用于大多數全局變量和全局對象的屬性。局部變量會在它們離開執行環境時自動被解除引用。
JavaScript變量可以用來保存兩種類型的值:基本類型值和引用類型值。
基本類型值和引用類型值具有以下特點:
所有變量都存在于一個執行環境(也稱作用域)當中,這個執行環境決定了變量的生命周期,以及哪一部分代碼可以訪問其中的變量。
以下是關于執行環境的幾點總結:
JavaScript是一門具有自動垃圾收集機制的編程語言,開發人員不必關心內存分配和回收問題,可以對JavaScript的垃圾收集例程做如下總結:
創建Object實例的兩種方式:
var person = new Object();
person.name = "ZWW";
person.age = 22;
var person = {
name: "ZWW",
age: 22
};
在對象字面量中,使用逗號來分隔不同的屬性,并且在最后一個屬性后面不能添加逗號。
在使用對象字面量時,屬性名也可以使用字符串;如果留空其花括號,則可以定義只包含默認屬性和方法的對象。