3 基本概念

本章內容

  • 語法
  • 數據類型
  • 流控制語句
  • 理解函數

3.1 語法

3.1.1 區分大小寫

區分大小寫

3.1.2 標識符

  • 第一個字符必須是一個字母、下劃線(_)或1一個美元符號($);
  • 其他字符可以是字母、下劃線、美元符號或數字。
    按照慣例,ECMAScript 標識符采用駝峰大小寫格式。

3.1.3 注釋

// 單行注釋
/*
    這是一個多行(塊級)注釋
*/

3.1.4 嚴格模式

"use strict";
function doSomething () {
  "use strict";
  //函數體
}

3.1.5 語句

ECMAScript中的語句以一個分號結尾。

var sum = a + b
var diff = a - b;

雖然語句結尾的分號不是必需的,但我們建議任何時候都不要省略它。

最佳實踐是始終在控制語句中使用代碼塊

if (test) {
  alert(test);
}

3.2 關鍵字和保留字

3.3 變量

var message;
var message = "hi";
var message = "hi";
message = 100;    //有效,但不推薦
function test () {
  var message = "hi"; // 局部變量
}
test();
alert(message); // 錯誤!

3.4 數據類型

ECMAScript 中有5種基本數據類型:Undefined、NullBoolean、NumberString。還有一種復雜數據類型——Object。

3.4.1 typeof 操作符

對于一個值使用typeof操作符可能返回下列某個字符串:

  • "undefined"——如果這個值未定義
  • "boolean"——如果這個值是布爾值
  • "string"——如果這個值是字符串
  • "number"——如果這個值是數值
  • “object”——如果這個值是對象或null
  • "function"——如果這個值是函數
var message = "some string";
alert(typeof message);  // "string"
alert(typeof(message));  // "string"
alert(typeof 95);  // "number"

注意,typeof是一個操作符而不是函數,因此例子中的圓括號不是必需的。

3.4.2 Undefined 類型

Undefined類型只有一個值,即特殊的 undefined

var message;
alert(message == undefined);  //true
var message;

// 下面這個變量未聲明
// var age

alert(message);  // "undefined"
alert(age);  // 產生錯誤
var message

// var age

alert(typeof message);  // "undefined"
alert(typeof age);  // "undefined"

3.4.3 Null 類型

只有一個值null。

var car = null;
alert(typeof car);  // "object"

如果定義的變量準備在將來用于保存對象,那么最好將該變量初始化為null而不是其他值。

if (car != null) {
  // 對 car 對象執行某些操作
}

實際上,undefined值是派生自null值的。

alert(null == undefined);  //true

沒有必要把一個變量的值顯式地設置為undefined。
只要意在保存對象的變量還沒有真正保持隊形,就應該明確保存null值。

3.4.4 Boolean 類型

var found = true;
var lost = false;

TrueFalse都不是Boolean值,只是標識符。

ECMAScript中所有類型的值都有與這兩個Boolean值等價的值,可以調用轉型函數Boolean()。

var message = "Hello world!";
var messageAsBoolean = Boolean(message);
數據類型 轉換為true的值 轉換為false的值
Boolean true false
String 任何非空字符串 空字符串
Number 任何非零數字值 0和NaN
Object 任何對象 null
Undefined n/a (不適用) undefined
var message = "Hello world";
if (message) {
  alert("Value is true");
}

3.4.5 Number 類型

var intNum = 55;

八進制字面值的第一位必須是0。八進制字面量在嚴格模式下是無效的。

var octalNum1 = 070;  //八進制的56
var octalNum2 = 079;
var octalNum3 = 08;  //無效的八進制數值——解析為8

十六進制字面值的前兩位必須是 0x。

var hexNum1 = 0xA;  //十六進制的10
var hexNum2 = 0xlf;  //十六進制的31

在進行算術計算時,所有以八進制和十六進制表示的數值最終都將被轉換成十進制數值。

1.浮點數值

var floatNum1 = 1.1;
var floatNum2 = 0.1;
var floatNum3 = .1;  //有效,但不推薦
var floatNum1 = 1.;  //小數點后面沒有數字——解析為1
var floatNum2 = 10.0;  //整數——解析為10
var floatNum = 3.125e7;  //等于31250000

在默認情況下,ECMAScript會將那些小數點后面帶有6個零以上的浮點數值轉換為以e表示法表示的數值(0.0000003會被轉換成3e-7)。
浮點數值的最高精度是17位小數,但在進行算術計算時其精確度遠遠不如整數。例如,0.1加0.2結果不是0.3,而是0.30000000000000004。

2.數值范圍

計算結果得到一個超出JavaScript數值范圍的值,那么這個數值將被自動轉換成特殊的Infinity值。負數會被轉換成-Infinity,正數會被轉換成Infinity。

var result =   Number.MAX_VALUE + Number.MAX_VALUE;
alert(isFinite(result));  //false

3.NaN

這個數值用于表示一個本來要返回數值的操作數未返回數值的情況(這樣就不會拋出錯誤了)。在ECMAScript中,任何數值除以0會返回NaN,不會影響其他代碼的執行。
任何涉及NaN的操作都會返回NaN,這個特點在多步計算中有可能導致問題。
NaN與任何值都不想等,包括NaN本身。

