JavaScript簡介
JavaScript簡史
JavaScript誕生于1997年,
由于JavaScript 1.0獲得巨大成功,Netscape在Netscape Navigator 3中發布JavaScript 1.1,
1997年,JavaScript 1.1為藍本的建議被提交給了歐洲計算機制造商協會(ECMA,European Computer Manufacturers Association),該協會指定39號技術委員會負責“標準化一種通用、跨平臺、供應商中立的腳本語言的語法和語意”,
第二年,ISO/IEC也采用ECMAScript作為標準,
JavaScript實作
一個完整的JavaScript實作應該由下列三個不同的部分組成:
- 核心(ECMAScript)
- 檔案物件模型(DOM)
- 瀏覽器物件模型(BOM)
ECMAScript
我們常見的web瀏覽器只是ES實作可能的宿主環境之一,宿主環境不僅提供基本的ES實作,同時也會提供該語言的擴展,以便語言與環境之間對接互動,
ECMA-262標準沒有參照web瀏覽器,它規定了這門語言的下列組成部分:語法、型別、陳述句、關鍵字、保留字、運算子、物件,
什么是ECMAScript兼容
要想成為ECMAScript的實作,則該實作必須做到:
- 支持ECMA-262描述的所有“型別、值、物件、屬性、函式以及程式句法和語意”;
- 支持Unicode字符標準;
此外,兼容的實作還可以進行下列擴展:
- 添加ECMA-262沒有描述的“更多型別、值、物件、屬性和函式”;
- 可以修改和擴展內置的正則運算式語法;
檔案物件模型(DOM)
DOM是針對XML但經過擴展用于HTML的應用程式編程介面(API)
瀏覽器物件模型(BOM)
從根本上將,BOM只處理瀏覽器視窗和框架;但人們習慣上也把所有針對瀏覽器的JavaScript擴展算作BOM的一部分,下面就是一些這樣的擴展:
- 彈出新瀏覽器視窗的功能
- 移動、縮放和關閉瀏覽器視窗的功能
- 提供瀏覽器詳細資訊的navigator物件
- 提供瀏覽器所加載頁面的詳細資訊的location物件
- 提供用戶顯示幕解析度詳細資訊的screen物件
- 對cookies的支持
- 像XMLHttpRequest和IE的ActiveXObject這樣的自定義物件
小結
JavaScript是一種專門與網頁互動而設計的腳本語言,由下列三個部分組成:
- ECMAScript,由ECMA-262定義,提供核心語言功能;
- 檔案物件模型(DOM),提供訪問和操作網頁內容的方法和介面;
- 瀏覽器物件模型(BOM),提供與瀏覽器互動的方法和介面;
在HTML中使用JavaScript
script元素
向HTML頁面中插入JavaScript的主要方法就是使用<script>元素,
HTML 4.01 為<script>定義了下列6個屬性:
- async:可選,表示應該立即下載腳本,但不應妨礙頁面中的其他操作,比如下載其他資源或等待加載其他腳本,只對外部腳本檔案有效;
- charset:可選,表示通過src屬性指定的代碼的字符集,由于大多數瀏覽器會忽略它的值,因此這個屬性很少有人用;
- defer:可選,表示腳本可以延遲到檔案完全被決議和顯示之后再執行,只對外部腳本檔案有效;
- language:已廢棄;
- src:可選,表示包含要執行代碼的外部檔案;
- type:可選,表示撰寫代碼使用的腳本語言的內容型別(也稱為MIME型別);
使用<script>元素的方式有兩種:直接在頁面中嵌入JavaScript代碼和包含外部JavaScript檔案,
- 在頁面中使用 <script>元素嵌入JavaScript代碼時,只須為 <script>指定type屬性,然后,像下面這樣把JavaScript代碼直接放在元素內部即可:
<script type="text/javascript">
function sayHi(){
alert("hello");
}
</script>
注意:在使用頁面嵌入JavaScript代碼時,記住千萬不要在代碼中任何地方出現“</script>”字串,因為當瀏覽器遇到字串“</script>”時,就會認為那是結束標簽,應用通過轉義字符“\”解決此問題,“<\ /script>”,
- 使用</script>元素來包含外部JavaScript檔案,那src屬性就是必須的,這個屬性的值指向外部JavaScript檔案鏈接,例如:
<script type="text/javascript" src="https://www.cnblogs.com/LqZww/p/index.js"></script>
注意:帶有src屬性的<script>元素不應該在其<script>和</script>標簽之間再添加額外的JavaScript代碼,如果包含了嵌入的代碼,嵌入的代碼會被忽略,
另外,通過<script>元素的src屬性還可以包含來自外部域的JavaScript檔案,
標簽的位置
按照傳統的做法,所有<script>元素都應該放在頁面中<head>元素中,這種做法的目的就是把所有外部檔案(CSS檔案和JavaScript檔案)的參考都放在相同的地方,
可是這種做法有一種缺點就是,必須要等到全部JavaScript代碼都被下載、決議和執行完成以后,才能開始出現頁面的內容(瀏覽器在遇到<body>標簽時才開始出現內容),對于需要很多JavaScript代碼的頁面來說,這會導致頁面出現明顯的延遲,而在延遲期間頁面是一片空白,所以,為了避免這個問題,我們應該把JavaScript參考放在<body>元素的頁面內容的后面,例如:
<!DOCTYPE html>
<html>
<head>
<title>demo</title>
</head>
<body>
<!-- html代碼 -->
<script type="text/javascript" src="https://www.cnblogs.com/LqZww/p/index.js"></script>
</body>
</html>
延遲腳本
HTML 4.01 為<script>標簽定義了defer屬性,用途是表明腳本在執行時不會影響頁面的構造,也就是說,腳本會被延遲到整個頁面都決議完畢后再運行,因此,在<script>元素中設定defer屬性,相當于告訴瀏覽器立即下載,但延遲執行,
假設把<script>元素放在<head>元素之中,并加上defer屬性,但其中包含的腳本將延遲到瀏覽器遇到</html>標簽后再執行,HTML5 規范要求腳本按照它們出現的先后順序執行,因此第一個延遲腳本會先于第二個延遲腳本執行,而這兩個腳本會先于DOMContentLoaded事件執行,但在現實中,延遲腳本不一定會按順序執行,也不一定會在DOMContentLoaded事件觸發前執行,因此最好只包含一個延遲腳本,
defer屬性只適用于外部腳本檔案,
異步腳本
HTML5 為<script>元素定義了async屬性,與defer屬性類似,都用于改變處理腳本的行為,也只適用于外部腳本檔案,并告訴瀏覽器立即下載檔案,但不同的是,標記為async的腳本并不保證按照指定它們的先后順序執行,
例如,在<head>元素中放兩個<script>元素,第二個腳本檔案可能會在第一個腳本檔案前執行,指定async屬性的目的是不讓頁面等待兩個腳本下載和執行,從而異步加載頁面其他內容,因此,建議異步腳本不要在加載期間修改DOM,
異步腳本一定會在頁面的load事件前執行,但可能會在DOMContentLoaded事件觸發前或后執行,
嵌入代碼與外部檔案
使用外部檔案的優點:
- 可維護性:遍及不同HTML頁面的JavaScript會造成維護問題,但把所有JavaScript檔案都放在一個檔案夾中維護起來就輕松很多,
- 可快取:如果有兩個頁面都使用同一個檔案,那么這個檔案只需下載一次,最終結果是能夠加快頁面的加載速度,
- 適應未來
檔案模式
兩種檔案模式:混雜模式(quirks mode)、標準模式(standards mode)
noscript元素
這個元素用以在不支持JavaScript的瀏覽器中顯示替代的內容,
包含<noscript>元素中的內容只有在下面的情況才會顯示出來:
- 瀏覽器不支持腳本
- 瀏覽器支持腳本,但腳本被禁用
小結
使用包含外部的JavaScript檔案,需要注意的地方:
- 在包含外部JavaScript檔案時,必須將src屬性設定為指向相應檔案的URL,
- 所有<script>元素都會按照它們在頁面出現的先后順序依次被決議,在不使用defer和async屬性時,只有決議完前面<script>元素代碼之后,才會開始決議后面<script>元素的代碼,
- 一般把<script>元素放在頁面最后,即主要內容后面,</body>標簽前面,
- 使用defer屬性可以讓腳本在檔案完全呈現之后再執行,延遲腳本總是按照指定它們的順序執行,
- 使用async屬性可以表示當前腳本不必等待其他腳本,也不必阻塞檔案呈現,不能保證異步腳本按照它們在頁面中出現的順序執行,
語法
語法
ECMAScript的語法借鑒了C及其他類C語言(如Java、Perl)的語法,
區分大小寫
ECMAScript中的一切(變數、函式名和運算子)都區分大小寫,
識別符號
識別符號就是指變數、函式、屬性的名字,或函式的引數,
識別符號按照以下規則組合:
- 第一個字符必須是字母、下劃線(_)或美元符號($)
- 其他字符可以是字母、下劃線、美元符號或數字
不能把關鍵字、保留字、true、false、null用作識別符號,
注釋
單行注釋、多行注釋:
//單行注釋
/*
這是多行注釋
*/
嚴格模式
ECMAScript 5 引入嚴格模式(strict mode)的概念,嚴格模式是為JavaScript定義了一種不同的決議與執行模型,在嚴格模式下,一些不確定的行為將得到處理,對某些不安全的操作會拋出錯誤,
要在整個腳本中啟用嚴格模式,可以在頂部添加下列代碼:
"use strict";
它是一個編譯指示(pragma),用來告訴支持JavaScript引擎切換到嚴格模式,
可以指定函式在嚴格模式下執行:
function doSomething(){
"use strict";
//函式體
}
在嚴格模式下,JavaScript的執行結果會有很大不同,
陳述句
ECMAScript中陳述句以一個分號結尾,如果省略分號,則由決議器確定陳述句的結尾,
雖然陳述句結尾的分號并不是必須的,但最好還是不要省略,因為加上分號可以避免很多錯誤,也會在某些情況下增進代碼性能,
關鍵字和保留字
ECMA-262描述了一組具有特定用途的關鍵字,可用于表示控制陳述句的開始或結束,或用于執行特定操作等,關鍵字不能用作識別符號,
ECMA-262還描述了一組不能用作識別符號的保留字,
變數
ECMAScript的變數是松散型別的,松散型別就是可以用來保存任何型別的資料,
定義變數時要使用var運算子(var是一個關鍵字),后跟變數名(即識別符號),
雖然省略var運算子可以定義全域變數,但這也不是我們所推薦的,因為在區域作用域中定義的全域變數很難維護,而且如果有意地忽略了var運算子,也會由于相應變數不會馬上就有定義而導致不必要的混亂,給未經宣告的變數賦值在嚴格模式下會拋出ReferenceError錯誤,
在嚴格模式下,不能定義名為eval或arguments的變數,會導致語法錯誤,
資料型別
簡單資料型別(基本資料型別):undefined、null、boolean、number、string、symbol(ES 6新引入的資料型別),
復雜資料型別:object,
typeof運算子
由于ECMAScript是松散型別,因此需要有一種手段來檢測給定變數的資料型別——typeof就是負責提供這方面資訊的運算子,
對一個值使用typeof運算子可能回傳下列某個字串:
- “undefined”-如果這個值未定義
- “boolean”-如果這個值是布林值
- “string”-如果這個值是字串
- “number”-如果這個值是數值
- “object”-如果這個值是物件或null
- “function”-如果這個值是函式
typeof是一個運算子而不是函式
Undefined型別
Undefined型別只有一個值,即特殊的undefined,
在使用var宣告變數但未對其加以初始化時,這個變數的值就是Undefined,
對未初始化的變數執行typeof運算子會回傳Undefined值,而對未宣告的變數執行typeof運算子同樣也會回傳Undefined值,
Null型別
Null型別也只有一個值,這個特殊的值是null,
使用typeof運算子檢測null值時會回傳“object”:
var car = null
alert(typeof car); // "object"
Boolean型別
Boolean型別只有兩個值:true、false,
注意:Boolean型別的值是區分大小寫的,True、False(以及其他的混合大小寫形式)都不是Boolean的值,只是識別符號,
要將一個值轉換為其對應的Boolean值,可以呼叫轉型函式Boolean(),
Number型別
八進制字面值的第一位必須是零(0),然后是八進制數字序列(0~7),如果字面值中的數值超出了范圍,那前導零將被忽略,后面的數值將被當作十進制數決議,
十六進制字面值的前兩位必須是0x,后面跟任何十六進制數字(0~9及A~F),其他A~F可以大寫,也可以小寫,
在進行算術計算時,所有以八進制、十六進制表示的數值最后將被轉換成十進制數值,
浮點數值
浮點數值就是該數值中必須包含一個小數點,并且小數點后面必須至少有一位數字,
數值范圍
ECMAScript能夠表示的最小數值存在Number.MIN_VALUE中,在大多數瀏覽器中,這個值是5e-324,
ECMAScript能夠表示的最大數值存在Number.MAX_VALUE中,在大多數瀏覽器中,這個值是1.7976931348623157e+308,
如果計算結果超出了JavaScript數值范圍時,那么這個數值將被自動轉換成Infinity值,如果這個數值為負數,將被轉換成-Infinity(負無窮),如果這個數值為正數,將被轉換成Infinity(正無窮),
NaN
即非數值是一個特殊的數值,這個數值用于表示一個本來要回傳數值的運算元未回傳數值的情況(這樣就不會拋出錯誤),
有兩個特點,首先,任何涉及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()函式的轉換規則:
- 如果是boolean值,true、false將分別轉為1和0,
- 如果是數字值,只是簡單的傳入和回傳,
- 如果是null值,回傳0,
- 如果是undefined,回傳NaN,
- 如果是字串,遵循下列規則:
- 如果字串中只包含數字(包括帶正負號情況),則將其轉換為十進制數值(前導的零會被忽略),
- 如果字串中包含有效的浮點格式,則將其轉換為對應的浮點數值(前導的零會被忽略),
- 如果字串中包含有效的十六進制格式,則將其轉換為相同大小的十進制數值,
- 如果字串為空(不含任何字符),則將其轉換為0,
- 如果字串中包含除上述格式以外的字符,則將其轉換為NaN,
- 如果是物件,則呼叫物件的valueOf()方法,然后依照前面的規則轉換回傳的值,如果轉換結果為NaN,則呼叫物件的toString()方法,然后再依照前面的規則轉換回傳的字串值,
String型別
String型別用于表示由零或多個16位Unicode字符組成的字符序列,即字串,
字符字面量
String資料型別包含一些特殊的字符字面量,也叫轉義序列,
\n 換行
\t 制表
\b 退格
\r 回車
\f 進紙
\\ 斜杠
' 單引號(')
" 雙引號(")
字串的特點
ECMAScript的字串是不可變的,也就是說,字串一旦創建,它們的值就不能改變,如果要改變某個變數保存的字串,首先要摧毀原來的字串,然后再用另一個包含新值得字串填充該變數,
轉換為字串
把一個值轉換為一個字串有兩種方式,第一種是使用幾乎每個值都有得toString()方法,
在不知道要轉換的值是不是null或undefined的情況時,還可以使用轉型函式String(),這個函式能夠將任何型別的值轉換為字串,
String()函式遵循下列轉換規則:
- 如果值由toString()方法,則呼叫該方法(沒有引數)并回傳相應的結果,
- 如果值是null,則回傳"null",
- 如果值是undefined,則回傳"undefined",
Object型別
ECMAScript中的物件其實就是一組資料和功能的集合,
物件可以通過執行new運算子后跟要創建的物件型別的名稱來創建,而創建Object型別的實體并為其添加屬性和(或)方法,就可以創建自定義物件,
Object的每個實體都具有下列屬性和方法:
- constructor:保存著用于創建當前物件的函式,
- hasOwnProperty(propertyName):用于檢查給定的屬性在當前物件實體中是否存在,
- isPrototypeOf(object):用于檢查傳入的物件是否是當前物件的原型,
- propertyIsEnumerable(propertyName):用于檢查給定的屬性是否能夠使用for-in陳述句來列舉,
- toLocaleString():回傳物件的字串表示,該字串與執行環境的地區對應,
- toString():回傳物件的字串表示,
- valueOf():回傳物件的字串、數值或布林值表示,通常與toString()方法的回傳值相同,
運算子
運算子包括算術運算子(如加號和減號)、位運算子、關系運算子、相等運算子,
一元運算子
只能操作一個值的運算子叫一元運算子,
遞增和遞減運算子
前置型、后置型,前置型應該位于要操作的變數之前,后置型應該位于要操作的變數之后,
執行前置遞增和遞減操作時,變數的值都是在陳述句被求值以前改變的,
后置的遞增和遞減操作是在包含它們的陳述句被求值之后才執行的,
在應用于不同的值時,遞增和遞減運算子遵循下列規則:
- 在應用于一個包含有效數字字符的字串時,先將其轉換為數字值,再執行加減1的操作,字串變數變成數值變數,
- 在應用于一個不包含有效數字字符的字串時,將變數的值設定為NaN,字串變數變成數值變數,
- 在應用于布林值false時,先將其轉換為0再執行加減1的操作,布林值變數變成數值變數,
- 在應用于布林值true時,先將其轉換為1再執行加減1的操作,布林值變數變成數值變數,
- 在應用于浮點數值時,執行加減1的操作,
- 在應用于物件時,先呼叫物件的value()方法以取得一個可供操作的值,然后對該值應用前述規則,如果結果是NaN,則在呼叫toString()方法后再應用前述規則,
一元加和減運算子
一元加運算子以一個加號(+)表示,放在數值前面,對數值不會產生任何影響,
一元減運算子主要用于表示負數,
位運算子
位運算子不直接操作64位的值,而是先將64位的值轉換成32位的整數,然后執行操作,最后再將結果轉換回64位,
對于有符號的整數,32位中的前31位用于表示整數的值,第32位表示數值的符號:0表示正數,1表示負數,這個表示符號的位叫符號位,
負數以二進制碼存盤,使用的格式是二進制補碼,計算一個數值的二進制補碼,經過以下步驟:
- 求這個數值絕對值的二進制碼;
- 求二進制反碼,即將0替換為1,1替換為0;
- 得到的二進制反碼加1,
根據這3個步驟求-18的二進制碼,首先求18的二進制碼:
0000 0000 0000 0000 0000 0000 0001 0010
然后,求其二進制反碼:
1111 1111 1111 1111 1111 1111 1110 1101
最后,二進制反碼加1:
1111 1111 1111 1111 1111 1111 1110 1101
1
-------------------------------------------------------------------------------
1111 1111 1111 1111 1111 1111 1110 1110
就求得-18的二進制表示,
如果對非數值應用位運算子,會先使用Number()函式將該值轉換為一個數值,然后再應用位操作,得到的結果是一個數值,
按位非(NOT)
按位非運算子由一個波浪線(~)表示,
執行按位非得結果就是回傳數值得反碼,
按位非操作的本質:運算元的負值減1,
按位非是在數值表示的最底層執行操作,因此速度更快,
按位與(AND)
按位與運算子由一個和號字符(&)表示,它由兩個運算子數,
按位與操作只在兩個數值的對應位都是1時才回傳1,任何一位是0,結果都是0,
按位或(OR)
按位或運算子由一個豎線(|)表示,也有兩個運算子,
按位或操作在有一個位是1的情況下就回傳1,而只有在兩個位都是0的情況下才回傳0,
按位異或(XOR)
按位異或運算子由一個插入符號(^)表示,也有兩個運算子,
按位異或操作在兩個數值對應位上只有一個1時才回傳1,如果對應的兩位都是1或0,則回傳0,
左移
左移運算子由兩個小于號(<<)表示,這個運算子會將數值的所有位向左移動指定的位數,
在向左移位后,原數值的右側就多出了空位,就用0來填充空位,
左移不會影響運算元的符號位,
有符號的右移
有符號的右移運算子由兩個大于號(>>)表示,這個運算子會將數值向右移動,但保留符號位(即正負號標記),
在右移程序中,原數值出現空位,用符號位的值來填充空位,
無符號右移
無符號右移運算子由3個大于號(>>>)表示,這個運算子會將數值的所有32位都向右移動,對正數來說,無符號右移的結果與有符號右移相同,
對于負數來說,無符號右移是以0來填充空位,
布爾運算子
布爾運算子一共有3個:非(NOT)、與(AND)、或(OR),
邏輯非
邏輯非運算子由一個嘆號(!)表示,邏輯非運算子首先會將它的運算元轉換為一個布林值,然后再對其求反,
邏輯非運算子遵循以下規則:
- 如果運算元是一個物件,回傳false;
- 如果運算元是一個空字串,回傳true;
- 如果運算元是一個非空字串,回傳false;
- 如果運算元是數值0,回傳true;
- 如果運算元是任意非0數值(包括Infinity),回傳false;
- 如果運算元是null,回傳true;
- 如果運算元是NaN,回傳true;
- 如果運算元是undefined,回傳true,
邏輯非運算子也可以用于將一個值轉換為與其對應的布林值,用同時使用兩個邏輯非運算子(!!),
邏輯與
邏輯與運算子由兩個和號(&&)表示,有兩個運算元,
在有一個運算元不是布林值的情況下,邏輯與操作不一定回傳布林值,此時遵循下列規則:
- 如果第一個運算元是物件,則回傳第二個運算元;
- 如果第二個運算元是物件,則只有在第一個運算元的求值結果為true的情況下才會回傳該物件;
- 如果兩個運算元都是物件,則回傳第二個運算元;
- 如果第一個運算元是null,則回傳null;
- 如果第一個運算元NaN,則回傳NaN;
- 如果一個運算元是undefined,則回傳undefined,
邏輯與操作屬于短路操作,即第一個運算元能夠決定結果,那么就不會再對第二個運算元求值,
邏輯或
邏輯或運算子由兩個豎線符號(||)表示,也有兩個運算元,
遵循以下規則:
- 如果第一個運算元是物件,則回傳第一個運算元;
- 如果第一個運算元的求值結果為false,則回傳第二個運算元;
- 如果兩個運算元都是物件,則回傳第一個運算元;
- 如果兩個運算元都是null,則回傳null;
- 如果兩個運算元都是NaN,則回傳NaN;
- 如果兩個運算元都是undefined,則回傳undefined,
邏輯或運算子也是短路運算子,如果第一個運算元的求值結果為true,就不會對第二個運算元求值,
乘性運算子
3個乘性運算子:乘法、除法、求模,
乘法
乘法運算子由一個星號(*)表示,用于計算兩個數值的乘積,
在處理特殊值情況下,遵循下列特殊規則:
- 如果運算元都是數值,執行常規的乘法計算;
- 如果有一個運算元是NaN,則結果是NaN;
- 如果是Infinity與0相乘,則結果是NaN;
- 如果是Infinity與非0數值相乘,則結果是Infinity或-Infinity,取決于有符號運算子的符號;
- 如果是Infinity與Infinity相乘,則結果是Infinity;
- 如果有一個運算元不是數值,則在后臺呼叫Number()將其轉換為數值,然后再應用以上規則,
除法
除法運算子由一個斜線符號(/)表示,執行第二個運算元除第一個運算元的計算,
在處理特殊值情況下,遵循下列特殊規則:
- 如果運算元都是數值,執行常規的除法計算;
- 如果有一個運算元是NaN,則結果是NaN;
- 如果是Infinity被Infinity除,則結果是NaN;
- 如果是零被零除,則結果是NaN;
- 如果是非零的有限數被零除,則結果是Infinity或-Infinity,取決于有符號運算元的符號;
- 如果是Infinity被任何非零數值除,則結果是Infinity或-Infinity,取決于有符號運算元的符號;
- 如果有一個運算元不是數值,則在后臺呼叫Number()將其轉換為數值,然后再應用以上規則,
求模
求模(余數)運算子由一個百分號(%)表示,
在處理特殊值情況下,遵循下列特殊規則:
- 如果運算元都是數值,執行常規的除法計算,回傳除得的余數;
- 如果被除數是無窮大值而除數是有限大的數值,則結果是NaN;
- 如果被除數是有限大的數值而除數是零,則結果是NaN;
- 如果是Infinity被Infinity除,則結果是NaN;
- 如果被除數是有限大的數值而除數是無窮大的數值,則結果是被除數;
- 如果被除數是零,則結果是零;
- 如果有一個運算元不是數值,則在后臺呼叫Number()將其轉換為數值,然后再應用以上規則,
加性運算子
加法
加法運算子(+)
如果兩個運算子都是數值,執行常規的加法計算,然后根據下列規則回傳結果:
- 如果有一個運算元是NaN,則結果是NaN;
- 如果是Infinity加Infinity,則結果是Infinity;
- 如果是-Infinity加-Infinity,則結果是-Infinity;
- 如果是Infinity加-Infinity,則結果是NaN;
- 如果是+0加+0,則結果是+0;
- 如果是-0加-0,則結果是-0;
- 如果是+0加-0,則結果是+0,
如果有一個運算子是字串,就要應用如下規則:
- 如果兩個運算元都是字串,則將第二個運算元與第一個運算元拼接起來;
- 如果只有一個運算元是字串,則將另一個運算元轉換為字串,然后再將兩個字串拼接起來,
如果有一個運算元是物件、數值、布林值,則呼叫它們的toString()方法取得相應的字串值,然后再應用關于字串的規則,
對于undefined和null,則分別呼叫String()函式并取得字串“undefined”和“null”,
減法
減法運算子(-)
一樣需要遵循如下特殊規則:
- 如果兩個運算子都是數值,則執行常規的算術減法操作并回傳結果;
- 如果有一個運算元是NaN,則結果是NaN;
- 如果是Infinity減Infinity,則結果是NaN;
- 如果是-Infinity減-Infinity,則結果是NaN;
- 如果是Infinity減-Infinity,則結果是Infinity;
- 如果是-Infinity減Infinity,則結果是-Infinity;
- 如果是+0減+0,則結果是+0;
- 如果是-0減+0,則結果是-0;
- 如果是-0減-0,則結果是+0;
- 如果有一個運算元是字串、布林值、null或undefined,則先在后臺呼叫Number()函式將其轉換為數值,然后再根據前面規則執行減法計算,如果轉換結果是NaN,則減法的結果就是NaN;
- 如果有一個運算元是物件,則呼叫物件的valueOf()方法以取得表示該物件的數值,如果得到的值是NaN,則減法的結果就是NaN,如果物件沒有valueOf()方法,則呼叫其toString()方法并將得到的字符轉換為數值,
關系運算子
小于(<)、大于(>)、小于等于(<=)和大于等于(>=)這幾個關系運算子用于對兩個值進行比較,這幾個運算子都回傳一個布林值,
當關系運算子的運算元使用了非數值時,要進行資料轉換或完成某些操作,以下就是其規則:
- 如果兩個運算元都是數值,則執行數值比較;
- 如果兩個運算元都是字串,則比較兩個字串對應的字符編碼值;
- 如果一個運算元是數值,則將另一個運算元轉換為一個數值,然后執行數值比較;
- 如果一個運算元是物件,則呼叫這個物件的valueOf()方法,用得到的結果按照前面規則比較,如果物件沒有valueOf()方法,則呼叫toString()方法,并用得到的結果根據前面規則比較;
- 如果一個運算元是布林值,則先將其轉換為數值,然后再比較,
相等運算子
ES提供兩組運算子:相等和不相等——先轉換再比較,全等和不全等——僅比較不轉換,
相等和不相等
相等運算子由兩個等于號(==)表示,如果兩個運算元相等,回傳true,
不相等運算子由嘆號后跟等于號(!=)表示,如果兩個運算元不相等,回傳false,
這兩個運算子都會先轉換運算元(通常稱為強制轉型),然后比較它們的相等性,
在轉換不同資料型別時,遵循下列規則:
- 如果有一個運算元是布林值,則在比較相等性之前先將其轉換為數值,false轉換為0,true轉換為1;
- 如果一個運算元是字串,另一個運算元是數值,在比較相等性之前先將字串轉換為數值;
- 如果一個運算元是物件,另一個運算元不是,則呼叫valueOf()方法,用得到的基本型別值按照前面規則比較;
這兩個運算子在比較時遵循下列規則:
- null和undefined是相等的;
- 比較相等性之前,不能將null和undefined轉換成其他任何值;
- 如果有一個運算元是NaN,則相等運算子回傳false,而不相等運算子回傳true,注意:即使兩個運算元都是
- 如果兩個運算元都是物件,則比較它們是不是同一個物件,如果兩個運算元都指向同一個物件,則相等運算子回傳true;否則回傳false,
全等和不全等
全等運算子由3個等于號(===)表示,它只在兩個運算元未經轉換就相等的情況下回傳true,
不全等運算子由一個嘆號后跟兩個等于號(!==)表示,它在兩個運算元未經轉換就不相等的情況下回傳true,
注意:null == undefined回傳true,因為它們是類似的值;但null === undefined會回傳false,
條件運算子
條件運算子遵循與Java中的條件運算子相同的語法形式,
賦值運算子
簡單的賦值運算子由等于號(=)表示,其作用就是把右側的值賦給左側的變數,
如果在等于號(=)前面再添加乘性運算子、加性運算子或位運算子,就可以完成復合賦值操作,
每個主要算術運算子都有對應的復合賦值運算子,這些運算子如下:
- 乘/賦值(*=);
- 除/賦值(/=);
- 模/賦值(%=);
- 加/賦值(+=);
- 減/賦值(-=);
- 左移/賦值(<<=);
- 有符號右移/賦值(>>=);
- 無符號右移/賦值(>>>=),
逗號運算子
使用逗號運算子可以在一條陳述句中執行多個操作,
逗號運算子多用于宣告多個變數,還可以用于賦值,用于賦值時,逗號運算子總會回傳運算式中的最后一項,
陳述句
陳述句通常使用一或多個關鍵字來完成給定任務,
if陳述句
語法如下:
if(condition){
statement1
}else{
statement2
}
其中的condition(條件)可以是任意運算式,如果對condition求值的結果為true,則執行statement1(陳述句1),否則執行statement2(陳述句2),
do-while陳述句
do-while陳述句是一種后測驗回圈陳述句,即只有在回圈體中的代碼執行之后,才會測驗出口條件,換句話說,在對條件運算式求值之前,回圈體內的代碼至少會被執行一次,語法如下:
do{
statement
}while(expression);
while陳述句
while陳述句屬于前測驗回圈陳述句,也就是說,在回圈體內的代碼被執行之前,就會對出口條件求值,因此,回圈體內的代碼有可能永遠不會被執行,語法如下:
while(expression){
statement
}
for陳述句
for陳述句也是一種前測驗回圈陳述句,但它具有在執行回圈之前初始化變數和定義回圈后要執行的代碼能力,語法如下:
for(initialization;expression;post-loop-expression){
statement
}
在for回圈的變數初始化運算式中,也可以不使用var關鍵字,該變數的初始化可以在外部執行,
for陳述句中的初始化運算式、控制運算式和回圈后運算式都是可選的,如果將三個運算式全部省略,就會創建一個無限回圈,
for-in陳述句
for-in陳述句是一種精準的迭代陳述句,可以用來列舉物件的屬性,語法如下:
for(property in expression){
statement
}
如果表示要迭代的物件的變數值為null或undefined,for-in陳述句會拋出錯誤,ES5更正了這一行為,對這種情況不再拋出錯誤,而只是不執行回圈體,為了保證最大限度的兼容性,建議在使用for-in回圈之前,先檢測該物件的值不是null或undefined,
label陳述句
使用label陳述句可以在代碼中添加標簽,以便將來使用,語法如下:
label: statement
示例:
start:for(var i = 0; i < count; i++){
alert(i);
}
break和continue陳述句
break和continue陳述句用于在回圈中精確地控制代碼的執行,
break陳述句會立即退出回圈,強制繼續執行回圈后面的陳述句,continue陳述句雖然也是立即退出回圈,但退出回圈后會從回圈的頂部繼續執行,
with陳述句
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陳述句
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值,這種用法一般用在需要提前停止函式執行而又不需要回傳值的情況下使用,
嚴格模式對函式有一些限制,如下:
- 不能把函式命名為eval或arguments;
- 不能把引數命名為eval或arguments;
- 不能出現兩個命名引數同名的情況,
如發生以上情況,將導致語法錯誤,無法執行,
理解引數
在函式體內可以通過arguments物件來訪問這個引數陣列,從而獲取傳遞給函式的每一個引數,
ES函式一個重要特定:命名的引數只提供便利,但不是必需的,
沒有傳遞值的命名引數將自動被賦予undefined值,
ES中的所有引數傳遞的都是值,不可能通過參考傳遞引數,
沒有多載
ES函式不能像傳統意義上那樣實作多載,ES函式沒有簽名,因為其引數是由包含零或多個值的陣列來表示的,而沒有函式簽名,真正的多載是不可能做到的,
如果在ES中定義了兩個名字相同的函式,則該名字只屬于后定義的函式,
小結
下面簡要總結ES中的基本要素:
- ES中的基本資料型別包括Undefined、Null、Boolean、Numbe和String,
- ES沒有為整數和浮點數值分別定義不同的資料型別,Number型別可以用于表示所有數值,
- ES中有一種復雜的資料型別:Object型別,該型別是所有物件的基礎型別,
- 嚴格模式為這門語言中容易出錯的地方施加了限制,
- ES提供了很多基本運算子:算術運算子、布爾運算子、關系運算子、相等運算子及賦值運算子等等,
- ES中有很多流控制陳述句,如:if陳述句、for陳述句和switch陳述句等等,
- 無須指定函式的回傳值,因為ES函式都可以在任何時候回傳任何值,
- 未指定回傳值的函式回傳的是一個特殊的undefined值,
- ES中沒有函式簽名的概念,因為其函式引數是以一個包含零或多個值的陣列的形式傳遞的,
- 可以向ES函式傳遞任意數量的引數,并可以通過arguments物件來訪問這些引數,
- 由于不存在函式簽名,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,
執行環境及作用域
執行環境定義了變數或函式有權訪問的其他資料,決定了它們各自的行為,每個執行環境都有一個與之關聯的變數物件,環境中定義的所有變數和函式都保存在這個物件中,
每個函式都有自己的執行環境,當執行流進入一個函式時,函式的環境就會被推入一個環境堆疊中,而在函式執行之后,堆疊將其環境彈出,把控制權回傳給之前的執行環境,
當代碼在一個環境中執行時,會創建變數物件的一個作用域鏈,作用域鏈的用途是保證對執行環境有權訪問的所有變數和函式的有序訪問,
延長作用域鏈
雖然執行環境的型別總共只有兩種:全域和區域(函式),但還是有其他辦法延長作用域鏈的,因為有些陳述句可以在作用域鏈的前端臨時增加一個變數物件,該變數物件會在代碼執行后被移除,在兩種情況下會發生這種情況,具體的說就是當執行流進入下列任何一個陳述句時,作用域鏈就會得到加長:
- try-catch陳述句的catch塊;
- with陳述句,
這兩個陳述句都會在作用域鏈的前端添加一個變數物件,對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變數可以用來保存兩種型別的值:基本型別值和參考型別值,
基本型別值和參考型別值具有以下特點:
- 基本型別值在記憶體中占據固定大小的空間,因此被保存在堆疊記憶體中;
- 從一個變數向另一個變數復制基本型別的值,會創建這個值的一個副本;
- 參考型別的值是物件,保存在堆記憶體中;
- 包含參考型別值得變數實際上包含的并不是物件本身,而是一個指向該物件的指標;
- 從一個變數向另一個變數復制參考型別的值,復制的其實是指標,因此兩個變數最終都指向同一個物件;
- 確定一個值是哪種基本型別可以使用typeof運算子,而確定一個值是哪種參考型別可以使用instanceof運算子,
所有變數都存在于一個執行環境(也稱作用域)當中,這個執行環境決定了變數的生命周期,以及哪一部分代碼可以訪問其中的變數,
以下是關于執行環境的幾點總結:
- 執行環境有全域執行環境和函式執行環境之分;
- 每次進入一個新執行環境,都會創建一個用于搜索變數和函式的作用域鏈;
- 函式的區域環境不僅有權訪問函式作用域中的變數,而且還有權訪問其包含(父)環境,乃至全域環境;
- 全域環境只能訪問在全域環境中定義的變數和函式,而不能直接訪問區域環境中的任何資料;
- 變數的執行環境有助于確定應該何時釋放記憶體,
JavaScript是一門具有自動垃圾收集機制的編程語言,開發人員不必關心記憶體分配和回收問題,可以對JavaScript的垃圾收集例程做如下總結:
- 離開作用域的值將被自動標記為可以回收,因此將在垃圾收集期間被洗掉;
- “標記清除”是目前主流的垃圾收集演算法,這種演算法思想是給當前不使用的值加上標記,然后再回收其記憶體;
- 另一個垃圾收集演算法是“參考計數”,這種演算法思想是跟蹤記錄所有值被參考的次數,JavaScript引擎目前都不再使用這種演算法;
- 當代碼中存在回圈參考現象時,“參考計數”演算法就會導致問題;
- 解除變數的參考不僅有助于消除回圈參考現象,而且對垃圾收集也有好處,為了確保有效地回收記憶體,應該及時解除不再使用的全域物件、全域物件屬性以及回圈參考變數的參考,
參考型別
Object型別
創建Object實體的兩種方式:
- 使用new運算子后跟Object建構式,例如:
var person = new Object();
person.name = "ZWW";
person.age = 22;
- 使用物件字面量表示法,例如:
var person = {
name: "ZWW",
age: 22
};
在物件字面量中,使用逗號來分隔不同的屬性,并且在最后一個屬性后面不能添加逗號,
在使用物件字面量時,屬性名也可以使用字串;如果留空其花括號,則可以定義只包含默認屬性和方法的物件,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/164489.html
標籤:JavaScript
上一篇:獲取頁面元素
