第1章 JavaScript 簡介
JavaScript 具備與瀏覽器窗口及其內(nèi)容等幾乎所有方面交互的能力访惜。
歐洲計(jì)算機(jī)制造商協(xié)會 ECMA European Computer Manufactures Association
ECMAScript ek-ma-script
一個(gè)完整的JavaScript實(shí)現(xiàn)應(yīng)該由下列三個(gè)不同的部分組成:
- 核心(ECMAScript)咽筋,提供核心語言功能
- 文檔對象模型(DOM)弊仪,提供訪問和操作網(wǎng)頁內(nèi)容的方法和接口
- 瀏覽器對象模型(BOM)但绕,提供與瀏覽器交互的方法和接口
ECMAScript
我們常見的 Web 瀏覽器知識 ECMAScript 實(shí)現(xiàn)可能的宿主環(huán)境之一齿椅,其他宿主環(huán)境包括Node(一種服務(wù)端JavaScript平臺)和 Adobe Flash戈轿。
ECMAScript規(guī)定了這門語言的下列組成部分:
- 語法
- 類型
- 語句
- 關(guān)鍵字
- 保留字
- 操作符
- 對象
ECMAScript 3.1 成為 ECMA-262 第5版,在2009年發(fā)布店雅。
瀏覽器對象模型(BOM)
- 彈出新瀏覽器窗口的功能
- 移動(dòng)政基、縮放和關(guān)閉瀏覽器窗口的功能
- 提供瀏覽器詳細(xì)信息的 navigator 對象
- 提供瀏覽器所加載頁面的詳細(xì)信息的 location 對象
- 提供用戶顯示器分辨率詳細(xì)信息的 screen 對象
- 對 cookies 的支持
- 像 XMLHttpRequest 和 IE 的 ActiveXObject 這樣的自定義對象
HTML5 致力于把很多 BOM 功能寫入正式規(guī)范。
第2章 在 HTML 中使用 JavaScript
向 HTML 頁面中插入 JavaScript 的主要方法闹啦,就是使用<script>
元素沮明。HTML 4.01 為<script>
定義了下列6個(gè)屬性:
-
async
:可選,異步腳本窍奋。表示應(yīng)該立即下載腳本珊擂,但不應(yīng)妨礙頁面中的其他操作,比如下載其他資源或等待加載其他腳本费变。只對外部腳本文件有效。 -
charset
:可選圣贸。表示通過src
屬性指定的代碼的字符集挚歧。由于大多數(shù)瀏覽器會忽略它的值,因此這個(gè)屬性很少有人用吁峻。 -
defer
:可選滑负,延遲腳本在张。表示腳本可以延遲到文檔被解析和顯示之后再執(zhí)行。只對外部腳本文件有效矮慕。IE7 及更早版本對嵌入腳本也支持這個(gè)屬性帮匾。也就是說,腳本會被延遲到整個(gè)頁面都解析完畢后再運(yùn)行痴鳄。 -
language
:已廢棄瘟斜。大多數(shù)瀏覽器會忽略這個(gè)屬性,因此也沒有必要再用了痪寻。 -
src
:可選螺句。表示包含要執(zhí)行代碼的外部文件。 -
type
:可選橡类∩呱校可以看成是language
的替代屬性;表示編寫代碼使用的腳本語言的內(nèi)容類型(也成為 MIME 類型)顾画。雖然text/javascript
和text/ecmascript
都已經(jīng)不被推薦使用取劫,但人們一直以來使用的都還是type/javascript
。實(shí)際上研侣,服務(wù)器在傳送JavaScript 文件時(shí)使用的 MIME 類型通常是application/x-javascript
谱邪,但在type
中設(shè)置這個(gè)值卻可能導(dǎo)致腳本被忽略。這個(gè)屬性不是必須的义辕,如果沒有指定這個(gè)屬性虾标,其默認(rèn)值仍為text/javascript
。
轉(zhuǎn)義字符“\”
使用<script>
元素的方式有兩種:直接在頁面中嵌入 JavaScript 代碼和包含外部 JavaScript 文件灌砖。
第一種:
<script type="text/javascript">
function sayHi(){
alert("Hi");
}
</script>
第二種:
<script type="text/javascript" src="example.js"></script>
外部文件example.js
只需包含通常要放在開始的<script>
和結(jié)束的</script>
之間的那些 JavaScript 代碼即可璧函。
與解析嵌入式 JavaScript 代碼一樣,在解析外部 JavaScript 文件(包括下載該文件)時(shí)基显,頁面的處理也會暫時(shí)停止屯蹦。
需要注意的是,帶有src
屬性的<script>
元素不應(yīng)該在其<script>
和</script>
標(biāo)簽之間再包含額外的JavaScript 代碼欧漱。如果包含了嵌入的代碼退唠,則只會下載并執(zhí)行外部腳本文件,嵌入的代碼會被忽略窜醉。
另外宪萄,通過<script>
元素的src
屬性還可以包含來自外部域的JavaScript 文件。
引用外部JavaScript 文件的優(yōu)點(diǎn)
并不存在必須使用外部文件的硬性規(guī)定榨惰,但支持使用外部文件的人多會強(qiáng)調(diào)如下優(yōu)點(diǎn):
- 可維護(hù)性:遍及不同HTML 頁面的JavaScript 會造成維護(hù)問題拜英。但把所有JavaScript 文件都放在一個(gè)文件夾中,維護(hù)起來就輕松多了琅催。而且開發(fā)人員因此能夠在不觸及HTML 標(biāo)記的情況下居凶,集中精力編輯JavaScript 代碼虫给。
- 可緩存:瀏覽器能夠根據(jù)具體的設(shè)置緩存鏈接的所有外部JavaScript 文件。也就是說侠碧,如果有兩個(gè)頁面都使用同一個(gè)文件抹估,那么這個(gè)文件只需下載一次。因此弄兜,最終結(jié)果就是能夠加快頁面加載的速度药蜻。
- 適應(yīng)未來
使用<noscript>
元素可以指定在不支持腳本的瀏覽器中顯示的替代內(nèi)容。但在啟用了腳本的情況下挨队,瀏覽器不會顯示<noscript>
元素中的任何內(nèi)容谷暮。
第3章 基本概念
任何語言的核心都必然會描述這門語言的最基本的工作原理。而描述的內(nèi)容通常都要涉及這門語言的語法盛垦、操作符湿弦、數(shù)據(jù)類型、內(nèi)置功能等用于構(gòu)建復(fù)雜解決方案的基本概念腾夯。
本章將主要按照第3版定義的ECMAScript 介紹這門語言的基本概念颊埃,并就第5版的變化給出說明。
ECMAScript 中的一切(變量蝶俱、函數(shù)名和操作符)都區(qū)分大小寫班利。
標(biāo)識符
所謂標(biāo)識符,就是指變量榨呆、函數(shù)罗标、屬性的名字,或者函數(shù)的參數(shù)积蜻。標(biāo)識符可以是按照下列格式規(guī)則組合起來的一或多個(gè)字符:
- 第一個(gè)字符必須是一個(gè)字母闯割、下劃線或一個(gè)美元符號
- 其他字符可以是字母、下劃線竿拆、美元符號或數(shù)字
按照慣例宙拉,ECMAScript 標(biāo)識符采用駝峰大小寫格式,也就是第一個(gè)字母小寫丙笋,剩下的每個(gè)單詞的首字母大寫谢澈。
關(guān)鍵字和保留字
以下就是ECMAScript 的全部關(guān)鍵字:
以下是 ECMA-262 第3版定義的全部保留字:
在實(shí)現(xiàn) ECMAScript 3 的JavaScript 引擎中使用關(guān)鍵字做標(biāo)識符,會導(dǎo)致“Identifier Expected”錯(cuò)誤御板。
變量
ECMAScript 的變量是松散類型的锥忿,所謂松散類型就是可以用來保存任何類型數(shù)據(jù)。換句話說怠肋,每個(gè)變量僅僅是一個(gè)用于保存值的占位符而已缎谷。定義變量時(shí)要使用var
操作符(注意var
是一個(gè)關(guān)鍵字),后跟變量名(即一個(gè)標(biāo)識符)。
用var
操作符定義的變量將成為定義該變量的作用域中的局部變量列林。也就是說,如果在函數(shù)中使用var
定義一個(gè)變量酪惭,那么這個(gè)變量在函數(shù)退出后就會被銷毀希痴。可以省略var
操作符春感,從而創(chuàng)建一個(gè)全局變量砌创。
雖然省略
var
操作符可以定義全局變量,但不推薦這種做法鲫懒。因?yàn)樵诰植孔饔糜蛑卸x的全局變量很難維護(hù)嫩实,而且如果有意地忽略了var
操作符,也會由于相應(yīng)變量不會馬上就有定義而導(dǎo)致不必要的混亂窥岩。如未經(jīng)聲明的變量賦值在嚴(yán)格模式下會導(dǎo)致拋出ReferenceError
錯(cuò)誤甲献。
數(shù)據(jù)類型
ECMAScript 中有5種簡單數(shù)據(jù)類型(基本數(shù)據(jù)類型):Undefinded、Null颂翼、Boolean晃洒、Number 和String。還有1種復(fù)雜數(shù)據(jù)類型——Object朦乏,Object 本質(zhì)上是由一組無序的名值對組成的球及。
typeof 操作符
鑒于ECMAScript 是松散類型的,因此需要有一種手段來檢測給定變量的數(shù)據(jù)類型——typeof 就是負(fù)責(zé)提供這方面信息的操作符呻疹。對一個(gè)值使用 typeof 操作符可能返回下列某個(gè)字符串:
- ”undefined“——未定義
- ”boolean“——布爾值
- ”string“——字符串
- ”number“——數(shù)值
- ”object“——對象或null
- ”function“——函數(shù)
注意吃引,typeof 是一個(gè)操作符而不是函數(shù),因此可以在其后使用圓括號刽锤,但不是必須的镊尺。
從技術(shù)角度講,函數(shù)在ECMAScript 中是對象姑蓝,不是一種數(shù)據(jù)類型鹅心。然而,函數(shù)也確實(shí)有一些特殊的屬性纺荧,因此通過typeof 操作符來區(qū)分函數(shù)和其他對象是有必要的旭愧。
Undefined 類型
Undefined 類型只有一個(gè)值,即特殊的 undefined宙暇。在使用var
聲明變量但未對其加以初始化時(shí)输枯,這個(gè)變量的值就是 undefined
Null 類型
Null 類型只有一個(gè)值,即特殊的 null占贫。從邏輯角度來看桃熄,null 值表示一個(gè)空對象指針,而這也正是使用 typeof 操作符檢測 null 值時(shí)會返回”object“的原因型奥。
如果定義的變量準(zhǔn)備在將來用于保存對象瞳收,那么最好將該變量初始化為null 而不是其他值碉京。
Boolean 類型
Boolean 類型是ECMAScript 中使用最多的一種類型,該類型只有兩個(gè)字面值:true 和 false螟深。(區(qū)分大小寫)
雖然Boolean 類型的字面值只有兩個(gè)谐宙,但ECMAScript 中所有類型的值都有與這兩個(gè)Boolean 值等價(jià)的值。要將一個(gè)值轉(zhuǎn)換為其對應(yīng)的Boolean 值界弧,可以調(diào)用轉(zhuǎn)型函數(shù)Boolean() 凡蜻。下表給出了各種數(shù)據(jù)類型及其對應(yīng)的轉(zhuǎn)換規(guī)則:
這些轉(zhuǎn)換規(guī)則對理解流控制語句自動(dòng)執(zhí)行相應(yīng)的Boolean 轉(zhuǎn)換非常重要。
Number 類型
最基本的數(shù)值字面量格式是十進(jìn)制整數(shù)垢箕。
八進(jìn)制字面值的第一位必須是0
十六進(jìn)制字面量的前兩位必須是0x
在進(jìn)行算數(shù)運(yùn)算時(shí)划栓,所有以八進(jìn)制和十六進(jìn)制表示的數(shù)值最終都將被轉(zhuǎn)換成十進(jìn)制數(shù)值。
浮點(diǎn)數(shù)值
所謂浮點(diǎn)數(shù)值条获,就是該數(shù)值中必須包含一個(gè)小數(shù)點(diǎn)忠荞,并且小數(shù)點(diǎn)后面必須至少有一位數(shù)字。由于保存浮點(diǎn)數(shù)值需要的內(nèi)存空間是保存整數(shù)值的兩倍月匣,因此ECMAScript 會不失時(shí)機(jī)地將浮點(diǎn)數(shù)值轉(zhuǎn)換為整數(shù)值钻洒。
對于那些極大或極小的數(shù)值,可以用e 表示法(即科學(xué)計(jì)數(shù)法)表示的浮點(diǎn)數(shù)值表示锄开。
浮點(diǎn)數(shù)值的最高精讀是17位小數(shù)素标,但在進(jìn)行算術(shù)計(jì)算時(shí)其精確度遠(yuǎn)遠(yuǎn)不如整數(shù)。例如萍悴,0.1加0.2的結(jié)果不是0.3头遭,而是0.30000000000000004。因此癣诱,永遠(yuǎn)不要測試某個(gè)特定的浮點(diǎn)數(shù)值计维。
數(shù)值范圍
最小數(shù)值:Number.MIN_VALUE
最大數(shù)值:Number.MAX_VALUE
如果某次計(jì)算的結(jié)果得到了一個(gè)超出JavaScript 數(shù)值范圍的值,那么這個(gè)數(shù)值將會被自動(dòng)轉(zhuǎn)換成特殊的Infinity 數(shù)值撕予。要想確定一個(gè)數(shù)值是不是位于最大和最小的數(shù)值之間鲫惶,可以使用isFinite()
函數(shù)。這個(gè)函數(shù)在參數(shù)位于最大與最小數(shù)值之間時(shí)會返回true
NaN
NaN实抡,即非數(shù)值(Not a Number)是一個(gè)特殊的數(shù)值欠母,這個(gè)數(shù)值用于表示一個(gè)本來要返回?cái)?shù)值的操作數(shù)未返回?cái)?shù)值的情況(這樣就不會拋出錯(cuò)誤了)。例如吆寨,在其他編程語言中赏淌,任何數(shù)值除以非數(shù)值都會導(dǎo)致錯(cuò)誤,從而停止執(zhí)行代碼啄清。但在ECMAScript 中六水,任何數(shù)值除以非數(shù)值會返回NaN ,因此不會影響其他代碼的執(zhí)行。
NaN 本身有兩個(gè)非同尋常的特點(diǎn):
- 任何涉及NaN的操作(例如NaN/10)都會返回NaN
- NaN與任何值都不相等掷贾,包括NaN本身
針對NaN的這兩個(gè)特點(diǎn)睛榄,ECMAScript 定義了isNaN()
函數(shù)。這個(gè)函數(shù)接受一個(gè)參數(shù)想帅,該參數(shù)可以是任何類型懈费,而函數(shù)會幫我們確定這個(gè)參數(shù)是否”不是數(shù)值“。任何不能被轉(zhuǎn)換為數(shù)值的值都會導(dǎo)致這個(gè)函數(shù)返回true博脑。
isNaN()
也適用于對象。
數(shù)值轉(zhuǎn)換
有三個(gè)函數(shù)可以把非數(shù)值轉(zhuǎn)換為數(shù)值:
- Number()
- parseInt()
- parseFloat()
Number() 函數(shù)的轉(zhuǎn)換規(guī)則如下:
- 如果是Boolean 值票罐,true 和 false 將分別被轉(zhuǎn)換為1和0
- 如果是數(shù)字值叉趣,只是簡單的傳入和返回
- 如果是null 值,返回0
- 如果是undefined该押,返回NaN
- 字符串轉(zhuǎn)換規(guī)則如下
- 如果是對象疗杉,則調(diào)用對象的
valueOf()
方法,然后依照前面的規(guī)則轉(zhuǎn)換返回的值蚕礼。如果轉(zhuǎn)換的結(jié)果是NaN烟具,則調(diào)用對象的toString()
方法,然后再次依照前面的規(guī)則轉(zhuǎn)換返回的字符串值
字符串的Number() 轉(zhuǎn)換規(guī)則:
- 如果字符串中只包含數(shù)字奠蹬,則將其轉(zhuǎn)換為十進(jìn)制數(shù)值
- 如果字符串中包含有效的浮點(diǎn)格式朝聋,將其轉(zhuǎn)換為對應(yīng)的浮點(diǎn)數(shù)值
- 如果字符串中包含有效的十六進(jìn)制格式,將其轉(zhuǎn)換為相同大小的十進(jìn)制整數(shù)格式
- 如果字符串是空的囤躁,將其轉(zhuǎn)換為0
- 如果字符串中包含除上述格式之外的字符冀痕,將其轉(zhuǎn)換為NaN
示例:
一元加操作符的操作與Number() 函數(shù)相同
處理整數(shù)時(shí)更常用的是parseInt()
函數(shù)。轉(zhuǎn)換時(shí)狸演,它會忽略字符串前面的空格言蛇,直至找到第一個(gè)非空格字符。如果第一個(gè)字符不是數(shù)字字符或者符號宵距,parseInt()
就會返回NaN腊尚。也許考慮到八進(jìn)制和十六進(jìn)制的問題。示例:
建議提供此函數(shù)第二個(gè)參數(shù)满哪,表示轉(zhuǎn)換時(shí)使用的基數(shù)(即多少進(jìn)制)婿斥。多數(shù)情況下,我們要解析的都是十進(jìn)制數(shù)值翩瓜,因此始終將10作為第二個(gè)參數(shù)是非常有必要的受扳。
與parseInt()
函數(shù)類似,parseFloat()
也是從第一個(gè)字符開始解析每個(gè)字符兔跌。不同的是勘高,parseFloat()
只解析十進(jìn)制數(shù),因此不需要第二個(gè)參數(shù)。示例:
String 類型
String 類型用于表示由零或多個(gè)16位Unicode 字符組成的字符序列华望,即字符串蕊蝗。可用雙引號或單引號表示赖舟。
與PHP中的雙引號和單引號會影響對字符串的解釋方式不同蓬戚,ECMAScript 中的這兩種方式并沒有什么區(qū)別。
字符字面量
String 數(shù)據(jù)類型包含一些特殊的字符字面量宾抓,也叫轉(zhuǎn)義序列子漩,用于表示非打印字符,或者具有其他用途的字符石洗。
這些字符字面量可以出現(xiàn)在字符串的任意位置幢泼,將被作為一個(gè)字符來解析。
字符串的特點(diǎn)
ECMAScript 中的字符串是不可變的讲衫,也就是說缕棵,字符串一旦創(chuàng)建,它們的值就不能改變涉兽。要改變某個(gè)變量保存的字符串招驴,首先要銷毀原來的字符串,然后再用另一個(gè)包含新值的字符串填充該變量枷畏。
轉(zhuǎn)換為字符串
要把一個(gè)值轉(zhuǎn)換為字符串有兩種方式:
- toString()——例如a.toString()
- String()——例如String(a)
區(qū)別在于轉(zhuǎn)換的數(shù)值為null 或undefined 的情況下别厘,后者可以轉(zhuǎn)換為相應(yīng)的字面量。
要把某個(gè)值轉(zhuǎn)換為字符串矿辽,可以使用加號操作符把它與一個(gè)字符串加在一起丹允。
Object 類型
ECMAScript 中的對象其實(shí)就是一組數(shù)據(jù)和功能的集合。
在ECMAScript 中袋倔,Object 類型是所有它的實(shí)例的基礎(chǔ)雕蔽。換句話說,Object 類型所具有的任何屬性和方法也同樣存在于更具體的對象中宾娜。
Object 的每個(gè)實(shí)例都具有下列屬性和方法:
- constructor:保存著用于創(chuàng)建當(dāng)前對象的函數(shù)批狐。
- hasOwnProperty(propertyName):用于檢查給定的屬性在當(dāng)前對象實(shí)例中(而不是在實(shí)例的原型中)是否存在。其中前塔,作為參數(shù)的屬性名(propertyName)必須以字符串的形式指定嚣艇。
- isPrototypeof(object):用于檢查傳入的對象是否是當(dāng)前對象的原型
- propertyIsEnumerable(propertyName):用于檢查給定的屬性是否能夠使用for-in 語句來枚舉。作為參數(shù)的屬性名必須以字符串形式指定华弓。
- toLocaleString():返回對象的字符串表示食零,該字符串與執(zhí)行環(huán)境的地區(qū)對應(yīng)。
- toString():返回對象的字符串表示寂屏。
- valueOf():返回對象的字符串贰谣、數(shù)值或布爾值表示娜搂。通常與toString() 方法的返回值相同。
由于在ECMAScript 中Object 是所有對象的基礎(chǔ)吱抚,因此所有對象都具有這些基本的屬性和方法百宇。
操作符
一元操作符
只能操作一個(gè)值的操作符叫做一元操作符。一元操作符是ECMAScript 中最簡單的操作符秘豹。
遞增和遞減操作符
分為前置型和后置型携御。
執(zhí)行前置遞增和遞減操作時(shí),變量的值都是在語句被求值以前改變的既绕。而后置的是在包含它們的語句被求值之后才執(zhí)行的啄刹。
所有這4個(gè)操作符對任何值都適用,規(guī)則如下:
- 在應(yīng)用于一個(gè)包含有效數(shù)字字符的字符串時(shí)凄贩,先將其轉(zhuǎn)換為數(shù)字值鸵膏,再執(zhí)行加減1的操作。字符串變量變成數(shù)值變量怎炊。
- 在應(yīng)用于一個(gè)不包含有效數(shù)字字符的字符串時(shí),將變量的值設(shè)置為NaN廓译。字符串變量變成數(shù)值變量评肆。
- 在應(yīng)用于布爾值時(shí),false轉(zhuǎn)換為0非区,true轉(zhuǎn)換為1瓜挽,之后再執(zhí)行加減1的操作。布爾值變量變成數(shù)值變量征绸。
- 在應(yīng)用于浮點(diǎn)數(shù)值時(shí)久橙,執(zhí)行加減1操作。
- 在應(yīng)用于對象時(shí)管怠,先調(diào)用對象的valueOf() 方法以取得一個(gè)可供操作的值淆衷。然后對該值應(yīng)用前述規(guī)則。如果結(jié)果是NaN渤弛,則再調(diào)用toString() 方法后再應(yīng)用前述規(guī)則祝拯。對象變量變?yōu)閿?shù)值變量。
示例:
一元加和減操作符
一元加和減操作符主要用于基本的算術(shù)運(yùn)算她肯。
對非數(shù)值應(yīng)用一元加或減操作符時(shí)佳头,該操作符會像Number() 轉(zhuǎn)型函數(shù)一樣對這個(gè)值執(zhí)行轉(zhuǎn)換。
位操作符
位操作符用在最基本的層次上晴氨,即按內(nèi)存中表示數(shù)值的位來操作數(shù)值康嘉。速度更快
在ECMAScript 中,當(dāng)對數(shù)值應(yīng)用位操作符時(shí)籽前,后臺會發(fā)生如下轉(zhuǎn)換過程:64位的數(shù)值被轉(zhuǎn)換為32位數(shù)值亭珍,然后執(zhí)行位操作敷钾,最后再將32位的結(jié)果轉(zhuǎn)換回64位數(shù)值。
如果對非數(shù)值應(yīng)用位操作符块蚌,會先使用Number() 函數(shù)將該值轉(zhuǎn)換為一個(gè)數(shù)值(自動(dòng)完成)闰非,然后再應(yīng)用位操作。得到的將是一個(gè)數(shù)值峭范。
按位非(NOT)
按位非操作符由一個(gè)波浪線(~)表示财松,執(zhí)行按位非的結(jié)果就是返回?cái)?shù)值的反碼。示例:
按位非操作的本質(zhì):操作數(shù)的負(fù)值減1
按位與(AND)
按位與操作符由一個(gè)和號字符(&)表示纱控,它有兩個(gè)操作符數(shù)辆毡。按位與操作只在兩個(gè)數(shù)值對應(yīng)位都是1時(shí)才返回1,任何一位是0甜害,結(jié)果都是0舶掖。
按位或(OR)
按位或操作符由一條豎線符號(|)表示,有兩個(gè)操作數(shù)尔店。按位或操作在有一個(gè)位是1的情況下就返回1眨攘,而只有在兩個(gè)位都是0的情況下才返回0。
按位異或(XOR)
按位異或操作符由一個(gè)插入符號(^)表示嚣州,有兩個(gè)操作數(shù)鲫售。這個(gè)操作在兩個(gè)數(shù)值對應(yīng)位上只有一個(gè)1是才返回1,如果對應(yīng)的兩位都是1或都是0该肴,則返回0情竹。
左移
左移操作符由兩個(gè)小于號(<<)表示,這個(gè)操作符會將數(shù)值的所有位向左移動(dòng)指定的位數(shù)匀哄。
注意秦效,左移不會影響操作數(shù)的符號位。
有符號的右移
有符號的右移操作符由兩個(gè)大于號(>>)表示涎嚼,這個(gè)操作符會將數(shù)值向右移動(dòng)阱州,但保留符號位(即正負(fù)號標(biāo)記)。
在移位的過程中法梯,原數(shù)值會出現(xiàn)空位贡耽。只不過這次的空位出現(xiàn)在原數(shù)值的左側(cè)、符號位的右側(cè)鹊汛。而此時(shí)ECMAScript 會用符號位的值來填充所有空位蒲赂,以便得到一個(gè)完整的值。
無符號右移
無符號右移操作符由3個(gè)大于號(>>>)表示刁憋,這個(gè)操作符會將數(shù)值的所有32位都向右移動(dòng)滥嘴,然后以0來填充空位。所以至耻,對正數(shù)的無符號右移與有符號右移結(jié)果相同若皱,但對負(fù)數(shù)的結(jié)果就不一樣了镊叁。
無符號右移操作符會把負(fù)數(shù)的二進(jìn)制碼當(dāng)成正數(shù)的二進(jìn)制碼。而且走触,由于負(fù)數(shù)以其絕對值的二進(jìn)制補(bǔ)碼形式表示晦譬,因此就會導(dǎo)致無符號右移后的結(jié)果非常之大。
布爾操作符
布爾操作符一共有3個(gè):
- 非(NOT)
- 與(AND)
- 或(OR)
邏輯非
邏輯非操作符由一個(gè)嘆號(!)表示互广。同時(shí)使用兩個(gè)邏輯非操作符敛腌,實(shí)際上就會模擬Boolean() 轉(zhuǎn)型函數(shù)的行為。
邏輯與
邏輯與操作符由兩個(gè)和號(&&)表示惫皱,有兩個(gè)操作數(shù)像樊。邏輯與操作屬于短路操作,即如果第一個(gè)操作數(shù)能夠決定結(jié)果旅敷,那么就不會再對第二個(gè)操作數(shù)求值生棍。
邏輯或
邏輯或操作符由兩個(gè)豎線符號(||)表示,有兩個(gè)操作數(shù)媳谁。也屬于短路操作符涂滴。
我們可以利用邏輯或的屬性為變量賦值,以避免null 或 undefined 值晴音。
var myObject = preferredObject || backupObject;
ECMAScript 程序的賦值語句經(jīng)常會使用這種模式氢妈。
乘性操作符
ECMAScript 定義了3個(gè)乘性操作符:乘法*、除法/和求模%段多。如果參與乘性計(jì)算的某個(gè)操作數(shù)不是數(shù)值,后臺會先使用Number() 轉(zhuǎn)型函數(shù)將其轉(zhuǎn)換為數(shù)值壮吩。也就是說进苍,空字符串會被當(dāng)作0,布爾值true 將被當(dāng)作1鸭叙。
關(guān)系操作符
- 小于<
- 大于>
- 小于等于<=
- 大于等于>=
一些規(guī)則:
- 如果兩個(gè)操作數(shù)都是數(shù)值觉啊,則執(zhí)行數(shù)值比較
- 如果兩個(gè)操作數(shù)都是字符串,則比較兩個(gè)字符串對應(yīng)的字符編碼值
- 如果一個(gè)操作數(shù)是數(shù)值沈贝,則將另一個(gè)操作數(shù)轉(zhuǎn)換為一個(gè)數(shù)值杠人,然后執(zhí)行數(shù)值比較
- 如果一個(gè)操作數(shù)是對象,則調(diào)用這個(gè)對象的valueof() 方法宋下,用得到的結(jié)果按照前面的規(guī)則執(zhí)行比較嗡善。如果對象沒有valueof() 方法,則調(diào)用toString() 方法学歧,并用得到的結(jié)果根據(jù)前面的規(guī)則執(zhí)行比較罩引。
- 如果一個(gè)操作數(shù)是布爾值檬贰,則先將其轉(zhuǎn)換為數(shù)值豹芯,然后再執(zhí)行比較。
相等操作符
相等和不相等
- 相等==
- 不相等!=
這兩個(gè)操作符都會先轉(zhuǎn)換操作數(shù)(強(qiáng)制轉(zhuǎn)型),然后再比較它們的相等性扔涧。
在轉(zhuǎn)換不同的數(shù)據(jù)類型時(shí),相等和不相等操作符遵循下列基本規(guī)則:
- 如果有一個(gè)操作數(shù)是布爾值限匣,則在比較相等性之前先將其轉(zhuǎn)換為數(shù)值
- 如果一個(gè)操作數(shù)是字符串彤守,另一個(gè)操作數(shù)是數(shù)值,在比較相等性之前先將字符串轉(zhuǎn)換為數(shù)值
- 如果一個(gè)操作數(shù)是對象洒缀,另一個(gè)操作數(shù)不是瑰谜,則調(diào)用對象的valueOf() 方法,用得到的基本類型值按照前面的規(guī)則進(jìn)行比較
進(jìn)行比較時(shí)遵循下列規(guī)則:
- null 和undefined 是相等的
- 要比較相等性之前帝洪,不能將null 和undefined 轉(zhuǎn)換成其他任何值
- 如果有一個(gè)操作數(shù)是NaN似舵,則相等操作符返回false,而不相等操作符返回true葱峡。重要提示:即使兩個(gè)操作數(shù)都是NaN砚哗,相等操作符也返回false;因?yàn)榘凑找?guī)則砰奕,NaN不等于NaN蛛芥。
- 如果兩個(gè)操作數(shù)都是對象,則比較它們是不是同一個(gè)對象军援。如果兩個(gè)操作數(shù)都指向同一個(gè)對象仅淑,則相等操作符返回true;否則胸哥,返回false涯竟。
下表列出一些特殊情況及比較結(jié)果:
全等和不全等
除了在比較之前不轉(zhuǎn)換操作數(shù)之外,全等和不全等操作符沒有什么區(qū)別空厌。全等操作符由3個(gè)等于號(===)表示庐船,它只在兩個(gè)操作數(shù)未經(jīng)轉(zhuǎn)換就相等的情況下返回true。
不全等操作符由一個(gè)嘆號后跟兩個(gè)等于號(!==)表示嘲更,它在兩個(gè)操作數(shù)未經(jīng)轉(zhuǎn)換就不相等的情況下返回true筐钟。
記住:null == undefined 會返回true赋朦,因?yàn)樗鼈兪穷愃频闹德ǔ澹坏玭ull === undefined會返回false,因?yàn)樗鼈兪遣煌愋偷闹怠?/p>
由于相等和不相等操作符存在類型轉(zhuǎn)換問題宠哄,而為了保持代碼中數(shù)據(jù)類型的完整性壹将,我們推薦使用全等和不全等操作符。
條件操作符
variable = boolean_expression ? true_value : false_value;
例如:
var max = (num1 > num2) ? num1 : num2;
賦值操作符
簡單的賦值操作符由等于號(=)表示毛嫉,其作用就是把右側(cè)的值賦給左側(cè)的變量瞭恰。
如果在等于號(=)前面再添加乘性操作符、加性操作符或位操作符狱庇,就可以完成復(fù)合賦值操作惊畏。
設(shè)計(jì)復(fù)合賦值操作符的主要目的是簡化賦值操作恶耽,并不會帶來任何性能的提升。
逗號操作符
使用逗號操作符可以在一條語句中執(zhí)行多個(gè)操作颜启。
語句
if 語句
語法:
if (condition) statement1 else statement2
其中的condition (條件)可以是任意表達(dá)式偷俭;而且對這個(gè)表達(dá)式求值的結(jié)果不一定是布爾值。ECMAScript 會自動(dòng)調(diào)用Boolean() 轉(zhuǎn)換函數(shù)將這個(gè)表達(dá)式的結(jié)果轉(zhuǎn)換為一個(gè)布爾值缰盏。
do-while 語句
do-while 語句是一種后測試循環(huán)語句涌萤,即只有在循環(huán)體中的代碼執(zhí)行之后,才會測試出口條件口猜。換句話說负溪,在對條件表達(dá)式求值之前,循環(huán)體內(nèi)的代碼至少會被執(zhí)行一次济炎。語法:
do {
statement
} while (expression)
while 語句
while 語句屬于前測試循環(huán)語句川抡,也就是說,在循環(huán)體內(nèi)的代碼被執(zhí)行之前须尚,就會對出口條件求值崖堤。因此,循環(huán)體內(nèi)的代碼有可能永遠(yuǎn)不會被執(zhí)行耐床。語法:
while (expression) statement
for 語句
for 語句也是一種前測試循環(huán)語句密幔,但它具有在執(zhí)行循環(huán)之前初始化變量和定義循環(huán)后要執(zhí)行的代碼的能力。
for (initialization; expression; post-loop-expression) statement
使用while 循環(huán)做不到的撩轰,使用 for 循環(huán)同樣也做不到胯甩。也就是說,for 循環(huán)知識把與循環(huán)有關(guān)的代碼集中在了一個(gè)位置堪嫂。
由于ECMAScript 中不存在塊級作用域偎箫,因此在循環(huán)內(nèi)部定義的變量也可以在外部訪問到。
for 語句中的初始化表達(dá)式溉苛、控制表達(dá)式和循環(huán)后表達(dá)式都是可選的。將這三個(gè)表達(dá)式全部省略弄诲,就會創(chuàng)建一個(gè)無限循環(huán)愚战。而只給出控制表達(dá)式實(shí)際上就把for 循環(huán)換成了while 循環(huán)。
for-in 語句
for-in 語句是一種精準(zhǔn)的迭代語句齐遵,可以用來枚舉對象的屬性寂玲。枚舉的是屬性而非值。
for (property in expression) statement
示例:
for (var propName in window){
document.write(propName);
}
在這個(gè)例子中梗摇,使用for-in 循環(huán)來顯示BOM 中window 對象的所有屬性拓哟。
ECMAScript 對象的屬性沒有順序。因此伶授,通過for-in 循環(huán)輸出的屬性名的順序是不可預(yù)測的断序。具體來講流纹,所有屬性都會被返回一次,但返回的先后次序可能會因?yàn)g覽器而異违诗。
為了保證最大限度的兼容性漱凝,建議在使用for-in 循環(huán)之前,先檢測確認(rèn)該對象的值不是null 或 undefined诸迟。
label 語句
使用label 語句可以在代碼中添加標(biāo)簽茸炒,以便將來使用。語法:
label: statement
定義的標(biāo)簽由break 或 continue 語句引用阵苇。加標(biāo)簽的語句一般都要與for 語句等循環(huán)語句配合使用壁公。
break 和 continue 語句
break 和 continue 語句用于在循環(huán)中精確地控制代碼的執(zhí)行。其中绅项,break 語句會立即退出循環(huán)紊册,強(qiáng)制繼續(xù)執(zhí)行循環(huán)后面的語句。而 continue 語句雖然也是立即退出循環(huán)趁怔,但退出循環(huán)后會從循環(huán)的頂部繼續(xù)執(zhí)行湿硝。
break 和 continue 語句都可以與 label 語句聯(lián)合使用,從而返回代碼中特定的位置润努。這種聯(lián)合使用的情況多發(fā)生在循環(huán)嵌套的情況下关斜。
with 語句
with 語句的作用是將代碼的作用域設(shè)置到一個(gè)特定的對象中。語法:
with (expression) statement;
嚴(yán)格模式下不允許使用with 語句铺浇,否則將視為語法錯(cuò)誤痢畜。
由于大量使用with 語句會導(dǎo)致性能下降,同時(shí)也會給調(diào)試代碼造成困難鳍侣,因此在開發(fā)大型應(yīng)用程序時(shí)丁稀,不建議使用with 語句。
switch 語句
switch (expression){
case value: statement
break;
case value: statement
break;
default: statement
}
ECMAScript 中switch 語句中可以使用任何數(shù)據(jù)類型倚聚,無論是字符串线衫,還是對象都沒有問題。且每個(gè)case 的值不一定是常量惑折,可以使變量授账,甚至是表達(dá)式。
switch 語句在比較值時(shí)使用的是全等操作符惨驶,因此不會發(fā)生類型轉(zhuǎn)換白热。
函數(shù)
ECMAScript 中的函數(shù)使用function 關(guān)鍵字來聲明,后跟一組參數(shù)以及函數(shù)體粗卜。語法:
function functionName(arg0, arg1,...,argN){
statements
}
函數(shù)中屋确,可通過return 語句后跟要返回的值來實(shí)現(xiàn)返回值。位于return 語句之后的任何代碼永遠(yuǎn)都不會執(zhí)行。
理解參數(shù)
ECMAScript 函數(shù)的參數(shù)與大多數(shù)其他語言中函數(shù)的參數(shù)有所不同攻臀。ECMAScript 函數(shù)不介意傳遞進(jìn)來多少個(gè)參數(shù)焕数,也不在乎傳進(jìn)來參數(shù)是什么數(shù)據(jù)類型。也就是說茵烈,即便你定義的函數(shù)只接收兩個(gè)參數(shù)百匆,在調(diào)用這個(gè)函數(shù)時(shí)也未必一定要傳遞兩個(gè)參數(shù)∥赝叮可以傳遞一個(gè)加匈、三個(gè)甚至不傳遞參數(shù),而解析器永遠(yuǎn)不會有什么怨言仑荐。之所以會這樣雕拼,原因是ECMAScript 中的參數(shù)在內(nèi)部是用一個(gè)數(shù)組來表示的。函數(shù)接收到的始終是這個(gè)數(shù)組粘招,而不關(guān)心數(shù)組中包含哪些參數(shù)啥寇。實(shí)際上,在函數(shù)體內(nèi)可以通過arguments 對象來訪問這個(gè)參數(shù)數(shù)組洒扎,從而獲得傳遞給函數(shù)的每一個(gè)參數(shù)辑甜。
ECMAScript 函數(shù)的一個(gè)重要特點(diǎn):命名的參數(shù)只提供便利,但不是必需的袍冷。
通過訪問arguments 對象的length 屬性可以獲知有多少個(gè)參數(shù)傳遞給了函數(shù)磷醋。
arguments 對象可以與命名參數(shù)一起使用,且它的值永遠(yuǎn)與對應(yīng)命名參數(shù)的值保持同步胡诗。
沒有傳遞值的命名參數(shù)將自動(dòng)被賦予undefined 值邓线。
沒有重載
ECMAScript 中定義了兩個(gè)名字相同的函數(shù),則該名字只屬于后定義的函數(shù)煌恢。
通過檢查傳入?yún)?shù)中函數(shù)的類型和數(shù)量并作出不同的反應(yīng)骇陈,可以模仿方法的重載。
例如:
function doAdd(){
if(arguments.length == 1){
alert(arguments[0] + 10);
} else if(arguments.length == 2){
alert(arguments[0] + arguments[1])
}
}
第4章 變量瑰抵、作用域和內(nèi)存問題
基本類型和引用類型的值
ECMAScript 變量可能包含兩種不同數(shù)據(jù)類型的值:基本類型值和引用類型值你雌。基本類型值指的是簡單的數(shù)據(jù)段二汛,而引用類型值指那些可能由多個(gè)值構(gòu)成的對象婿崭。
動(dòng)態(tài)的屬性
定義基本類型值和引用類型值的方式是類似的:創(chuàng)建一個(gè)變量并為該變量賦值。對于引用類型的值习贫,我們可以為其添加屬性和方法逛球,也可以改變和刪除其屬性和方法千元。而基本類型值不能被添加苫昌。
復(fù)制變量值
除了保存的方式不同之外,在從一個(gè)變量向另一個(gè)變量復(fù)制基本類型值和引用類型值時(shí),也存在不同祟身。
如果從一個(gè)變量向另一個(gè)變量復(fù)制基本類型的值奥务,會在變量對象上創(chuàng)建一個(gè)新值,然后把該值復(fù)制到為新變量分配的位置上袜硫。示例:
var num1 = 5;
var num2 = num1;
圖示:
當(dāng)從一個(gè)變量向另一個(gè)變量復(fù)制引用類型的值時(shí)氯葬,同樣也會將存儲在變量對象中的值復(fù)制一份放到為新變量分配的空間中。不同的是婉陷,這個(gè)值的副本實(shí)際上是一個(gè)指針帚称,而這個(gè)指針指向存儲在堆中的另一個(gè)對象。復(fù)制操作結(jié)束后秽澳,兩個(gè)變量實(shí)際上將引用同一個(gè)對象闯睹。因此,改變其中一個(gè)變量担神,就會影響另一個(gè)變量楼吃,示例如下:
var obj1 = new Object();
var obj2 = obj1;
obj1.name = "Nicholas";
alert(obj2.name); //"Nicholas"
圖示:
傳遞參數(shù)
ECMAScript 中所有函數(shù)的參數(shù)都是按值傳遞的。也就是說妄讯,把函數(shù)外部的值復(fù)制給函數(shù)內(nèi)部的參數(shù)孩锡,就和把值從一個(gè)變量復(fù)制到另一個(gè)變量一樣。基本類型值的傳遞如同基本類型變量的復(fù)制一樣亥贸,而引用類型值的傳遞躬窜,則如同引用類型變量的復(fù)制一樣。
在向參數(shù)傳遞基本類型的值時(shí)砌函,被傳遞的值會被復(fù)制給一個(gè)局部變量(即命名參數(shù)斩披,或者用ECMAScript 的概念來說,就是arguments 對象的一個(gè)元素)讹俊。在向參數(shù)傳遞引用類型的值時(shí)垦沉,會把這個(gè)值在內(nèi)存中的地址復(fù)制給一個(gè)局部變量,因此這個(gè)局部變量的變化會反映在函數(shù)的外部仍劈。
檢測類型
typeof 操作符是確定一個(gè)變量是字符串厕倍、數(shù)值、布爾值贩疙,還是undefined 的最佳工具讹弯。如果變量的值是一個(gè)對象或null,則typeof 操作符會返回”object“这溅。
檢測是什么類型的對象组民,可以使用instanceof 操作符,語法如下:
result = variable instanceof constructor
如果變量是給定引用類型的實(shí)例悲靴,那么instanceof 操作符就會返回true臭胜。
根據(jù)規(guī)定,所有引用類型的值都是Object 的實(shí)例。因此耸三,在檢測一個(gè)引用類型值和Object 構(gòu)造函數(shù)時(shí)乱陡,instanceof 操作符始終會返回true。當(dāng)然仪壮,如果使用instanceof 操作符檢測基本類型的值憨颠,則該操作符會返回false,因?yàn)榛绢愋筒皇菍ο蟆?/p>
小結(jié)
- 基本類型值在內(nèi)存中占據(jù)固定大小的空間积锅,因此被保存在棧內(nèi)存中
- 從一個(gè)變量向另一個(gè)變量復(fù)制基本類型的值爽彤,會創(chuàng)建這個(gè)值的一個(gè)副本
- 引用類型的值是對象,保存在堆內(nèi)存中
- 包含引用類型值的變量實(shí)際上包含的并不是對象本身缚陷,而是一個(gè)指向該對象的指針
- 從一個(gè)變量向另一個(gè)變量復(fù)制引用類型的值淫茵,復(fù)制的其實(shí)是指針,因此兩個(gè)變量最終都指向同一個(gè)對象
- 確定一個(gè)值是哪種基本類型可以使用typeof 操作符蹬跃,而確定一個(gè)值是哪種引用類型可以使用instanceof 操作符匙瘪。
執(zhí)行環(huán)境及作用域
執(zhí)行環(huán)境定義了變量或函數(shù)有權(quán)訪問的其他數(shù)據(jù),決定了它們各自的行為蝶缀。每個(gè)執(zhí)行環(huán)境都有一個(gè)與之關(guān)聯(lián)的變量對象丹喻,環(huán)境中定義的所有變量和函數(shù)都保存在這個(gè)對象中。雖然我們編寫的代碼無法訪問這個(gè)對象翁都,但解析器在處理數(shù)據(jù)時(shí)會在后臺使用它碍论。
全局執(zhí)行環(huán)境是最外圍的一個(gè)執(zhí)行環(huán)境。在Web 瀏覽器中柄慰,全局執(zhí)行環(huán)境被認(rèn)為是window 對象鳍悠,因此所有全局變量和函數(shù)都是作為window 對象的屬性和方法創(chuàng)建的。
每個(gè)函數(shù)都有自己的執(zhí)行環(huán)境坐搔。當(dāng)執(zhí)行流進(jìn)入一個(gè)函數(shù)時(shí)藏研,函數(shù)的環(huán)境就會被推入一個(gè)環(huán)境棧中。而在函數(shù)執(zhí)行之后概行,棧將其環(huán)境彈出蠢挡,把控制權(quán)返回給之前的執(zhí)行環(huán)境。
當(dāng)代碼在一個(gè)環(huán)境中執(zhí)行時(shí)凳忙,會創(chuàng)建變量對象的一個(gè)作用域鏈业踏。作用域鏈的用途,是保證對執(zhí)行環(huán)境有權(quán)訪問的所有變量和函數(shù)的有序訪問涧卵。作用域鏈的前端勤家,始終都是當(dāng)前執(zhí)行的代碼所在環(huán)境的變量對象。如果這個(gè)環(huán)境是函數(shù)柳恐,則將其活動(dòng)對象作為變量對象伐脖⊥╇活動(dòng)對象在最開始時(shí)只包含一個(gè)變量,即arguments 對象(這個(gè)對象在全局環(huán)境中是不存在的)晓殊。作用域鏈中的下一個(gè)變量對象來自包含(外部)環(huán)境,而再下一個(gè)變量對象則來自下一個(gè)包含環(huán)境伤提。這樣巫俺,一直延續(xù)到全局執(zhí)行環(huán)境;全局執(zhí)行環(huán)境的變量對象始終都是作用域鏈中的最后一個(gè)對象肿男。
標(biāo)識符解析是沿著作用域鏈一級一級地搜索標(biāo)識符的過程介汹。搜索過程始終從作用域鏈的前端開始,然后逐級地向后回溯舶沛,直至找到標(biāo)識符為止(如果找不到標(biāo)識符嘹承,通常會導(dǎo)致錯(cuò)誤發(fā)生)。
內(nèi)部環(huán)境可以通過作用域鏈訪問所有的外部環(huán)境如庭,但外部環(huán)境不能訪問內(nèi)部環(huán)境中的任何變量和函數(shù)叹卷。這些環(huán)境之間的聯(lián)系是線性、有次序的坪它。每個(gè)環(huán)境都可以向上搜索作用域鏈骤竹,以查詢變量和函數(shù)名;但任何環(huán)境都不能通過向下搜索作用域鏈而進(jìn)入另一個(gè)執(zhí)行環(huán)境往毡。
函數(shù)參數(shù)也被當(dāng)做變量來對待蒙揣,因此其訪問規(guī)則與執(zhí)行環(huán)境中的其他變量相同。
延長作用域鏈
當(dāng)執(zhí)行流進(jìn)入下列任何一個(gè)語句時(shí)开瞭,作用域鏈就會得到加長:
- try-catch 語句的catch 塊
- with 語句
這兩個(gè)語句都會在作用域鏈的前端添加一個(gè)變量對象懒震。對with 語句來說,會將指定的對象添加到作用域鏈中嗤详。對catch 語句來說个扰,會創(chuàng)建一個(gè)新的變量對象,其中包含的是被拋出的錯(cuò)誤對象的聲明葱色。
沒有塊級作用域
使用var 聲明的變量會自動(dòng)被添加到最接近的環(huán)境中锨匆。在函數(shù)內(nèi)部,最接近的環(huán)境就是函數(shù)的局部環(huán)境冬筒;在with 語句中恐锣,最接近的環(huán)境是函數(shù)環(huán)境。如果初始化變量時(shí)沒有使用var 聲明舞痰,該變量會自動(dòng)被添加到全局環(huán)境土榴。
當(dāng)在某個(gè)環(huán)境中為了讀取或?qū)懭攵靡粋€(gè)標(biāo)識符時(shí),必須通過搜索來確定該標(biāo)識符實(shí)際代表什么响牛。搜索過程從作用域鏈的前端開始玷禽,向上逐級查詢與給定名字匹配的標(biāo)識符赫段。如果在局部環(huán)境中找到了該標(biāo)識符,搜索過程停止矢赁,變量就緒糯笙。如果在局部環(huán)境中沒有找到該變量名,則繼續(xù)沿作用域鏈向上搜索撩银。搜索過程將一直追溯到全局環(huán)境的變量對象给涕。如果在全局環(huán)境中也沒有找到這個(gè)標(biāo)識符,則意味著該變量尚未聲明额获。
在這個(gè)搜索過程中够庙,如果存在一個(gè)局部的變量的定義,則搜索會自動(dòng)停止抄邀,不再進(jìn)入另一個(gè)變量對象耘眨。換句話說,如果局部環(huán)境中存在著同名標(biāo)識符境肾,就不會使用位于父環(huán)境中的標(biāo)識符剔难。
如果在局部環(huán)境中需要訪問同名的全局變量,需添加window
奥喻。
小結(jié)
- 執(zhí)行環(huán)境有全局執(zhí)行環(huán)境(也稱為全局環(huán)境)和函數(shù)執(zhí)行環(huán)境之分
- 每次進(jìn)入一個(gè)新執(zhí)行環(huán)境钥飞,都會創(chuàng)建一個(gè)用于搜索變量和函數(shù)的作用域鏈
- 函數(shù)的局部環(huán)境不僅有權(quán)訪問函數(shù)作用域中的變量,而且有權(quán)訪問其包含(父)環(huán)境衫嵌,乃至全局環(huán)境
- 全局環(huán)境只能訪問在全局環(huán)境中定義的變量和函數(shù)读宙,而不能直接訪問局部環(huán)境中的任何數(shù)據(jù)
- 變量的執(zhí)行環(huán)境有助于確定應(yīng)該何時(shí)釋放內(nèi)存
垃圾收集
JavaScript 具有自動(dòng)垃圾收集機(jī)制,也就是說楔绞,執(zhí)行環(huán)境會負(fù)責(zé)管理代碼執(zhí)行過程中使用的內(nèi)存结闸。
- 離開作用域的值將被自動(dòng)標(biāo)記為可以回收,因此將在垃圾收集期間被刪除酒朵。
- ”標(biāo)記清除“是目前主流的垃圾收集算法桦锄,這種算法的思想是給當(dāng)前不使用的值加上標(biāo)記,然后再回收其內(nèi)存蔫耽。
- 另一種垃圾收集算法是”引用計(jì)數(shù)“结耀,這種算法的思想是跟蹤記錄所有值被引用的次數(shù)。JavaScript 引擎目前都不再使用這種算法匙铡。
- 在代碼中存在循環(huán)引用現(xiàn)象時(shí)图甜,”引用計(jì)數(shù)“算法就會導(dǎo)致問題。
- 解除變量的引用不僅有助于消除循環(huán)引用現(xiàn)象鳖眼,而且對垃圾收集也有好處黑毅。為了確保有效地回收內(nèi)存,應(yīng)該及時(shí)解除不再使用的全局對象钦讳、全局對象屬性以及循環(huán)應(yīng)用變量的引用矿瘦。
確保占用最少的內(nèi)存可以讓頁面獲得更好的性能枕面。而優(yōu)化內(nèi)存占用的最佳方式,就是為執(zhí)行中的代碼只保存必要的數(shù)據(jù)缚去。一旦數(shù)據(jù)不再有用潮秘,最好通過將其設(shè)置為null 來釋放其引用——這個(gè)做法叫做解除引用。這一做法適用于大多數(shù)全局變量和全局對象的屬性易结。局部變量會在它們離開執(zhí)行環(huán)境時(shí)自動(dòng)被解除引用枕荞。
不過,解除一個(gè)值的引用并不意味著自動(dòng)回收該值所占用的內(nèi)存衬衬。解除引用的真正作用是讓值脫離執(zhí)行環(huán)境,以便垃圾收集器下次運(yùn)行時(shí)將其回收改橘。