alert(NaN == NaN);  //false
alert(isNaN(NaN));  //true
alert(isNaN(10));  //false
alert(isNaN("10"));  //false
alert(isNaN("blue"));  //true(不能轉換成數值)
alert(isNaN(true));  //false(可以被轉換成數值1)

4.數值轉換

有3個函數可以把非數值轉換為數值:Number()、parseInt()parseFloat()
Number()函數的轉換規則如下。

  • 如果是Boolean值,truefalse將被轉換為10。
  • 如果是數字值,只是簡單的傳入和返回。
  • 如果是null值,返回0。
  • 如果是undefined,返回NaN
  • 如果是字符串,遵循下列規則:
    如果是字符串中只包含數字,則將其轉換為十進制數值;(忽略前導零)
    如果字符串中包含有效的浮點格式,則將其轉換為對應的浮點數值;(忽略前導0)
    如果字符串中包含有效的十六進制格式,則將其轉換為相同大小的十進制整數值;
    如果字符串是空的,則將其轉換為0;
    如果字符串中包含除上述格式之外的字符,則將其轉換為NaN
  • 如果是對象,則調用對象的valueOf()方法,然后依照前面的規則轉換返回的值。如果轉換的結果是NaN,則調用對象的toString(),然后再次依照前面的規則轉換返回的字符串值。
var num1 = Number("Hello world!");  //NaN
var num2 = Number("");  //0
var num3 = Number("000011");  //11
var num4 = Number(true);  //1

一元操作符的操作與Number()函數相同

parseInt()函數在轉換字符串時,更多的是看其是否符合數值模式。它會忽略字符串前面的空格,直至找到第一個非空格字符。如果第一個字符不是數字字符或者負號,會返回NaN;如果第一個字符是數字字符,會繼續解析,知道解析完所有后需字符或遇到一個非數字字符。

var num1 = parseInt("1234blue1213");  //1234
var num2 = parseInt("");  //NaN
var num3 = parseInt("0xA");  //10 (十六進制數)
var num4 = parseInt(22.5);  //22
var num = parseInt("0xAF", 16);  //175

var num = parseInt("AF", 16);  //175
var num2 = parseInt("AF");  //NaN

parseFloat也是從第一個字符開始解析每個字符。一直解析到字符串末尾,或者解析到遇見一個無效的浮點數字字符為止。
始終都會忽略前導的零。只解析十進制值。

var num1 = parseFloat("1234blue");  //1234
var num2 = parseFloat("0xA");  //0
var num3 = parseFloat("22.5");  //22.5
var num4 = parseFloat("22.34.5");  //22.34
var num5 = parseFloat("0908.5");  //908.5
var num6 = parseFloat("3.12e7");  //31250000

3.4.6 String 類型

var firstName = "Nicholas";
var lastName = 'Zakas';

1.字符字面量

String數據類型包含一些特殊的字符字面量,也叫轉義序列。

2.字符串的特點

要改變某個變量保存的字符串,首先要銷毀原來的字符串,然后再用另一個包含新值的字符串填充該變量。

var lang = "Java";
lang =lang + "Script";

3.轉換為字符串

把一個值轉換為一個字符串有兩種方式。第一種是使用幾乎每個值都有的toString()方法。這個方法唯一要做的就是返回相應值得字符串表現。

var age =11;
var ageAsString = age.toString();  //字符串“11”
var found = true;
var foundAsString = found.toString();  //字符串”true“

數值、布爾值、對象和字符串值都有toString()方法。但nullundefined值沒有這個方法。
toString()可以輸出以二進制、八進制、十六進制,乃至其他任意有效進制格式標識的字符串值。

var num = 10;
alert (num.toString());  //"10"
alert (num.toString(2));  //"1010"
alert (num.toString(8));  //"12"
alert (num.toString(10));  //"10"
alert (num.toString(10));  //"a"

在不知道要轉換的值是不是nullundefined的情況下,還可以使用轉型函數String(),這個函數能將任何類型的值轉換為字符串。

  • 如果值有toString()方法,則調用該方法(沒有參數)并返回相應的結果;
  • 如果值是null,則返回”null“;
  • 如果值是undefined,則返回”undefined“。

3.4.7 Object 類型

ECMAScript中的對象其實就是一組數據和功能的集合。

var o = new Object();
var o = new Object;//有效,但不推薦省略圓括號

Object的每個實例都具有下列屬性和方法。

  • Constructor:保存著用于創建當前對象的函數。對于前面的例子而言,構造函數就是Object()。
  • hasOwnProperty(propertyName):用于檢查給定的屬性在當前對象實例中是否存在。其中,作為參數的屬性名必須以字符串形式指定。
  • isPrototypeOf(object):用于檢查傳入的對象是否是另一個對象的原型。
  • propertyIsEnumerable(propertyName):用于檢查給定的屬性是否能夠使用for-in語句來枚舉。作為參數的屬性名必須以字符串形式指定。
  • toLocaleString():返回對象的字符串表示,該字符串與執行環境的地區對應。
  • toString():返回對象的字符串表示。
  • valueOf():返回對象的字符串、數值或布爾值表示。通常與toString()方法的返回值相同。

