### 3.1 语法
ECMAScript中的一切(变量名、函数名、属性名、参数名、操作符)都区分大小写。
所谓**标识符**,就是指变量、函数、属性的名字,或者函数的参数。
* ***第一个字符必须是字母、下划线_、或美元符号$;***
* ***第二个字符可以使字母、下划线_、美元符号$或数字。***
***按照惯例,ECMAScript标识符采用驼峰大小写格式。***
### 3.3 变量
`var message;`
这行代码定义了一个名为message的变量,该变量可以用来保存任何值(像这样未经过初始化的变量,会保存一个特殊的值--undefined)。使用var操作符定义的变量将成为该变量的作用域中的局部变量,而省略var操作符会创建一个全局变量。
### 3.4 数据类型
ECMAScript中有5种简单数据类型(也称为基本数据类型):Undeifined、Null、Boolean、Number和String。还有一种复杂数据类型--Object,Object本质上是由一组无序的名值对组成的。
#### 3.4.1 typeof操作符
typeof操作符用于检测给定变量的数据类型。对一个值使用typeof操作符可能返回下列某个字符串(均为小写字母):
* “undefined”----如果这个值未定义;
* “boolean”----如果这个值是布尔值;
* “string”----如果这个值是字符串;
* “number”----如果这个值是数值;
* **“object”----如果这个值是对象(包括正则表达式)或者null;**
* **“function”----如果这个值是函数。**
#### 3.4.2 Undefined类型
Undefined类型只有一个值,即特殊的undefined。在使用var声明变量但未对其加以初始化(个人理解:初始化等同于赋值)时,这个变量的值就是undefined,例如:
~~~
var message;
alert(message == undifined); //true
~~~
对于尚未声明过的变量,只能执行一项操作,即使用typeof操作符检测其数据类型(其他操作均会产生错误)。然而令人困惑的是:**对未初始化的变量执行typeof操作符会返回undefined值,而对未声明的变量执行typeof操作符同样也会返回undefined值**。因此,即使未初始化的变量会自动被赋与undefined值,但显式地初始化变量依然是明智的选择。如果能够做到这一点,那么当typeof操作符返回”undefined”值时,我们就知道被检测的变量还没有被声明,而不是尚未初始化。
*****
如果只是声明了一个变量而不是马上对其实际赋值,那么将其初始化的最佳实践个人总结如下:
* ***Boolean类型赋值为true/false;***
* ***Number类型赋值为0;***
* ***String类型赋值为空字符串;***
* ***Object类型(空对象)赋值为null;***
#### 3.4.4 Boolean类型
要将一个值转换为其对应的布尔值,可以调用转型函数Boolean()。虽然Boolean类型的字面值只有两个,但ECMAScript中所有类型的值都有与这两个布尔值等价的值。各种数据类型及其对应的布尔值转换规则见P26。**流控制语句(如if语句)自动执行相应的Boolean转换(自动调用Boolean()函数)**。
#### 3.4.5 Number类型
Number类型的注意点包括:
- ***永远不要测试某个特定的浮点数值***。
- ECMAScript中整数除了能以十进制表示外,还可以通过八进制或十六进制的字面值来表示。
- 如果某次计算返回了正或负的Infinity值,那么该值将无法继续参与下一次的计算。要想确定一个数值是不是有穷的,可以使用isFinite()函数。这个函数会在参数位于最小与最大数值之间时返回true。
- 任何涉及NaN的计算操作都会返回NaN,这个特点在多步计算中有可能导致问题;NaN与任何值都不相等,包括NaN本身。
- IsNaN()函数用于判断参数是否“不是数值”,isNaN()函数的参数可以为任何类型,在接收到一个值之后,会尝试将这个值转换为数值(个人推测是调用Number()函数)。
- Number()函数可用于任何类型的转换,而parseInt()、parstFloat()函数则专门用于把字符串转换成数值,它们之间的主要区别有两个:
1. Number()函数不接受任何包含非数字的字符串(返回NaN),而后两者的参数只要第一个非空字符为数字即会返回数值,数字后的字符串将被忽略。
2. 是Number()函数对空字符串会返回数值0,而后两者会返回NaN。
此外,Number()函数会将undefined值转换为NaN而不是0。
#### 3.4.6 String类型
字符字面量中的转义序列详见P33。他们的一致特点是用反斜杠”\”开头。(最常用的\n表示换行)
*****
要把一个值转换为一个字符串有两种方式。第一种是使用每个引用类型值都有的toString()方法,但null和undefined没有这个方法。第二种是在不知道要转换的值是不是null或undefined的情况下使用转型函数String(),这个函数可以把所有类型值转换为相应的字符串。
#### 3.4.7 Object类型
ECMAScript中的对象其实就是一组数据和功能的集合。对象可以通过执行new操作符后跟要创建的对象类型的名称来创建。**而创建Object类型的实例并为其添加属性和(或)方法,就可以创建自定义对象**。
*****
创建一个Object类型**实例**的基本语法:
`var o = new Object();`
*****
在ECMAScript中,Object类型是所有它的实例的基础。换句话说,Object类型所具有的任何属性和方法也同样存在于更具体的对象中。
***Object的每个实例都具有下列属性和方法***:
* constructor:保存着用于创建当前对象的函数。对于前面的例子而言,构造函数(constructor)就是Object()。
* hasOwnProperty(propertyName):用于检查给定的属性在当前对象实例中(而不是在实例的原形中)是否存在。其中,作为参数的属性名(propertyName)必须以字符串形式指定。(例如:o.hasOwnProperty(‘name’);)。
* isPrototypeOf(Object):用于检查当前对象是否是传入对象的原型。
* propertyIsEnumerable(propertyName):用于检查给定的属性是否能够使用for-in语句来枚举。
* toLocaleString():返回对象的字符串表示,该字符串与执行环境的地区对应。
* toString():返回对象的字符串表示;
* valueOf():返回对象的字符串、数值或布尔值表示。通常与toString()方法的返回值相同。
由于在ECMAScript中Object对象是所有对象的基础,因此所有对象都具有这些基本的属性和方法。
*****
从技术角度讲,ECMA-262中对象的行为不一定适用于JavaScript中的其他对象。浏览器环境中的对象,比如BOM和DOM中的对象,都属于宿主对象,因为它们是由宿主提供和定义的。ECMA-262不负责定义宿主对象,因此宿主对象可能会也可能不会继承Object。
### 3.5 操作符
ECMAScript操作符的与众不同之处在于,它们能够适用于很多值,例如字符串、数值、布尔值,甚至对象。不过,在应用于对象时,相应的操作符通常都会调用对象的valueOf()和(或)toString()方法,以便取得可以操作的值。
#### 3.5.1 一元操作符
只能操作一个值的操作符叫做一元操作符。它们包括:
递增和递减操作符:++、--
一元加和一元减操作符:+、-
*****
后置递增和递减与前置递增和递减有一个非常重要的区别,即递增和递减操作是在包含他们的语句被求值之后才执行的。
例:
~~~
var num1 = 2, num2 = 20;
Var num3 = num1-- + num2; //22
Var num4 = num1 + num2: //21
~~~
对单个数据类型使用一元加和一元减操作符,该操作符会像Number()转型函数一样对这个值执行转换。(注:在所有转型函数中,只有Number()会对Object类型调用valueOf()和(或)toString()方法进行下一步操作。)。
#### 3.5.3 布尔操作符
***ECMAScript程序的赋值语句经常会利用逻辑或的特性,避免为变量赋null或undefined值***:
`var myObject = preferredObject || backupObject;`
在逻辑与中,如果第一个运算数为假,那么返回第一个运算数,否则返回第二个运算数。
在逻辑或中,如果第一个运算数为真,那么返回第一个运算数,否则返回第二个运算数。
***逻辑与、逻辑或都只会进行布尔测试,而不会真正地对操作数进行转型转换*!**
#### 3.5.4 乘性操作符
3个乘性操作符:乘法、除法和求模,在操作数为非数值的情况下同样会执行自动的Number()类型转换。
#### 3.5.5 加性操作符
如果应用加法操作符(+)的数据中存在字符串,则都会转换成字符串进行拼接;如果应用减法操作符(-)则全部转换为数值进行运算。
*****
**总的来说,算数操作符中,除了加法操作符会在操作数存在字符串的情况下进行String()的类型转换,其他操作符在遇到非数值类型时,都会进行Number()转换。**
#### 3.5.6 关系操作符
关系操作符包括<、>、<=、>= 4个。如果两个操作数都是字符串,则会比较两个字符串对应的字符编码值。其他情况都会进行Number()转换。
#### 3.5.7 相等操作符
相等(==)和不等(!=)操作符优先进行Number()类型转换再比较。另外还要遵循下列规则:
- null和undefined是相等的;
- 要比较相等性之前,不能将null和undefined转换成其他值。
- 如果有一个操作数是NaN,则相等操作符返回false,而不相等操作符返回true。即使两个数都是NaN,相等操作符也返回false。
- 如果两个操作数是对象,则比较它们是不是同一个对象。如果两个操作数都指向同一个对象,则相等操作符返回true,否则返回false。
***在实际编程中,为了保持代码中数据类型的完整性,推荐使用全等和全不等操作符。***
#### 3.5.8 条件操作符
条件操作符(也称三元操作符)的语法形式如下:
`var variable = boolean_expression ? true_value : false_value;`
问号前面的表达式将进行布尔测试,相当于
```
var variable;
if (boolean_expression) {
variable = true_value;
} else {
Varable = false_value;
}
```
## 操作符类型转换总结:
- 算数操作符(++、--、一元加操作符+、一元减操作符-、加法操作符+、减法操作符-、*、/、%):除了加法操作符(+)会在其中一个操作数是字符串的情况下对其他类型操作数进行String()类型转换,其他操作符在遇到非数值类型是,都会进行Number()转换。
- 布尔操作符(!、||、&&):逻辑非(!)会进行Boolean()转换,逻辑与(&&)和逻辑或(||)不进行类型转换。
- 关系操作符(<、>、<=、>=):如果两个操作数都是字符串,则会比较两个字符串对应的字符编码值。其他情况都会进行Number()转换,再返回布尔值。
- 相等操作符(==、!=、===、!==):相等(==)和不等(!=)操作符优先进行Number()类型转换再比较。另外还要遵循特殊的规则。全等(===)和全不等(!==)操作符不进行类型转换。
### 3.6 语句
#### 3.6.1 if语句
***多尝试使用if...else if...else语句以精简代码,当然最佳实践是使用switch。***
#### 3.6.5 for语句
for-in语句是一种精确的迭代语句,可以用来枚举对象的属性。以下是for-in语句的语法:
`for (property in expression) statement`
示例:
~~~
for (var propName in window) {
document.write(propName);
}
~~~
在这个例子中,我们使用for-in语句循环来显示了BOM中window对象的所有属性。每次执行循环,都会将window对象中存在的一个属性名赋值给变量propName。这个过程会一直持续到对象中的所有属性都被枚举一遍为止。与for语句类似,这里控制语句中的var操作符也不是必须的,但是为了保证使用局部变量,我们推荐上面例子中的这种做法。
*****
如果表示要迭代的对象的变量值为null或undefined,for-in语句在ES5之前的版本会抛出错误。建议在使用for-in循环之前,先检测确认该对象的值不是null或undefined。
#### 3.6.9 switch语句
***switch语句可以合并使用***,例:
~~~
Switch (i) {
case 25:
case 35:
alert(“25或35”);
break;
case 45:
alert(“45”);
break;
default:
alert(“other”);
}
~~~
这其实也相当于
~~~
if (i == 25 || i==35) {
alert(“25或35”);
}
~~~
### 3.7 函数
#### 3.7.1 理解参数
ECMAScript中的参数在内部是用一个数组来表示的,在函数体内可以通过arguments对象来访问这个参数数组,从而获取出传递给函数的每一个参数。
*****
其实,arguments对象只是与数组类似(它并不是Array的实例),因为***可以使用方括号语法访问它的每一个元素,使用length属性来确定传递进来多少个参数。开发人员可以利用length属性让函数能够接收任意个参数并分别实现适当的功能***。
*****
关于arguments的行为,还有一点比较有意思。那就是它的值永远与对应命名参数的值保持同步。没有传递值的命名参数将被自动赋予undefined值。