電腦程式的運作需要對值(value)例如數字3.14或文字"hello world"進行操作,在程式語言中,能夠表示並操作的值的型別叫做資料型別(type),程式語言最基本的特性就是主持多種資料型別。當程式需要將值保持起來以備將來使用時,便將其賦值給(將值「保存」到)一個變數(variable)。變數是一個值的符號名稱,可以透過名稱獲得對值的參考。變數的工作機制是程式語言的令一個基本特性。本章將參考上節幫助理解本章內容,後續將更深入的講解。
javascript的資料分為兩類:原始類別(primitive type)和物件類型(object type)
javascript中的原始類別包括數字,字串,布林值,本章會有單獨的章節專門講述javascript的數字、字串、布林值。 javascript還有兩個特殊的原始值,null(空)和Undefined(未定義),他們不是數字、字串、布林值。它們分別代表了各自特殊類型的唯一成員。
javascript除了數字、字串、布林值、null、undefined之外就是物件了。物件 (object)是屬性(property)的集合。每個屬性都由"名/值對"(值可以是原始值,例如數字,字串,也可以是物件)構成。其中一個比較特殊的物件(全域物件(global object)會在第五小姐介紹,第六小節將更詳細的描述)
普通的javascript物件是「命名值」的無需集合。 javascript同樣定義了一種特殊物件--陣列(array),表示帶編號的值的有序集合。 javascript為陣列定義了專用的語法。使陣列擁有一些和普通物件不同的特有的行為屬性。
javascript也定義了一種特殊的物件--函數。函數是具有與它想關聯的可執行程式碼的對象,透過呼叫函數來執行科執行程式碼,並返還運算結果。和陣列一樣,函數行為特徵和其它物件都不一樣。 javascript為使用函數定義了專用語法。對javascript函數來講。最重要的是,他們都是真值,並且javascript可以講他們當普通物件來對待。
如果函數初始化(使用new運算子)一個新建對象,我們稱之為建構函式(constructor)。每個建構函式定義了一類(class)物件--建構函式初始化物件組成的集合。類別可以看做物件類型的子類型。除了陣列(array)類別和函數(function)類別之外,javascript還定義了其它三種由用的類別。日期(date)定義了代表日期的物件。正規(regExp)定義了正規表示式的物件。錯誤(error)類別定義了那行表示javascript程式中運行時錯誤和語法錯誤物件。可以透過定義自己的建構函數來定義需要的類別。
javascript解釋器有自己的記憶體管理機制,可以自動對記憶體進行垃圾回收(garbagecollection)。這意味著程式程式可以按需建立對象,程式設計師則不必擔心這些對象的銷毀了記憶體回收。當不再有任何一個引用指向一個對象,解釋器就知道這個對象沒有用了,然後會自動回收它所佔用的記憶體資源。
javascript是一種物件導向的語言。不嚴格的講,這意味著我們不用全域的定義函數去操作不同類型的值,資料型別本身可以定義方法(method)來使用值,例如要對數組a中的元素進行排序,不必要將a傳入sort()函數,而是調運a的一個方法sort()
a.sort(); //sort(a)物件導向的版本
從技術上來將,只有javascript物件才能擁有方法。然而,數字,字串,布林值也擁有自己的方法。在javascript中,只有null和undefined是無法擁有方法的值。
javascript的類型可以分為原始類型和物件類型,可分為可以擁有方法的類型和不能擁有方法的類型。同樣可分為可變(mutable)和不可變(immutable)型。可變類型的值是可以修改的,物件和陣列屬於可變類型:javascript程式可以改變物件的屬性值和陣列元素的值。
數字、布林值、null和undefined屬於不可改變的型別。比如,修改一個陣列的內容本身就說不通。字串可以看做是字元組成的數組,你可以認為它是可以變的。然而在javascript中,字串是不可變的。可以存取字串任意位置的文本,但javascript並未提供修改一直字串文本內容的方法。
javascript可以自由地進行資料型別轉換。例如,如果在程式期望使用字串的地方使用了數字,javascript會自動將數字轉換為字串。如果期望在使用布林值的地方使用了非布林值,javascript也會對應的轉換。 javascript中對靈活的型別抓換規則對「判斷相等」(equality)
javascript的變數是無類型的(untyped),變數可以被賦予人和類型的值,使用var關鍵字來宣告(declare)變數。 javascript採用語法作用域,不在任何函數內宣告的變數稱為全域變數(global variable),它在javascript的程式 中任何地方都是可見的。
1.數字
和其它程式語言不同,javascript不區分整數數值和浮點數數值。 javascript中的數值皆以浮點數數值表示。當一個數字直接出現在javascript程式中,我們陳之為數字直接量(numeric literal),javascript支援多種格式的數字直接量。 (注意:在任何數字前直接加上負號(-)可以得到它們的負值)但負號是一元求反運算子。 ,並不是數字直接量語法的組成部分。 )
i整數型直接量
javascript中用一個陣列序列表示一個十進制的整數
除了十進制的整數直接量,javascript同樣辨識十六機制(16)為基數的的值。所謂十六進位是以「0X」或"0x"為前綴,其後緊跟著十六進制數串的直接量。十六進制數值是0-9的數字和a(A)-f(F)之間的字母構成。 a-f的字母對於的表述數字10-15下面是十六進位整數直接量的例子
儘管ECMAScript不支援八進位直接量,但javascript的某些實作可以允許採用八進位(基數為8)形式表示整數。八進位直接量以數字0開始,其後接著隨著一個0-7之間數字組成的序列。
由於某些javascript的實現支持八進制的之間量,而有些不支持,因此最好不要使用以0為前綴的整數之間量,畢竟我們也無法得知當前javascript的實現是否支持八進制的解析。在ECMAScript6的嚴格模式下,八進制的直接量是明令禁止的。
ii.浮點型直接量
浮點型直接量可以含有小數點,它們採用的是傳統的實數寫法。一個實數由整數部分,小數點和小數部分組成。
此外,也可以使用指數計數法表示浮點型直接量。即在實數後面接字母E或e,後面再跟正負號,其後再加一個整數的指數。這種計數方法表示的數值,是有前面的實數乘以10的指數冪。
可以使用更簡潔的語法來表示
iii.javascript中的算術運算
javascript程式是使用語言本省提供的算術運算子來進行數字運算的。這些運算符包含 - * /和求餘(整除後的餘數)運算符%
除了基本的運算子之外,javascript還支援更複雜的算術運算,這個線複雜的運算透過作為Math物件的屬性定義的函數和常數來實現。
javascript中的算術運算在溢出(overflow)、下溢(underflow)或被零整除時不會報錯。但數字運算結果超過了javascript所能表示的數字上線(溢出),結果為一個特殊的無限大的值(infinty)值,在javascript中以infinty表示。同樣地,當負數的值超過了javascript所能表達的負數範圍,結果為負無窮大,在javascript中以-Infinty表示。無窮大值的行為特性和我們所期望的是一致的:基於它們的加減乘除運算結果是無限大(保留正負號)
下溢(underflow)是當運算結果無線接近零並比 javascript能表示的最小值還小的時候發生的一種情況。當一個負數發生下溢時,javascript返回一個特殊的值,“負零”,這個(負零)幾乎和正常的零完全一樣。 javascript程式設計師很少用到負零。
javascript預先定義了全域變數Infinaty和NaN,用來表示正無窮大河非數字值,在ECMAScipt3中,這兩個值是可以讀寫的。 ECMAScript5修正了這個問題,將他們定義為唯讀的。 ECMAScipt3中的Number物件定義的屬性值也是唯讀的,這裡有一些例子:
javascript中的非數字值有一點特殊,它和人和值都不相等,包括自身。也就是說沒辦法透過x==NaN來判斷x是否為NaN。相反,應使用x! =x來判斷,當且僅當x為NaN的時候,表達式的結果為true.函數isNaN()作用與此相似,如果參數是NaN或者是一個非數字值(例如字串和物件),則傳回true。 javascript中有一個類似的函數isFinite(),在參數不是NaN、Infinty或-Infinity的時候回傳true.
負零值同樣有些特殊,它和正負零是相等的(甚至使用javascript的嚴格相等測試來判斷)、這意味著這兩個值幾乎是一模一樣的,除了作為除數之外:
iiii.二進位浮點數與四捨五入錯誤
實數有無數個,但javascript透過浮點數的形式只能表示有限的個數(確切的說有18 437 736 874 454 810 627個),也就是說,當javascript使用實數的時候,常常只是真實值的一個近似的表示。
javascript採用了IEEE-754浮點數表示法(幾乎所有的現代程式語言採用)。這是一種二進位表示法,可以精確的表示分數,例如1/2 1/8 和1/1024,遺憾的是,我們常採用的分數,特別是金融計算方面,都是以十進制分數1/10 ,1/100等。二進位表示法並不能表示類似0.1這樣簡單的數字。
javascript中的數字具有足夠的精確度。並且可以接近0.1.但事實上,數字不能精確表述帶來了一些問題。
由於舍入誤差,0.3和0.2之間的近似差值實際上並不等於0.2和0.1之間的近似差值(在真實模擬環境中,0.3-0.2=0.099 999 999 999 999 98).這個問題不只在javascript中存在,理解這一點十分重要:在任何使用二進制浮點數的程式語言中都會有這個問題。同樣需要注意的是,上述程式碼中x和y的值非常接近彼此和最終的正確值。這種計算結果可以勝任大多數的計算任務。這個問題也是只有比較兩個值是否相等的時候才會出現。
javascript的未來版或許支援十進制數字類型以避免這個問題,在這之前你可能更願意使用大整數進行重要的金融計算。例如,要使用整數“分”,而不要使用小數“元”進行基於貨幣單位的運算。
iiiii.日期和時間
javascript語言核心包含Date()建構函數,原來創建日期和時間的對象,這些日期對象的方法為日期計算提供了簡單的API,日期對像不能像數字那樣是基本資料類型。
2.文字
字串(string)是一組16位元值組成的不可變的有序序列,每個字元通常來自於Unicode字元集。 javascript透過字串類型來表示文字。字串的長度(length)是其所含的16位元值的個數。 javascript字串(和其陣列)的索引從0開始。空字串的(empty string)長度為0,javascript中並沒有表示單一字元的「字元型」。要表示一個16位元值,只需要將其賦值給字串變數即可。這個字串的長度為1。
字元集,內碼和javascript字串
javascript採用UTF-16編碼的Unicode字元集,javascript字串是由一組無序號的16位元值組成的序列。最常用的Unicode字符都是透過16位的內碼表示,並代表字串中的單個字符,那行不能表示為16位的Unicode字符則遵循UTF-16編碼規則----用兩個16位數值組成一個序列(也稱為「代理項對」)表示。這意味著一個長度為2的javascript字串(兩個16位元值)可能表示一個Unicode字元。
javascript定義的各式字串操作方法均作用於16位值,而非字符,且不會對代理項對做單獨處理。同樣javascript不會對字串做標準化的加工。甚至不能保證字串是合法的UTF-16格式
i字串直接量
在javascript程式中的字串直接量,是由單引號或雙引號括起來的字元序列,由單引號定界的字串中可以包含雙引號,由雙引號定界的字串中也可以包含單引號。這裡有幾個字串直接量的例子。
ECMAScript3中,字串直接量必須寫在一行中,而在ECMAScript5中,字串的直接量可以拆分為數行,每行必須以反斜線()結束,反斜線和行結束符都不是字串直接量的內容。如果希望在一起,則可以使用 轉義字元。
要注意的是,當使用單引號定界字串時,需要格外小心英文中的縮寫和所有格式寫法,英文撇號和單引號是同一個字符,所以必須使用反斜線()來轉義。
ii轉義字元
在javascript字串中,反斜線()有著特殊的用途,反斜線後加一個字符,就不再表示他們的字面含義了,比如n 就是一個轉義字符,它表示一個換行符。
iii字串的使用
javascript的內建功能之一就是字串連接。將運算子 用於字串,表示字串連接。例如
要確定一個字串的長度-其所包含的16位元值的個數,可以使用length屬性,例如字串s的長度。
s.length
除了length屬性,字串還提供很多可以呼叫的方法。
在javascript中,字串是固定不變的,類似replace()和toUpperCase()方法都回傳了新的字串,原來的字元本身沒有改變。
在ECMAScript中,字元可以當做只讀數群組,除了使用charAt()方法,也可以使用方括弧來存取字串中的單一字元。 (16位元值)
Foxfire很久之前就支援這樣方法的字串索引,多數現代瀏覽器(IE除外)也緊跟Mozailla的腳步,在ECMAScript成型之前就完成了這一特性
iiii模式匹配
javascript定義了RegExp()建構函數,用來建立表示文字模式匹配的對象,這些模式稱為「正規表示式」(regular expression),javascript彩陽Perl中的正規表示語法。 String和RegExp物件均定義了利用正規表示式進行模式比對和尋找與取代的函數。
RegExp物件並不是語言中的基本資料類型,和Date一樣,它只是一種具有實用API的特殊物件。正規表示式的語法很複雜,API也很豐富。在第10章節會詳細介紹。 RegExp是一種強大且常用的文字處理工具,這裡只是一個概述。
儘管RegExp並不是語言中的基本資料類型,但是他們依然具有直接量的寫法,可以直接在javascript中使用。在兩條斜線之間的文字構成了一個正規表示式的直接量。第二條斜線之後也可以跟著一個或多個字母。用來修飾匹配模式的意思。例如:
RegExp物件定義了很多有用的方法,字串同樣具有可以接受RegExp參數的方法。例如:
3.布林值
布林值指真或假,開或關,這個型別只有兩個值,保留字true或false
javascript中的比較語句的結果通常都是布林值。例如
a==4
這段程式碼用來偵測變數的a的值是否等於4.如果等於,則值為true,如果不等值為false
布林值通常用於javascript的控制語句中,例如javascript中的if/else語句,如果布林值為true執行第一段邏輯,如果為false執行另一段程式碼,例如
任一javascript的值都可以轉換成布林值,以下這些值都轉換成false
所有其它值,包括所有物件(數組)都會被轉換為true,false和上面6個可以轉換為false的值有時稱為“假值”,javascript期望使用一個布林值時,假值會被當成false,真值會被當成true
來看一個例子,加上變數o是一個物件或是null,可以透過一條if語句來偵測o是否是非null值。
if(o!==null)...
不等操作符「!==」將o和null比較,並得出結果為 true或false。可以先忽略這裡的比較語句,null是一個假值,物件是一個真值。
if(o)...
對於第一種情況,只要當o不是null時才會執行if後的程式碼,第二種情況的限制就沒有那麼嚴格。只有o不是false或任何假值(例如null或unfined)時才執行這個if。
布林值包含toString()方法,因此可以使用這個方法將字串轉換為「true」或"false",但它不包含其他有用的方法,除了這個不重要的API,還有三個重要的布林值運算符。
&&運算符,||運算子和一元操作符「!」執行了布林非(NOT)操作,如果真值回傳false,假值回傳true,例如
4.null和undefined
null是javascript語言的關鍵字,它表示一個特殊值“空值”,對於null執行typeof()運算,返回object.也就是說,可以將null認為是一個特殊的對象值,含義是"非對象"。但實際上,通常認為null是它自由類型的唯一一個成員。它可以表示數字,字串,和物件是「無值」的。大多數程式語言和javascript一樣含有null,你可以對null或nil很熟。
javascript還有第二個值表示值的空缺。用來表示更深層的「空值」。它是一種變數的一種取值。表示變數的沒有初始化。如果要查詢物件屬性或陣列元素的值是傳回undefined則表示這個屬性或元素不存在。 undefined是預先定義的全域變數(它和null不一樣,它不是關鍵字),它的值就是未定義。如果使用typeof來測試undefined類型,則傳回“undefined”,表示這個值是這個類型的唯一成員。
儘管null和undefined是不同的,但它們都表示“值的空缺”,兩者往往可以互換。判斷相等的運算子「==」認為兩者是相等的(要使用嚴格相等運算子"==="來區分它們)。在希望值是布林類型的地方它們的值都是假值。和false類似。 null和undefined都是不包含任何屬性和方法。實際上,使用"."和"[]"來存取這兩個值的成員或方法,都會產生一個類型錯誤。
你或許認為undefined是表示系統級的,出乎意料的活類似錯誤的值的空缺,而null是表示程序級的,正常或在意料之中的值的空缺,如果你想將它們複製變數或屬性,或將它們作為參數傳入函數,null是最佳的選擇。
5.全域物件
前幾節討論了javascript的元素類型和原始值。物件類型-物件、陣列和函數/但有一類非常重要的對象,不得現在就必須將清楚:全域物件
全域物件(global object)在javascript中有著重要的用途。全域物件的屬性是全域定義的符號。 javascript程式可以直接使用。當javascript解釋器啟動時,它將新建一個新的全域對象,並給它一組定義的初始屬性。
全域屬性 例如undefined Infinty和NaN
全域函數 例如isNaN()、parseInt()和eval()
建構函數,如Date()、RegExp()、String()、Object()和Array()
全域對象,如Math 和JSON
全域物件的初始屬性並不是保留字,但他們應該做保留字來對待。
在程式碼的最頂層-不在任何函數內的javascript程式碼,可以透過javascript關鍵字來引用全域物件。
var global = this; //定義一個引用全域物件的全域變數。
在客戶端javascript中,window物件扮演了全域對象,這個全域window物件有一個熟悉window引用其本身,它可以取代this來引用全域對象,window定義了全域核心屬性。但也徵對web瀏覽器和和互動javascript定義了一部分其他全域屬性。
當初次建立時,全域物件定義了javascript中所有的預定義全域值,這個特殊物件同樣包含了為程式定義的全域值。如果程式碼聲明了一個全域變數。這個全域變數就是全域物件的一個屬性。
6.包裝對象
javascript物件是一種複合值:它是屬性或已命名值的集合。透過"."來引用屬性值,當屬性值是函數的時候,陳其為方法,透過o.m()來調運物件o中的方法。
我們看到字串也同樣具有屬性和方法。
字串既然不是對象,為什麼它有屬性呢?只要引用了字串s的屬性,javascript就會將字串的值透過呼叫new String(s)的方式轉換成對象,這個物件繼承了字串的方法。並被用來處理屬性引用。一旦新的屬性引用出來。一但引用結束,這個新建立的物件就會被銷毀。 (實際上並不一定創建或銷毀這個臨時對象,然而這個過程看起來是這樣的。)
如同字串一樣,數字和布林值也具有各自的方法,透過Number()和Boolean()建構子建立一個臨時物件。這些方法的呼叫均是來自於這個臨時物件。 (null和undefined沒有包裝過對象,存取他們的屬性會有一個類型錯誤)
看如下 程式碼,思考他們的執行過程
當運行這段程式碼時,t的值是undefined,第二行程式碼建立一個臨時字串對象,並給len的值為4,隨即銷毀這個對象,第三行用過原始(沒有被修改的)的字串建立一個新的字串對象,並嘗試讀取len 的屬性。
這個屬性自然不存在,表示結果undefined,這段程式碼說明了讀取字串、陣列和布林值的屬性值(或方法)時,表現的像物件一樣,但如果你試圖給其屬性賦值。則會忽略這個操作;修改只是發生在臨時物件身上。而這個臨時物件並未保留下來。
要注意的是,可以透過String(),Number(),Boolean()建構子來顯示創造包裝物件:
javascript會在必要的時候將包裝轉換為原始值,因此上段程式碼中的物件S N B常常——但不總是——表現的值和s n b一樣,"=="等於運算子將原始值和其包裝物件視為相等。
但"==="全籌運算子將它們視為不等,透過typeof運算子可以看到原始值和其包裝的物件之間的不同。
7.不可變的原始值和可變的物件參考。
javascript的原始值(undefined null 布林值 數字和字串)與物件(包括陣列和函數)有著根本的區別,原始值是不可更改的;任何方法都無法(或突變)一個原始值。對數字和布林值來說顯然如此———改變數字的值本身就說不通,而對字串來說就不那麼明顯,因為字串看起來由字元組成的陣列。我們期望可以透過指定的索引來修改字串中的字元。實際上javascript是禁止這樣做的。字串中所有的方法看上去回傳了一個修改過的字串,實際上是回傳一個新的字串。
原始值的比較是值的比較,只有在他們的值相當時它們在才相等。這對數字、布林值、null和undefined來說聽起來有點難,並沒有其他方法可以比較他們。同樣,對於字串來說則不那麼明顯;如果比較兩個單獨的字串,當且僅當他們的長度相等且每個索引的字元都相等時,javascript的才認為相等。
物件的比較並非值的比較:即使兩個物件包含相同的屬性及相同的值,他們也是不相等的,各個索引元素完全相等的兩個數組也不相等
我們通常將物件稱為引用型別(reference type),以此來和javascript的基本型別區分開來。依照術語的叫法,物件都是引用(reference),物件的比較都是引用的比較;當且當它們應用同一個基底物件時,它們才相等。
就像你剛才看到的如上程式碼,將物件(或數組)賦值給一個變量,僅僅是賦值的引用值:物件本身並沒有複製一次。
如果你想要得到一個物件或陣列的副本,則必須明確複製物件的每個屬性或陣列的每個元素。下面的這個例子則是透過循環來完成對數組的複製。
同樣的,如果我們想比較兩個單獨或數組,則必須比較他們的屬性或元素。下面這段程式碼定義了一個比較練個陣列的函數。
javascript中的取值型非常靈活,我們已經從布林值看到了這一點:當javascript期望使用一個布林值時候,你可以提供任意型別值。 javascript將根據需要自行轉換類型。一些值(真值)為true,其它值(假值)轉換為false。這在其它類型中同樣適用。如果javascript期望使用字串,它就會把給定的值轉換成字串。如果javascript期望使用一個數組,它把給定的值轉換為數字(如果轉化結果無意義的話將返回NaN),一些例子如下:
下表說明了在javascript中如何進行型別轉換。粗體突顯了那些讓你倍感意外的類型轉換。空白單元格表示不必要也沒有執行的轉換。
值 | 轉換為字串 | 數字 | 布林值 | 對象 |
undefined null |
"undefined" "null" |
NaN 0 |
false false |
throws TypeError throws TypeError |
true false |
"ture" "false" |
1 0 |
new Boolean(true) new Boolean(false) |
|
""(空字符串) "1.2"(非空,数字) "one"(非空,非数字) |
0 1.2 NaN |
false true true |
new String("") new String("1.2") new String("one") |
|
0 -0 NaN Infinty -Infinty 1(無窮大,非零) |
"0" "0" "NaN" "Infinity" "-Infinity" "1" |
false false false true true true |
new Number(0); new Number(-0); new Number(NaN) new Number(Infinty) new Number(-Infinty) new Number(1) |
|
{}(任意物件) [](任意數組) [9](1個數字元素) ['a'](其它數組) function(){}(任意の関数) |
このセクションの 3 番目のセクションを参照 "" 「9」 join() メソッドを使用します このセクションの 3 番目のセクションを参照してください |
このセクションの 3 番目のセクションを参照 0 9 なな なな |
本当 本当 本当 本当 本当 |
上記の表で説明されている元の値から元の値への変換は、この記事の 3 番目のセクションですでに説明しました。すべてのプリミティブ値の文字列への変換も明確に定義されています。数値への変換はさらに微妙です。数値として表現された文字列は、先頭と末尾にスペースを含めることができ、数値に直接変換できます。ただし、先頭と末尾の null 以外の文字は数値の一部とみなされず、文字列の結果が NaN になります。奇妙に見える数値変換がいくつかあります。 true は 1、false に変換され、空の文字列 "" は 0 に変換されます。
プリミティブ値のオブジェクトへの変換も非常に単純で、プリミティブ値です