由于在ECMAScript中Object是所有對象的基礎,因此所有對象都具有這些基本的屬性和方法。

3.5 操作符

ECMAScript 操作符的與眾不同之處在于,它們能夠適用于很多值,例如字符串、數字值、布爾值,甚至對象。在應用于對象時,相應的操作符通常都會調用對象的valueOf()和(或)toString()方法,以便取的可以操作的值。

3.5.1 一元操作符

1.遞增和遞減操作符

var age = 29;
++age;
var age = 29;
--age;

執行前置遞增和遞減操作時,變量的值都是在語句被求值以前改變的。

var age = 29;
var anotherAge = --age + 2;
alert(age);  //輸出28
alert(anotherAge);  //輸出30

后置型遞增和遞減操作是在包含它們的語句被求值之后才執行的。

var num1 = 2;
var num2 = 20;
var num3 = num1-- + num2;  //等于22
var num4 = num1 + num2;  //等于21

在應用于不同的值時,遞增和遞減操作符遵循下列規則。

  • 在應用與一個包含有效數字字符的字符串時,先將其轉換為數字值,在執行加減1的操作。字符串變量變成數值變量。
  • 在應用于一個不包含有效數字字符的字符串時,將變量的值設置為NaN。字符串變量變成數值變量。
  • 在應用于布爾值false時,先將其轉換為0再執行加減1的操作。布爾值變量變成數值變量。
  • 在應用于布爾值true時,先將其轉換為1在執行加減1的操作。布爾值變量變成數值變量。
  • 在應用于浮點數值時,執行加減1的操作。
  • 在應用于對象時,先調用對象的valueOf()方法以取得一個可供操作的值。然后對該值應用前述規則。如果結果是NaN,則在調用toString()方法后再應用前述規則。對象變量變成數值變量。
var s1 = "2";
var s2 = "z";
var b = false;
var f = 1.1;
var o = {
  valueOf: function() {
    return -1;
  }
};

s1++;  //3
s2++;  //NaN
b++;  //1
f--;  //0.10000000000000009
o--;  //-2

2.一元加和減操作符

一元操作符以一個加號表示,放在數值前面,對數值不會產生任何影響。

var num = 25;
num = +num;  //25

不過,在對非數值應用一元加操作符時,該操作符會像Number()轉型函數一樣對這個值執行轉換。

var s1 = "01";
var s2 = "1.1";
var s3 = "z";
var b = false;
var f = 1.1;
var o = {
  valueOf: function() {
    return -1;
  }
};

s1 = +s1;  //1
s2 = +s2;  //1.1
s3 = +s3;  //NaN
b = +b;  //0
f = +f;  //1.1
o = +o;  //-1

一元減操作符主要用于表示負數。

var num = 25;
num = -num;  //-25

應用于飛數值時,一元減操作符遵循與一元加操作符相同的規則,最后再將得到的數值轉換為負數。

3.5.2 位操作符

在 ECMAScript中,當對數值應用位操作符時,后臺會發生如下轉換過程:64位的數值被轉換成32位數值,然后執行位操作,最后再將32位的結果轉換回64位數值。這個轉換過程導致了一個嚴重的副效應,即在對特殊的NaNInfinity值應用位操作時,這兩個值都會被當成0來處理。
如果對非數值應用位操作符,會先使用Number()函數將該值轉換為一個數值,然后再應用位操作。得到的結果將是一個數值。

1.按位非 (NOT)

var num1 = 25;  //二進制
var num2 = ~num1;  //二進制 
alert(num2);  //-26

這也驗證了按位非操作的本質:操作數的負值減1。

2.按位與(AND)

從本質上講,按位與操作就是將兩個數值的每一位對齊,然后根據下表中的規則,對相同位置上的兩個數執行AND操作:

第一個數值的位 第二個數值的位 結果
1 1 1
1 0 0
0 1 0
0 0 0
var result = 25 & 3;
alert(result);  //  1

3.按位或 (OR)

按位或操作遵循下面這個真值表。

第一個數值的位 第二個數值的位 結果
1 1 1
1 0 1
0 1 1
0 0 0
var result = 25 | 3;
alert(result);  //27

4.按位異或 (XOR)

按位異或操作符由一個插入符號(^)表示,也有倆操作數。以下是按位異或的真值表。

第一個數值的位 第二個數值的位 結果
1 1 0
1 0 1
0 1 1
0 0 0
var result = 25 ^ 3;
alert (result);  //26

5.左移

左移操作符由兩個小于號(<<)表示,這個操作符會將數值的所有位向左移動指定的位數。

var oldValue = 2;  //等于二進制的10
var newValue = oldValue << 5;  //等于二進制的1000000,十進制的64

左移操作會以0來填充這些空位,以便得到的結果是一個完整的32位二進制數。
注意,左移不會影響操作數的符號位。換句話說,如果將-2向左移動5位,結果將是-64,而非64。

6.有符號的右移

有符號的右移操作符由兩個大于號(>>)表示,這個操作符會將數值向右移動,但保留符號位。

