第 3 章 基本概念
3.1 语法
3.1.1 区分大小写
是 ECMAScript 中的一切(变量、函数名和操作符)都区分大小写,变量名 test 和变量名 Test 分别表示两个不同的变量
3.1.2 标识符
所谓标识符,就是指变量、函数、属性的名字,或者函数的参数。标识符可以是按照下列格式规则
组合起来的一或多个字符:
- 第一个字符必须是一个字母、下划线(_)或一个美元符号($);
- 其他字符可以是字母、下划线、美元符号或数字
标识符采用驼峰大小写格式,也就是第一个字母小写,剩下的每个单词的
首字母大写
不能把关键字、保留字、true、false 和 null 用作标识符
3.1.3 注释
包括单行注释和块级注释
单行注释以两个斜杠开头 // 单行注释
块级注释以一个斜杠和一个星号(/)开头,以一个星号和一个斜杠(/)结尾
/*
* 这是一个多行
* (块级)注释
*/
3.1.4 严格模式
在函数内部的上方包含这条编译指示,也可以指定函数在严格模式下执行:
function doSomething(){ “use strict”; //函数体 }
3.1.5 语句
ECMAScript 中的语句以一个分号结尾;如果省略分号,则由解析器确定语句的结尾,如下例所示:
var sum = a + b // 即使没有分号也是有效的语句——不推荐
var diff = a - b; // 有效的语句——推荐
可以使用 C 风格的语法把多条语句组合到一个代码块中,即代码块以左花括号({)开头,以右花
括号(})结尾:
if (test){
test = false;
alert(test);
}
虽然条件控制语句(如 if 语句)只在执行多条语句的情况下才要求使用代码块,但最佳实践是始
终在控制语句中使用代码块——即使代码块中只有一条语句,例如:
if (test)
alert(test); // 有效但容易出错,不要使用
if (test){ // 推荐使用
alert(test);
}
在控制语句中使用代码块可以让编码意图更加清晰,而且也能降低修改代码时出错的机率。
3.2 关键字和保留字
ECMA-262 描述了一组具有特定用途的关键字,这些关键字可用于表示控制语句的开始或结束,或者用于执行特定操作等。按照规则,关键字也是语言保留的,不能用作标识符。以下就是 ECMAScript的全部关键字(带*号上标的是第 5 版新增的关键字):
ECMA-262 还描述了另外一组不能用作标识符的保留字。尽管保留字在这门语言中还没有任何特定的用途,但它们有可能在将来被用作关键字。以下是 ECMA-262 第 3 版定义的全部保留字:
第 5 版把在非严格模式下运行时的保留字缩减为下列这些:
在严格模式下,第 5 版还对以下保留字施加了限制:
注意,let 和 yield 是第 5 版新增的保留字;其他保留字都是第 3 版定义的。为了最大程度地保证兼容性,建议读者将第 3版定义的保留字外加 let 和 yield 作为编程时的参考。
3.3 变量
定义变量时要使用var
操作符(注意 var 是一个关键字),后跟变量名(即一个标识符),如下所示:
var message;
在定义变量的同时就可以设置变量的值,如下所示:
var message = "hi";
如果在函数中使用 var 定义一个变量,那么这个变量在函数退出后就会被销毁,例如:
function test(){
var message = "hi"; // 局部变量
}
test();
alert(message); // 错误!
省略 var 操作符,从而创建一个全局变量:
function test(){
message = "hi"; // 全局变量
}
test();
alert(message); // "hi"
虽然省略 var 操作符可以定义全局变量,但这也不是我们推荐的做法。因为在局
部作用域中定义的全局变量很难维护,而且如果有意地忽略了 var 操作符,也会由于
相应变量不会马上就有定义而导致不必要的混乱。给未经声明的变量赋值在严格模式
下会导致抛出 ReferenceError 错误。
3.4 数据类型
ECMAScript 中有 5 种简单数据类型(也称为基本数据类型):
Undefined、Null、Boolean、Number和 String
。
还有 1种复杂数据类型——Object,Object
本质上是由一组无序的名值对组成的。
3.4.1 typeof操作符
- “undefined”——如果这个值未定义;
- “boolean”——如果这个值是布尔值;
- “string”——如果这个值是字符串;
- “number”——如果这个值是数值;
- “object”——如果这个值是对象或 null;
- “function”——如果这个值是函数。
下面是几个使用 typeof 操作符的例子:
var message = "some string";
alert(typeof message); // "string"
alert(typeof(message)); // "string"
alert(typeof 95); // "number"
3.4.2 Undefined类型
Undefined
类型只有一个值,即特殊的 undefined。在使用 var 声明变量但未对其加以初始化时,这个变量的值就是 undefined,例如:
var message;
alert(message == undefined); //true
这个例子只声明了变量 message,但未对其进行初始化。比较这个变量与 undefined 字面量,结果表明它们是相等的。这个例子与下面的例子是等价的:
var message = undefined;
alert(message == undefined); //true
对未初始化的变量执行 typeof
操作符会返回 undefined
值,而对未声明的变量执行 typeof
操作符同样也会返回 undefined
值。
即便未初始化的变量会自动被赋予 undefined 值,但显式地初始化变量依然是
明智的选择。如果能够做到这一点,那么当 typeof 操作符返回"undefined"值时,
我们就知道被检测的变量还没有被声明,而不是尚未初始化。
3.4.3 Null类型
Null 类型是第二个只有一个值的数据类型,这个特殊的值是 null。从逻辑角度来看,null 值表示一个空对象指针,而这也正是使用 typeof 操作符检测 null 值时会返回"object"的原因,如下面的例子所示:
var car = null;
alert(typeof car); // "object"
如果定义的变量准备在将来用于保存对象,那么最好将该变量初始化为 null 这样,只要直接检查 null 值就可以知道相应的变量是否已经保存了一个对象的引用,如下面的例子所示:
只要意在保存对象的变量还没有真正保存对象,就应该明确地让该变量保存
null
值。这样做不仅可以体现null
作为空对象指针的惯例,而且也有助于进一步区分null
和undefined
。
3.4.4 Boolean类型
Boolean 有两个字面值:true
和false
。
因此 true 不一定等于 1,而 false 也不一定等于 0。以下是为变量赋Boolean 类型值的例子:
var found = true;
var lost = false;
注意:
Boolean 类型的字面值 true 和 false 是区分大小写的。
True 和 False(以及其他的混合大小写形式)都不是 Boolean 值,只是标识符。
要将一个值转换为其对应的 Boolean 值,可以调用转型函数 Boolean()
字符串 message 被转换成了一个 Boolean 值,该值被保存在 messageAsBoolean
变量中。可以对任何数据类型的值调用 Boolean()函数,而且总会返回一个 Boolean 值。至于返回的这个值是 true 还是 false,取决于要转换值的数据类型及其实际值。下表给出了各种数据类型及其对应的转换规则。
这些转换规则对理解流控制语句(如 if 语句)自动执行相应的 Boolean
转换非常重要,请看下面的代码:
运行这个示例,就会显示一个警告框,因为字符串 message 被自动转换成了对应的 Boolean 值(true)。由于存在这种自动执行的 Boolean 转换,因此确切地知道在流控制语句中使用的是什么变量至关重要。错误地使用一个对象而不是一个 Boolean 值,就有可能彻底改变应用程序的流程。
3.4.5 Number类型
Number:使用 IEEE754 格式来表示整数和浮点数值(浮点数值在某些语言中也被称为双精度数值)。
最基本的数值字面量格式是十进制整数,十进制整数可以像下面这样直接在代码中输入:
var intNum = 55; // 整数
除了以十进制表示外,整数还可以通过八进制(以 8 为基数)或十六进制(以 16 为基数)的字面值来表示。其中,八进制字面值的第一位必须是零(0),然后是八进制数字序列(0~7)。如果字面值中的数值超出了范围,那么前导零将被忽略,后面的数值将被当作十进制数值解析。
var octalNum1 = 070; // 八进制的 56
var octalNum2 = 079; // 无效的八进制数值——解析为 79
var octalNum3 = 08; // 无效的八进制数值——解析为 8
十六进制字面值的前两位必须是 0x,后跟任何十六进制数字(0~9 及 A~F)。其中,字母 A~F可以大写,也可以小写。如下面的例子所示:
var hexNum1 = 0xA; // 十六进制的 10
var hexNum2 = 0x1f; // 十六进制的 31
- 浮点数值:小数点后面必须至少有一位数字
var floatNum1 = 1.1;
var floatNum2 = 0.1;
var floatNum3 = .1; // 有效,但不推荐
浮点数值本身表示的就是一个整数(如 1.0),那么该值也会被转换为整数
var floatNum1 = 1.; // 小数点后面没有数字——解析为 1
var floatNum2 = 10.0; // 整数——解析为 10
var floatNum = 3.125e7; // 等于 31250000
- 数值范围
- NaN
NaN,即非数值(Not a Number)是一个特殊的数值
NaN 与任何值都不相等,包括 NaN 本身。例如,下面的代码会返回 false:
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)
这个例子测试了 5 个不同的值。测试的第一个值是 NaN 本身,结果当然会返回 true。然后分别测试了数值 10 和字符串"10",结果这两个测试都返回了 false,因为前者本身就是数值,而后者可以被转换成数值。但是,字符串"blue"不能被转换成数值,因此函数返回了 true。由于 Boolean 值 true可以转换成数值 1,因此函数返回 false。
- 数值转换
有 3 个函数可以把非数值转换为数值:Number()
、parseInt()
和parseFloat()
。
第一个函数,即转型函数 Number()可以用于任何数据类型,
另两个函数则专门用于把字符串转换成数值。
Number()函数的转换规则如下。
- 如果是 Boolean 值,true 和 false 将分别被转换为 1 和 0。
- 如果是数字值,只是简单的传入和返回。
- 如果是 null 值,返回 0。
- 如果是 undefined,返回 NaN。
各种数据类型转换为数值
var num1 = Number("Hello world!"); //NaN
var num2 = Number(""); //0
var num3 = Number("000011"); //11
var num4 = Number(true); //1
字符串"Hello world!"会被转换为 NaN,因为其中不包含任何有意义的数字值。空字符串会被转换为 0。字符串"000011"会被转换为 11,因为忽略了其前导的零。最后,true 值被转换为 1。
parseInt()
函数的转换规则,下面给出一些例子:
3.4.6 String类型
String 类型用于表示由零或多个 16 位 Unicode 字符组成的字符序列,即字符串。字符串可以由双引号(")或单引号(’)表示
var firstName = "Nicholas";
var lastName = 'Zakas';
3.4.7 Object类型
以创建自定义对象,如下所示:var o = new Object();
3.5 操作符
ECMA-262 描述了一组用于操作数据值的操作符,包括算术操作符(如加号和减号)、位操作符、关系操作符和相等操作符
3.5.1 一元操作符
只能操作一个值的操作符叫做一元操作符。
- 递增和递减操作符
使用前置递增操作符给一个数
值加 1 时,要把两个加号(++)放在这个数值变量前面,如下所示:
var age = 29;
++age;
与
var age = 29;
age = age + 1;
相同
执行前置递减操作的方法也类似,结果会从一个数值中减去 1。使用前置递减操作符时,要把两个减号(–)放在相应变量的前面,如下所示:
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; // 等于 21
var num4 = num1 + num2; // 等于 21
2. 一元加和减操作符
一元加操作符以一个加号(+)表示,放在数值前面,对数值不会产生任何影响
var num = 25;
num = +num; // 仍然是 25
下面的例子展示了对不同数据类型应用一元加操作符的结果:
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
例如将 1 转换成-1。
var num = 25;
num = -num; // 变成了-25
3.5.2 位操作符
3.5.3 布尔操作符
布尔操作符一共有 3 个:非(NOT)
、与(AND)
和或(OR)
。
- 逻辑非(!)
逻辑非操作符由一个叹号(!)表示
- 逻辑与(&&)
逻辑与操作符由两个和号(&&)表示,有两个操作数,如下面的例子所示:
var result = true && false;
3. 逻辑或(||)
逻辑或操作符由两个竖线符号(||)表示,有两个操作数,如下面的例子所示:
var result = true || false;
var found = true;
var result = (found || someUndefinedVariable); // 不会发生错误
alert(result); // 会执行("true")
3.5.4 乘性操作符
定义了 3 个乘性操作符:乘法、除法和求模,空字符串将被当作
0,布尔值 true 将被当作 1。
- 乘法
乘法操作符由一个星号(*)表示,用于计算两个数值的乘积
var result = 34 * 56;
- 除法
除法操作符由一个斜线符号(/)表示,执行第二个操作数除第一个操作数的计算
var result = 66 / 11;
-
求模
求模(余数)操作符由一个百分号(%)表示,用法如下:var result = 26 % 5; // 等于 1
3.5.5 加性操作符
加法操作符(+)的用法如下所示:
var result = 1 + 2;
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"
-
减法
减法操作符()是另一个极为常用的操作符,其用法如下所示:var result = 2 - 1;
3.5.6 关系操作符
小于(<)、大于(>)、小于等于(<=)和大于等于(>=)这几个关系操作符用于对两个值进行比较
var result1 = 5 > 3; //true
var result2 = 5 < 3; //false
3.5.7 相等操作符
ECMAScript 的解决方案就是提供两组操作符:相等和不相等——先转换再比较
,全等和不全等——仅比较而不转换
- 相等和不相等
相等操作符由两个等于号(==)表示
两个操作数相等,则返回 true。而不相等操作符由叹号后跟等于号(!=)表示
- 全等和不全等
全等操作符由 3 个等于号(===)表示
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;
求值结果为 true,给变量 variable 赋 true_value 值;
求值结果为 false,给变量 variable 赋 false_value 值
var max = (num1 > num2) ? num1 : num2;
如果 num1 大于 num2(关系表达式返回 true),则将 num1 的值赋给 max,否则反之
3.5.9 赋值操作符
简单的赋值操作符由等于号(=)表示,其作用就是把右侧的值赋给左侧的变量
var num = 10;
在等于号(=)前面再添加乘性操作符、加性操作符或位操作符,就可以完成复合赋值操作
var num = 10;
num = 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
3.6 语句
3.6.1 if语句
大多数编程语言中最为常用的一个语句就是 if 语句。以下是 if 语句的语法:
if (condition) statement1 else statement2
if (i > 25)
alert("Greater than 25."); // 单行语句
else {
alert("Less than or equal to 25."); // 代码块中的语句
}
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语句
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);
}
3.6.5 for-in语句
for-in 语句是一种精准的迭代语句,可以用来枚举对象的属性。以下是 for-in 语句的语法:
for (property in expression) statement
下面是一个示例:
for (var propName in window) {
document.write(propName);
}
3.6.6 label语句
使用 label 语句可以在代码中添加标签,以便将来使用。以下是 label 语句的语法:
label: statement
下面是一个示例:
start: for (var i=0; i < count; i++) {
alert(i);
}
3.7 函数
过函数可以封装任意多条语句,而且可以在任何地方、任何时候调用执行
function functionName(arg0, arg1,...,argN) {
statements
}
以下是一个函数示例:
function sayHi(name, message) {
alert("Hello " + name + "," + message);
}
任何函数在任何时候都可以通过return 语句后跟要返回的值来实现返回值。请看下面的例子:
function sum(num1, num2) {
return num1 + num2;
}
推荐的做法是要么让函数始终都返回一个值,要么永远都不要返回值。否则,如
果函数有时候返回值,有时候有不返回值,会给调试代码带来不便。
严格模式对函数有一些限制:
不能把函数命名为 eval 或 arguments;
不能把参数命名为 eval 或 arguments;
不能出现两个命名参数同名的情况。
如果发生以上情况,就会导致语法错误,代码无法执行。