JavaScript基礎入門教程(三)
說明
前面的兩篇博客介紹了js中的基本知識中的變量類型、標識符等。這篇博客主要談表達式以及運算符。
原始表達式
原始表達式就是表達式中最小的,不能在分割的表達式,一般指變量、常數直接量、關鍵字(true、false、null、this),這裏需要註意的是undefined是一個全局變量,而不是關鍵字。
對象和數組的初始化表達式
數組初始化表達式是一個方括號,中間放置數組中的元素,元素間用逗號分割。元素可以是表達式、數組、空。
1 [] //一個空數組,數組裏面沒有元素 2 [1+2,3+4] //一個擁有兩個元素的數組,第一個是3,第二個是7。 3 [[1,2,3],[4,5,6],[7,8,9,]] //數組中的元素還可以是數組。 4 [1,,,,5] //數組中的元素可以為空,這時空位會填充值undefined。
在執行var a=[1,,,,5]這條語句後,在chrome中表現為下圖所示:
對象初始化表達式和數組初始化表達式非常類似。
1 var stu={name:"張三", age:15}; //定義一個擁有兩個屬性的對象。
上面這行初始化對象的表達式類似於下面的數組,事實上js也確實支持這種寫法訪問對象中的屬性。
1 stu["name"]="張三"; 2 stu["age"]=15;
如果你學過java那麽上面那句var stu={name:"張三", age:15};可以很容易的寫成下面的java代碼:
1 class Student{ 2 String name; 3 int age; 4 5 Student(String name, int age){ 6 this.name=name; 7 this.age=age; 8 } 9 } 10 11 Student stu=new Student("張三", 15);
不過js可以完美支持多層的內部類,而java卻對內部類有好多限制。
屬性訪問表達式
在js中,屬性訪問有①expression.identifier、②expression[expression]這兩種方式。其中第一種雖然方便卻只能用於對象,而且要求訪問的屬性(identifier)不能是關鍵字、不能含有空格、不能是數字。而第二種訪問方式不僅支持對象還能用於數組,並且用於對象訪問自己的屬性時沒有第一種那麽多的限制。
1 var o={x:1, y:{z:3}}; 2 var a=[o, 4, [5, 6]]; 3 o.x //1:訪問o的x屬性 4 o.y.z //3:訪問o的y的z屬性 5 o["x"] //1:訪問o的x屬性 6 a[1] //4:訪問a的索引為1的元素 7 a[2]["1"] //6:訪問a[2]中的索引為1的元素 8 a[0].x //1:通過訪問a[0]得到o,再訪問o的x屬性
運算符
js所支持的運算符如上圖所示,學過其它編程的同學基本上看上一眼就知道了。這裏只對需要註意的部分做出說明。在基本算術運算符(加減乘除和求余)只有加法是相對有難度的,因為有時候它是加法,而有時候它又是字符串連接符,具體用法可概括為只有+運算符兩邊都是數字才是加法,更詳細的用法請參考這篇博客。此外還需要談的就是求余運算符,其運算結果的符號和左操作數相同,即5%1=1、-5%1=-1。
在js的位運算時,會有一個隱式轉換過程。默認情況下js中的數字都是存儲為64位浮點數格式,在位運算的時候會轉換為32位整數,所以隱式轉換會截斷小數部分和整數中超過32位能表示的部分。此外NaN、infinity和-infinity會轉換為0。
關於等於(==)運算符和嚴格等於(===)運算符請參考本系列教程的第一篇。
in運算符
in運算符希望它的左操作數是一個字符串或者能轉換為字符串,希望它的右操作數是一個對象。如果右側對象擁有一個名為左操作數的屬性則整個in表達式返回true。
1 var point={x:1, y:1}; 2 "x" in point //true:對象中有一個名為x的屬性 3 "z" in point //false:對象中不存在一個名為z的屬性 4 "toString" in point //true:對象繼承了toString()方法 5 6 var data=[7, 8, 9]; 7 "0" in data //true:數字包含下標為0的元素 8 1 in data //true:數字轉換為字符串 9 3 in data //false:沒有索引為3的元素
&&與||
學過C和java等語言的同學應該知道這兩個運算符有一個特性就是短路,這個特性在js中也同樣存在,習慣性利用這種特性為函數提供默認值。
1 function copy(o, p){ 2 o.p = p || {}; 3 }
上面的代碼中,如果copy函數被調用的時候p形參有值,則將p復制給o.p,否則將默認值{}復制給o.p。
表達式計算
在js中存在一個函數叫eval(),雖然這是一個函數,但一般被當成運算符來對待。一般來說函數是可以起別名的,比如var f=eval;再通過f調用eval函數。但是一般不推薦對eval函數起別名,ECMAScript3標準甚至直接禁止對eval函數起別名,所以說它幾乎被當成運算符對待。之所以這樣是因為這涉及到代碼優化的問題,如果允許對eval函數起別名將嚴重影響代碼的優化過程。
上面談了eval的特點,現在來說一下它的作用。這個函數只有一個參數,且一般是字符串。如果傳入的參數是字符串,js解釋器會把這個字符串當成js代碼來執行,且其執行環境為直接調用這個eval函數的上下文(下面將詳細解釋),如果其傳入的參數不是字符串,則eval函數直接返回這個參數值。
1 var global=1; 2 var foo = function(ele){ 3 var local = 2; 4 eval(ele); 5 console.log(where); 6 } 7 foo("console.log(global);console.log(local);var where=‘I am here!‘;"); 8 console.log(where);
上面這行代碼的執行結果(在chrome中)如下圖所示:
根據上面所說的eval的執行環境為直接調用eval的上下文,在上圖顯示的結果中為第8行所處的上下文,這樣的話eval自然能訪問global和local變量,但是其定義的where變量只能讓foo函數內部且在第8行以後的代碼訪問,這樣以來第9行代碼能正確執行,而第12行代碼拋出異常。
前面談過ECMAScript3禁止對eval起別名,但是現實中大多數解釋器的實現並非如此,之後的ECMAScript5標準也允許別名調用,但規範了其行為。當eval被別名調用時,其運行環境永遠為全局環境,與其調用者環境沒有關系,也就是說間接調用(別名調用)無法讀、寫、定義局部變量。
1 var geval = eval; 2 var x = "global", y = "global"; 3 function f(){ 4 var x="local"; 5 eval("x += ‘ changed‘;"); 6 return x; 7 } 8 function g(){ 9 var y="local"; 10 geval("y += ‘ changed‘;"); 11 return y; 12 } 13 console.log(f(), x); 14 console.log(g(), y);
上面代碼執行的結果(在chrome中)為:
JavaScript基礎入門教程(三)