var oldValue = 64;  //等于二進制的1000000
var newValue = oldValue >> 5;  //等于二進制的10,即十進制的2

7.無符號右移

無符號右移操作符由3個大于號(>>>)表示。這個操作符會將數值的所有32位都向右移動。對正數來說,無符號右移結果與有符號右移相同。

var oldValue = 64;
var newValue = oldValue >>> 5;  //等于二進制的10,即十進制的2

對負數來說,無符號右移是以0來填充空位,不會保留符號位的值。

var oldValue = -64;  
var newValue = oldValue >>> 5;  //等于十進制的134217726

3.5.3 布爾操作符

布爾操作符一共有3個:非(NOT)、與(AND)和或(OR)。

1.邏輯非

邏輯非操作符由一個嘆號(!)表示,可以應用于ECMAScript中的任何值。邏輯非操作符首先會將它的操作數轉換為一個布爾值,然后再對其求反。邏輯非操作符遵循下列規則:

  • 如果操作數是一個對象,返回fasle;
  • 如果操作數是一個空字符串,返回true
  • 如果操作數是一個非空字符串,返回false;
  • 如果操作數是數值0,返回true;
  • 如果操作數是任意非0數值(包括Infinity),返回false
  • 如果操作數是null,返回true;
  • 如果操作數是NaN,返回true;
  • 如果操作數是undefined,返回true。
alert(!false);  //true
alert(!"blue");  //false
alert(!0);  //true
alert(!NaN);  //true
alert(!"");   //true
alert(!12345);  //false

邏輯非操作符也可以用于將一個值轉換為與其對應的布爾值。而同時使用兩個邏輯非操作符,實際上就會模擬Boolean()轉型函數的行為。

alert(!!"blue");  //true
alert(!!0);  //false
alert(!!NaN);  //false
alert(!!"");  //false
alert(!!12345);  //true

2.邏輯與

邏輯與操作符由兩個和號(&&)表示,有兩個操作數,如下面的例子所示:

var result = true && false;

邏輯與的真值表如下:

第一個操作數 第一個操作數 結果
true true true
true false false
false true false
false false false

邏輯與操作可以應用于任何類型的操作數,而不僅僅是布爾值。在有一個操作數不是布爾值的情況下,邏輯與操作就不一定返回布爾值;此時,他遵循下列規則:

  • 如果第一個操作數是對象,則返回第二個操作數;
  • 如果第二個操作數是對象,則只有在第一個操作數的求值結果為true的情況下才會返回該對象;
  • 如果兩個操作數都是對象, 則返回第二個操作數;
  • 如果有一個操作數是NaN,則返回NaN;
  • 如果有一個操作數是undefined,則返回undefined。

邏輯與操作術語短路操作,即如果第一個操作數能夠決定結果,那么就不會再對第二個操作數求值。如果第一個操作數是false, 則無論第二個操作數是什么值,結果都不會是true了。

var found = true;
var result = (found && someUndefinedVariable);  //這里會發生錯誤
alert(result);  //這一行不會執行
var found = false;
var result = (found && someUndefinedVariable);  //不會發生錯誤
alert(result);  //會執行("false")

3.邏輯或

邏輯或操作符由兩個豎線符號(||)表示,由兩個操作數。

var result = true || false;

邏輯或的真值表如下:

第一個操作數 第二個操作數 結果
true true true
true false true
false true true
false false false

如果有一個操作數不是布爾值,邏輯或也不一定返回布爾值;此時,它遵循下列規則:

  • 如果第一個操作數是對象,則返回第一個操作數;
  • 如果第一個操作數的求值結果為false,則返回第二個操作數;
  • 如果兩個操作數都是對象,則返回第一個操作數;
  • 如果兩個操作數都是null,則返回null;
  • 如果兩個操作數都是NaN,則返回NaN;
  • 如果兩個操作數都是undefined,則返回undefined。

邏輯或操作符也是短路操作符。也就是說,如果第一個操作數的求值結果為true,就不會對第二個操作數求值了。

var found = true;
var result = (found || someUndefinedVariable);  //不會發生錯誤
alert(result);  //會執行 ("true")
var found = false;
var result = (found || someUndefinedVariable);  //這里會發生錯誤
alert(result);  //這一行不會執行

我們可以利用邏輯或的這一行為來避免為變量賦nullundefined值。例如:

var myObject = preferredObject || backupObject;

3.5.4 乘性操作符

ECMAScript定義了3個乘性操作符:乘法、除法和求模。如果參與乘法計算的某個操作數不是數值,后臺會先使用Number()轉型函數將其轉換為數值。也就是說,空字符串將被當作0,布爾值true將被當作1。

1.乘法

乘法操作符由一個星號(*)表示,用于計算兩個數值的乘積。

var result = 34 * 56;

在處理特殊值的情況下,乘法操作符遵循下列特殊的規則:

  • 如果操作數都是數值,執行常規的乘法計算。如果乘積超過了ECMAScript數值的表示范圍,則返回Infinity-Infinity;
  • 如果有一個操作數是NaN,則結果是NaN;
  • 如果是Infinity與0相乘,則結果是NaN;
  • 如果是Infinity與非0數值相乘,則結果是Infinity-Infinity,取決于有符號操作數的符號;
  • 如果有一個操作數不是數值,則在后臺調用Number()將其轉換為數值,然后再應用上面的規則。

2.除法

除法操作符由一個斜線符號(/)表示,執行第二個操作數除第一個操作數的計算,如下面的例子所示:

var result = 66 / 11;

規則如下:

  • 如果操作數都是數值,執行常規的除法計算。如果商超過了ECMAScript數值的表示范圍,則返回Infinity-Infinity;
  • 如果有一個操作數是NaN,則結果是NaN;
  • 如果是InfinityInfinity除,則結果是NaN;
  • 如果是零被零出,則結果是NaN;
  • 如果是非零的有限數被零除,則結果是Infinity-Infinity;
  • 如果是Infinity被任何非零數值除,則結果是Infinity-Infinity;
  • 如果有一個操作數不是數值,則在后臺調用Number()將其轉換為數值,然后在應用上面的規則。

3.求模

求模(余數)操作符由一個百分號(%)表示,用法如下:

var result = 26 % 5;  //等于1

求模操作符會遵循下列特殊規則來處理特殊的值:

  • 如果操作數都是數值,執行常規的除法計算,返回除得的余數;
  • 如果被除數是無窮大值而除數是有限大的數值,則結果是NaN
  • 如果被除數是有限大的數值而除數是零,則結果是NaN;
  • 如果是InfinityInfinity除,則結果是被除數;
  • 如果被除數是有限大的數值而除數是無窮大的數值,則結果是被除數;
  • 如果被除數是零,則結果是零;
  • 如果有一個操作數不是數值,則在后臺調用Number()將其轉化為數值,然后在應用上面的規則。

3.5.5 加性操作符

在ECMAScript中,這兩個操作符有一系列的特殊行為。

1.加法

var result = 1 + 2;

如果兩個操作符都是數值,執行常規的加法計算,然后根據下列規則返回結果:

  • 如果有一個操作數是NaN,則結果是NaN;
  • 如果是InfinityInfinity,則結果是Infinity;
  • 如果是-Infinity-Infinity,則結果是-Infinity;
  • 如果是+0加+0,則結果是+0;
  • 如果是-0加-0,則結果是-0;
  • 如果是+0加-0,則結果是+0;

不過,如果有一個操作數是字符串,那么就要應用如下規則:

  • 如果兩個操作數都是字符串,則將第二個操作數與第一個操作數拼接起來;
  • 如果只有一個操作數是字符串,則將另一個操作數轉換為字符串,然后再將兩個字符串拼接起來。

如果有一個操作數是對象、數值或布爾值,則調用它們的toString()方法取得相應的字符串值,然后再應用前面關于字符串的規則。對于undefinednull,則分別調用String()函數并取得字符串"undefined"和"null"。

var result1 = 5 + 5;  //連個數值相加
alert(result1);  //10
var result2 = 5 + "5";  //一個數值和一個字符串相加
alert(result2);  //"55"
var num1 =5;
var num2 =10;
var message = "The sum of 5 and 10 is" + num1 + num2;
alert(message);  //"The sum of 5 and 10 is 510"
var num1 = 5;
var num2 = 10;
var message = "The sum of 5 and 10 is" + (num1 + num2);
alert(message);  //"The sum of 5 and 10 is 15"

2.減法

var result = 2 - 1;

同樣需要遵循一些特殊規則,如下所示:

  • 如果兩個操作符都是數值,則執行常規的算術減法操作并返回結果;
  • 如果有一個操作數是NaN,則結果是NaN;
  • 如果是InfinityInfinity,則結果是NaN;
  • 如果是-Infinity-Infinity,則結果是NaN;
  • 如果是Infinity-Infinity,則結果是Infinity;
  • 如果是-InfinityInfinity,則結果是-Infinity
  • 如果是+0減+0,則結果是+0;
  • 如果是+0減-0,則結果是-0;
  • 如果是-0減-0,則結果是+0;
  • 如果有一個操作數是字符串、布爾值、nullundefined,則先在后臺調用Number()函數將其轉換為數值,然后再根據前面的規則執行減法計算。如果轉換的結果是NaN,則減法的結果就是NaN
  • 如果有一個操作符是對象,則調用對象的valueOf()方法以取得表示該對象的數值。如果得到的值是NaN,則減法的結果就是NaN。如果對象沒有valueOf()方法,則調用其toString()方法并將得到的字符串轉換為數值。
var result1 = 5 - true;  //4
var result2 = NaN - 1;  //NaN
var result3 = 5 - 3;  //2
var result4 = 5 - "";  //5
var result5 = 5 - "2";  //3
var result6 = 5 - null;  //5

3.5.6 關系操作符

小于(<)、大于(>)、小于等于(<=)和大于等于(>=)這幾個關系操作符用于對兩個值進行比較,這幾個操作符都返回一個布爾值。

var result1 = 5 > 3;  //true
var result2 = 5 < 3;  //false

當關系操作符的操作數使用了非數值時,也要進行數據轉換或完成某些奇怪的操作。

  • 如果連個操作數都是數值,則執行數值比較。
  • 如果兩個操作數都是字符串,則比較兩個字符串對應的字符編碼值。
  • 如果一個操作數是數值,則將一個操作數轉換為一個數值,然偶執行數值比較。
  • 如果一個操作數是對象,則調用這個對象的valueOf()方法,用得到的結果按照前面的規則執行比較。如果對象沒有valueOf()方法,則調用toString方法,并用得到的結果根據前面的規則執行比較。
  • 如果一個操作數是布爾值,則先將其轉換為數值,然后在執行比較。

在使用關系操作符比較兩個字符串時,會執行一種奇怪的操作。很多人都會認為,在比較字符串值時,小于的意思是“在字母表中的位置靠前”,而大于則意味著“在字母表中的位置靠后”,但實際上完全不是那么回事。在比較字符串時,實際比較的是兩個字符串中對應位置的每個字符的字符編碼值。經過這么一番比較后,再返回一個布爾值。由于大寫字母的字符編碼全部小于小寫字母的字符編碼,因此我們就會看到如下所示的奇怪現象:

var result = "Brick" < "alpahabet";  //true

要真正按照字母表順序比較字符串,就必須把兩個操作數轉換為相同的大小寫形式,然后在執行比較,如下所示:

var result = "Brick.toLowerCase()" < "alphabet".toLowerCase();  //false
var result = "23" < "3";  //true
var result = "23" < 3;  //false
var result = "a" < 3;  //false,因為“a”被轉換成了NaN
var result1 = NaN < 3;  //false
var result2 = NaN >= 3;  //false

按照常理,如果一個值不小于另一個值,則一定是大于或等于那個值。然而,在與NaN進行比較時,這兩個比較操作的結果都返回了fasle。

3.5.7 相等操作符

在比較字符串、數值和布爾值的的相等性時,問題還比較簡單。但在涉及到對象的比較時,問題就變得復雜了。最后,ECMAScript的解決方案就是提供兩組操作符:相等和不相等——先轉換在比較,全等和不全等——僅比較而不轉換。

1.相等和不相等

會先轉換操作數,然后再比較它們的相等性。在轉換不同的數據類型時,相等和不相等操作符遵循下列基本規則:

  • 如果有一個操作數是布爾值,則在比較相等性之前先將其轉換為數值——false轉換為0,而true轉換為1;
  • 如果一個操作數是字符串,另一個操作數是數值,在比較相等性之前先將字符串轉換為數值;
  • 如果一個操作數是對象,另一個操作數不是,則調用對象的valueOf()方法,用得到的基本類型值按照前面的規則進行比較;

這兩個操作符在進行比較時則要遵循下列規則。

  • nullundefined是相等的。
  • 要比較相等性之前,不能將nullundefined轉換成其他任何值。
  • 如果有一個操作數是NaN,則相等操作符返回false,而不相等操作符返回true。重要提示:即使兩個操作數都是NaN,相等操作符也返回false;按照規則,NaN不等于NaN
  • 如果兩個操作數都是對象,則比較它們是不是同一個對象。如果兩個操作數都指向同一個對象,則相等操作符返回true;否則,返回false。

下表列出了一些特殊情況及比較結果:

表達式
null == undefined true
"NaN" == NaN false
5 == NaN false
NaN == NaN false
NaN != NaN true
false == 0 true
true == 1 true
true == 2 false
undefined == 0 false
null == 0 false
"5" == 5 true

2.全等和不全等

只在兩個操作數未經轉換就相等的情況下返回true

var result1 = ("55" == 55);  //true,因為轉換后相等
var result2 = ("55" === 55);  //false,因為不同的數據類型不相等
var result1 = ("55" != 55);  //false,因為轉換后相等
var result2 = ("55" !== 55);  //true,因為不同的數據類型不相等

3.5.8 條件操作符

variable = boolean_expression ? true_value : false_value;
var max = (num1 > num2) ? num1: num2;

3.5.9 賦值操作符

var num = 10;
var num = 10;
num += 10;

每個主要算術操作符(以及個別的其他操作符)都有對應的復合賦值操作符。這些操作符如下所示:

  • 乘/賦值 (*=);
  • 除/賦值 (/=);
  • 模/賦值 (%=);
  • 加/賦值 (+=);
  • 減/賦值 (-=);
  • 左移/賦值 (<<=);
  • 有符號右移/賦值 (>>=);
  • 無符號右移/賦值 (>>>=)。

3.5.10 逗號操作符

使用逗號操作符可以在一條語句中執行多個操作。

var num1 =1, num2 = 2, num3 = 3;

逗號操作符還可以用于賦值。在用于賦值時,逗號操作符總會返回表達式中的最后一項。

var num = (5, 1, 4, 8, 0);  //num的值為0

由于0是表達式中的最后一項,因此num的值就是0。

3.6 語句

從本質上看,語句定義了ECMAScript中的主要語法,語句通常使用一或多個關鍵字來完成給定任務。

3.6.1 if 語句

if (condition) statement1 else statement2
if (i > 25)
  alert("Greater than 25.");  //單行語句
esle {
  alert("Less than or equal to 25.");  //代碼塊中的語句
}

不過,業界普遍推崇的最佳實踐是始終使用代碼塊,即使要執行的只有一行代碼。
另外,也可以把整個if語句寫在一行代碼中:

if (condition1) statement1 else if (condition2) statement2 else statement3

但我們推薦的做法則是像下面這樣:

if (i > 25) {
  alert("Greater than 25.");
} else if (i < 0) {
  alert("Less than 0.");
} else {
  alert("Between 0 and 25, inclusive.");
}

3.6.2 do-while 語句

do-while語句是一種后測試循環語句,即只有在循環體中的代碼執行之后,才會測試出口條件。

do {
  statement
} while (expression);
var i = 0;
do {
  i += 2;
} while (i < 10);
alert(i);

3.6.3 while 語句

while語句屬于前測試循環語句,也就是說,在循環體內的代碼被執行之前,就會對出口條件求值。

while(expression) statement
var i = 0;
while (i < 10) {
  i +=2;
}

3.6.4 for語句

for語句也是一種前測試循環語句,但它具有在執行循環之前初始化變量和定義循環后要執行的代碼的能力。

for (initialization; expression; post-loop-expression) statement
var count = 10;
for (var i = 0; i < count; i++) {
  alert(i);
}

這個for循環語句與下面的while語句的功能相同:

var count = 10;
var i = 0;
while (i < count) {
  alert(i);
  i++;
}

變量初始化可以在外部執行,由于ECMAScript中不存在塊級作用域,因此在循環內部定義的變量也可以在外部訪問到。
此外,for語句中的初始化表達式、控制表達式和循環后表達式都是可選的。將這兩個表達式全部省略,就會創建一個無限循環。

for (; ;) {  //無限循環
  doSomething();
}

而只給出控制表達式實際上就把for循環轉換成了while循環。

var count = 10;
var i = 0;
for (; i < count; ) {
  alert(i);
  i++;
}

3.6.5 for-in 語句

for-in語句是一種精準的迭代語句,可以用來枚舉對象的屬性。

for (property in expression) statement
for (var propName in window) {
  document.write(propName);
}

ECMAScript對象的屬性沒有順序。因此,通過for-in循環輸出的屬性名的順序是不可預測的。
但是,如果表示要迭代的對象的變量值為nullundefined,for-in語句會拋出錯誤。ECMAScript5更正了這一行為;對這種情況不再拋出錯誤,而只是不執行循環體。建議在使用for-in循環之前,先檢測確認該對象的值不是nullundefined。

3.6.6 label語句

使用label語句可以在代碼中添加標簽,以便將來使用。

label: statement
start: for(var i=0; i < count; i++) {
  alert(i);
}

這個例子中定義的start標簽可以在將來由breakcontinue語句引用。加標簽的語句一般都要與for語句等循環語句配合使用。

3.6.7 break和continue語句

breakcontinue語句用于在循環中精確控制代碼的執行。其中,break語句會立即退出循環,強制繼續執行循環后面的語句。而continue語句雖然也是立即退出循環,但退出循環后會從循環的頂部繼續執行。

var num = 0;
for (var i = 1; i < 10; i++) {
  if(i % 5 == 0) {
    break;
  }
  num++;
}
alert(num);  //4
var num = 0;
for (var i = 1; i < 10; i++) {
  if(i % 5 == 0) {
    continue;
  }
  num++;
}
alert(num);  //8

breakcontinue語句都可以與label語句聯合使用,從而返回代碼中特定的位置。

var num = 0;
outermost:
for (var i = 0; i < 10; i++) {
  for(var j = 0; j < 10; j++) {
    if(i == 5 && j == 5) {
      break outermost;
    }
    num++;
  }
}
alert(num);  //55

添加這個標簽的結果將導致break語句不僅會退出內部的for語句,而且也會退出外部的for語句。

3.6.8 with語句

with語句的作用是將代碼的作用域設置到一個特定的對象中。

with (expression) statement;

定義with語句的目的主要是為了簡化多次編寫同一個對象的工作。

var qs = location.search.substring(1);
var hostName = location.hostname;
var url = location.href; 
with(location) {
  var qs = search.substring(1);
  var hostName = host.name;
  var url = href;
}

嚴格模式下不允許使用with語句,否則將視為語法錯誤。
不建議使用with語句。

3.6.9 switch語句

ECMAScript中switch語句的語法與其他基于C的語言非常接近。

switch (expression) {
  case value: statement
    break;
  case value: statement
    break;
  case value: statement
    break;
  case value: statement
    break;
    default: statement
}

假如確實需要混合幾種情形,不要忘了在代碼中添加注釋,說明你是有意省略了break關鍵字。

switch(i) {
  case 25:
    /*合并兩種情形*/
  case 35:
    alert("25 or 35");
    break;
  case 45:
    alert("45");
    break;
  default:
    alert("other");
}

可以在switch語句中使用任何數據類型,無論是字符串,還是對象都沒有問題。。每個case的值不一定是常量,可以是變量,甚至是表達式。

switch語句在比較值時使用的是全等操作符,因此不會發生類型轉換。

3.7 函數

ECMAScript中的函數使用function關鍵字來聲明,后跟一組參數以及函數體。

function functionName(arg0, arg1, ..., argN) {
  statements
}
function sayHi(name, message) {
  alert("hello " + name + "," + message);
}

實際上,任何函數在任何時候都可以通過return語句后跟要返回的值來實現返回值。

function sum(num1, num2) {
  return num1 + num2;
}

return語句也可以不帶有任何返回值。在這種情況下,函數在停止執行后將返回undefined值。這種用法一般用在需要提前停止函數執行而又不需要返回值的情況下。

推薦的做法是要么讓函數始終都返回一個值,要么永遠都不要返回值。

嚴格模式對函數有一些限制:

  • 不能把函數命名為evalarguments;
  • 不能把參數命名為evalarguments;
  • 不能出現兩個命名參數同名的情況。

如果發生以上情況,就會導致語法錯誤,代碼無法執行。

3.7.1 理解函數

在函數體內可以通過arguments對象來訪問這個參數數組,從而獲取傳遞給函數的每一個參數。
arguments對象只是與數組類似,因為可以使用方括號語法訪問它的每一個元素,使用length屬性來確定傳遞進來多少個參數。

function sayHi() {
  alert("hello " + arguments[0] + "," + arguments[1]);
}

這個重寫后的函數中不包含命名的參數。這個事實說明:命名的參數只提供便利,但不是必需的。

通過訪問arguments對象的length屬性可以獲知有多少個參數傳遞給了函數。

function howManyArgs() {
  alert(arguments.length);
}

howManyArgs("string", 45);  //2
howManyArgs();  //0
howManyArgs(12);  //1

開發人員可以利用這一點讓函數能夠接收任意個參數并分別實現適當的功能。

function doAdd() {
  if(arguments.length == 1) {
    alert(arguments[0] + 10);
  } else if (arguments.length == 2) {
    alert(arguments[0] + arguments[1]);
  }
}

doAdd(10);  //20
doAdd(30, 20);  //50

另一個與參數相關的重要方面,就是arguments對象可以與命名參數一起使用。

function doAdd(num1, num2) {
  if(arguments.length ==1) {
    alert(num1 + 10);
  } else if (arguments.length == 2) {
    alert(arguments[0] + num2);
  }
}

關于arguments的行為,還有一點比較有意思。那就是它的值永遠與對應命名參數的值保持同步。

function doAdd(num1, num2) {
  arguments[1] = 10;
  alert(arguments[0] + num2);
}

每次執行這個函數都會重寫第二個參數,將第二個參數的值修改為10.因為arguments對象中的值會自動反映到對應的命名參數。它們的內存空間是獨立的,但它們的值會同步。但這種影響是單向的:修改命名參數不會改變arguments中對應的值。如果只傳入了一個參數,那么為arguments[1]設置的值不會反應到命名參數中。這是因為arguments對象的長度是由傳入的參數個數決定的,不是由定義函數時的命名參數的個數決定的。
關于參數還要記住最后一點:沒有傳遞值的命名參數將自動被賦予undefined值。
嚴格模式中,像前面例子中那樣的賦值會變得無效,即使把arguments[1]設置為10,num2的值仍然還是undefined。其次,重寫arguments的值會導致語法錯誤。

ECMAScript中的所有參數傳遞的都是值,不可能通過引用傳遞參數。

3.7.2 沒有重載

ECMAScript函數不能像傳統意義上那樣實現重載。
如果在ECMAScript中定義了兩個名字相同的函數,則該名字只屬于后定義的函數。

function addSomeNumber(num) {
  return num + 100;
}

function addSomeNumber(num) {
  return num + 200;
}
var result = addSomeNumber(100);  //300 

3.8 小結

以下簡要總結了ECMAScript中基本的要素。

  • ECMAScript中的基本數據類型包括Undefined、Null、Booelan、NumberString。
  • 與其他語言不同,ECMAScript沒有為整數和浮點數值分別定義不同的數據類型,Number類型可用于表示所有數值。
  • Object類型,該類型是這門語言中所有對象的基礎類型。
  • 嚴格模式為這門語言中容易出錯的地方施加了限制。
  • ECMAScript提供了很多與C及其其他類C語言中相同的基本操作符,包括算數操作符、布爾操作符、關系操作符、相等操作符及賦值操作符等。
  • 從其他語言借鑒了很多流控制語句,例如if語句、for語句和switch語句等。

ECMAScript中的函數與其他語言中的函數有諸多不同之處。

  • 無須指定函數的返回值。
  • 實際上,未指定返回值的函數返回的是一個特殊的undefined值。
  • ECMAScript中也沒有函數簽名的概念,因為其函數參數是以一個包含零或多個值的數組的形式傳遞的。
  • 可以向ECMAScript函數傳遞任意數量的參數,并且可以通過arguments對象來訪問這些參數。
  • 由于不存在函數簽名的特性,ECMAScript函數不能重載。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,837評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,196評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,688評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,654評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,456評論 6 406
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,955評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,044評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,195評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,725評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,608評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,802評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,318評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,048評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,422評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,673評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,424評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,762評論 2 372

推薦閱讀更多精彩內容