第三章 基本概念

本章内容

  1. 语法
  2. 数据类型
  3. 流控制语句
  4. 函数

3.1 语法

3.1.1 区分大小写

要理解的第一个概念就是 ECMAScript 中的一切(变量、函数名和操作符)都区分大小写。这也就意味着,变量名 test 和变量名 Test 分别表示两个不同的变量,而函数名不能使用 typeof ,因为它是一个关键字(3.2 节介绍关键字),但 typeOf 则完全可以是一个有效的函数名。

3.1.2 标识符

所谓标识符,就是指变量、函数、属性的名字,或者函数的参数。标识符可以是按照下列格式规则组合起来的一或多个字符:
  1. 第一个字符必须是一个字母、下划线( _ )或一个美元符号( $ );
  2. 其他字符可以是字母、下划线、美元符号或数字。
标识符中的字母也可以包含扩展的 ASCII或 Unicode字母字符(如 À和 Æ),但我们不推荐这样做。按照惯例,ECMAScript 标识符采用驼峰大小写格式,也就是第一个字母小写,剩下的每个单词的首字母大写,例如:
  1. firstSecond
  2. myCar
  3. doSomethingImportant
 
虽然没有谁强制要求必须采用这种格式,但为了与 ECMAScript 内置的函数和对象命名格式保持一致,可以将其当作一种最佳实践。
不能把关键字、保留字、 true 、 false 和 null 用作标识符。3.2节将介绍更多相关内容。
 

3.1.3 注释

// 单行注释
 
/*
* 这是一个多行
* (块级)注释
*/
 
虽然上面注释中的第二和第三行都以一个星号开头,但这不是必需的。之所以添加那两个星号,纯粹是为了提高注释的可读性(这种格式在企业级应用中用得比较多)。
 

3.1.4 严格模式

ECMAScript 5 引入了严格模式(strict mode)的概念。严格模式是为 JavaScript 定义了一种不同的解析与执行模型。在严格模式下,ECMAScript 3 中的一些不确定的行为将得到处理,而且对某些不安全的操作也会抛出错误。要在整个脚本中启用严格模式,可以在顶部添加如下代码:
"use strict";
这行代码看起来像是字符串,而且也没有赋值给任何变量,但其实它是一个编译指示(pragma),用于告诉支持的 JavaScript 引擎切换到严格模式。这是为不破坏 ECMAScript 3 语法而特意选定的语法。在函数内部的上方包含这条编译指示,也可以指定函数在严格模式下执行:

 严格模式下,JavaScript 的执行结果会有很大不同。 

3.1.5 语句

ECMAScript 中的语句以一个分号结尾;如果省略分号,则由解析器确定语句的结尾,如下例所示:
var sum = a + b // 即使没有分号也是有效的语句——不推荐
var diff = a - b; // 有效的语句——推荐
 
虽然语句结尾的分号不是必需的,但我们建议任何时候都不要省略它。
 
因为加上这个分号可以避免很多错误(例如不完整的输入),开发人员也可以放心地通过删除多余的空格来压缩 ECMAScript 代码(代码行结尾处没有分号会导致压缩错误)。另外,加上分号也会在某些情况下增进代码的性能,因为这样解析器就不必再花时间推测应该在哪里插入分号了。
 
虽然条件控制语句(如 if 语句)只在执行多条语句的情况下才要求使用代码块,但最佳实践是始终在控制语句中使用代码块——即使代码块中只有一条语句,例如:
在控制语句中使用代码块可以让编码意图更加清晰,而且也能降低修改代码时出错的几率。

3.2 关键字和保留字

按照规则,关键字也是语言保留的,不能用作标识符。以下就是 ECMAScript的全部关键字 带 * 号上标的是第 5 版新增的关键字

第 5 版把在非严格模式下运行时的保留字缩减为下列这些:

 在严格模式下,第 5 版还对以下保留字施加了限制:

除了上面列出的保留字和关键字,ECMA-262 第 5 版对 eval 和 arguments 还施加了限制。在严格模式下,这两个名字也不能作为标识符或属性名,否则会抛出错误。

3.3 变量

ECMAScript 的变量是松散类型的,所谓松散类型就是可以用来保存任何类型的数据。换句话说,每个变量仅仅是一个用于保存值的占位符而已。定义变量时要使用 var 操作符(注意 var 是一个关键字),后跟变量名(即一个标识符),如下所示:
var message;
这行代码定义了一个名为 message 的变量,该变量可以用来保存任何值(像这样未经过初始化的变量,会保存一个特殊的值—— undefined ,相关内容将在 3.4 节讨论)。ECMAScript 也支持直接初始化变量,因此在定义变量的同时就可以设置变量的值
var message = "hi";
 
有一点必须注意,即用 var 操作符定义的变量将成为定义该变量的作用域中的局部变量。也就是说,如果在函数中使用 var 定义一个变量,那么这个变量在函数退出后就会被销毁,例如:

这里,变量 message 是在函数中使用 var 定义的。当函数被调用时,就会创建该变量并为其赋值。而在此之后,这个变量又会立即被销毁,因此例子中的下一行代码就会导致错误。不过,可以像下面这样省略 var 操作符,从而创建一个全局变量:

这个例子省略了 var 操作符,因而 message 就成了全局变量。这样,只要调用过一次 test() 函数,这个变量就有了定义,就可以在函数外部的任何地方被访问到。虽然省略 var 操作符可以定义全局变量,但这也不是我们推荐的做法。因为在局部作用域中定义的全局变量很难维护,而且如果有意地忽略了 var 操作符,也会由于相应变量不会马上就有定义而导致不必要的混乱。给未经声明的变量赋值在严格模式下会导致抛出 ReferenceError 错误。

可以使用一条语句定义多个变量,只要像下面这样把每个变量(初始化或不初始化均可)用逗号分隔开即可:
这个例子定义并初始化了 3 个变量。同样由于 ECMAScript 是松散类型的,因而使用不同类型初始化变量的操作可以放在一条语句中来完成。虽然代码里的换行和变量缩进不是必需的,但这样做可以提高可读性。

 在严格模式下,不能定义名为 eval 或 arguments 的变量,否则会导致语法错误。

3.4 数据类型

5 种简单数据类型(也称为基本数据类型):UndefinedNullBooleanNumberString
1种复杂数据类型—— Object , Object 本质上是由一组无序的名值对组成的。
ECMAScript不支持任何创建自定义类型的机制,而所有值最终都将是上述 6 种数据类型之一。乍一看,好像只有 6种数据类型不足以表示所有数据;但是,由于 ECMAScript 数据类型具有动态性,因此的确没有再定义其他数据类型的必要了。

3.4.1  typeof 操作符

鉴于 ECMAScript 是松散类型的,因此需要有一种手段来检测给定变量的数据类型—— typeof 就是负责提供这方面信息的操作符。对一个值使用 typeof 操作符可能返回下列某个字符串:

  1. "undefined" ——如果这个值未定义;
  2. "boolean" ——如果这个值是布尔值;
  3. "string" ——如果这个值是字符串;
  4. "number" ——如果这个值是数值;
  5. "object" ——如果这个值是对象或 null ;
  6. "function" ——如果这个值是函数。
下面是几个使用 typeof 操作符的例子:
这几个例子说明, typeof 操作符的操作数可以是变量( message ),也可以是数值字面量。注意,typeof 是一个操作符而不是函数,因此例子中的圆括号尽管可以使用,但不是必需的。有些时候, typeof 操作符会返回一些令人迷惑但技术上却正确的值。比如,调用 typeof null会返回 "object" ,因为特殊值 null 被认为是一个空的对象引用。Safari 5 及之前版本、Chrome 7 及之前版本在对正则表达式调用 typeof 操作符时会返回 "function" ,而其他浏览器在这种情况下会返回"object" 。
从技术角度讲,函数在 ECMAScript中是对象,不是一种数据类型。然而,函数也确实有一些特殊的属性,因此通过 typeof 操作符来区分函数和其他对象是有必要的。

3.4.2  Undefined 类型

Undefined 类型只有一个值,即特殊的 undefined 。在使用 var 声明变量但未对其加以初始化时,这个变量的值就是 undefined ,例如:
下面是等价的
未经初始化的值默认就会取得 undefined 值。
 
一般而言,不存在需要显式地把一个变量设置为 undefined 值的情况。字面值undefined 的主要目的是用于比较,而 ECMA-262 第 3 版之前的版本中并没有规定这个值。第 3 版引入这个值是为了正式区分空对象指针与未经初始化的变量。

令人困惑的是:对未初始化的变量执行 typeof 操作符会返回 undefined 值,而对未声明的变量执行 typeof 操作符同样也会返回 undefined 值。来看下面的例子:

 

结果表明,对未初始化和未声明的变量执行 typeof 操作符都返回了 undefined 值;这个结果有其逻辑上的合理性。因为虽然这两种变量从技术角度看有本质区别,但实际上无论对哪种变量也不可能执行真正的操作。

 即便未初始化的变量会自动被赋予 undefined 值,但显式地初始化变量依然是明智的选择。如果能够做到这一点,那么当 typeof 操作符返回 "undefined" 值时,我们就知道被检测的变量还没有被声明,而不是尚未初始化。

3.4.3  Null 类型

Null 类型是第二个只有一个值的数据类型,这个特殊的值是 null 。从逻辑角度来看, null 值表示一个空对象指针,而这也正是使用 typeof 操作符检测 null 值时会返回 "object" 的原因,如下面的例子所示:

只要在意保存对象的变量还没有真正保存对象,就应该明确地让该变量保存 null 值。这样做不仅可以体现 null 作为空对象指针的惯例,而且也有助于进一步区分 null 和 undefined 。 

3.4.4  Boolean 类型

需要注意的是, Boolean 类型的字面值 true 和 false 是区分大小写的。也就是说, True 和 False(以及其他的混合大小写形式)都不是 Boolean 值,只是标识符。要将一个值转换为其对应的 Boolean 值,可以调用转型函数 Boolean() ,如下例所示:
可以对任何数据类型的值调用 Boolean() 函数,而且总会返回一个 Boolean 值。至于返回的这个值是 true 还是 false ,取决于要转换值的数据类型及其实际值。下表给出了各种数据类型及其对应的转换规则。
数据类型          转换为true的值          转换为false的值
Boolean            true                        false
String                任何非空字符串        "" (空字符串)
Number      任何非零数字值(包括无穷大)    0和 NaN (参见本章后面有关NaN的内容)
Object                任何对象                    null
Undefined        n/a①                            undefined
① n/a(或 N/A),是 not applicable的缩写,意思是“不适用”。
 
这些转换规则对理解流控制语句(如 if 语句)自动执行相应的 Boolean 转换非常重要,请看下面的代码:

3.4.5  Number 类型

 鉴于 JavaScript 中保存数值的方式,可以保存正零(+0)和负零(0)。正零和负零被认为相等,但为了读者更好地理解上下文,这里特别做此说明。

1. 浮点数值

所谓浮点数值,就是该数值中必须包含一个小数点,并且小数点后面必须至少有一位数字。
由于保存浮点数值需要的内存空间是保存整数值的两倍,因此 ECMAScript会不失时机地将浮点数值转换为整数值。
如果小数点后面没有跟任何数字,那么这个数值就可以作为整数值来保存。如果浮点数值本身表示的就是一个整数(如 1.0),那么该值也会被转换为整数

 ECMAScript 中 e 表示法的格式也是如此,即前面是一个数值(可以是整数也可以是浮点数),中间是一个大写或小写的字母 E,后面是 10 的幂中的指数,该幂值将用来与前面的数相乘。下面是一个使用 e 表示法表示数值的例子:

 

e 表示法的实际含义就是“3.125 乘以 10 7 ”。

浮点数值的最高精度是 17 位小数,但在进行算术计算时其精确度远远不如整数。例如,0.1 加 0.2的结果不是 0.3,而是 0.30000000000000004。这个小小的舍入误差会导致无法测试特定的浮点数值。

 

 在这个例子中,我们测试的是两个数的和是不是等于 0.3。如果这两个数是 0.05 和 0.25,或者是 0.15和 0.15 都不会有问题。而如前所述,如果这两个数是 0.1 和 0.2,那么测试将无法通过。因此,永远不要测试某个特定的浮点数值。

 关于浮点数值计算会产生舍入误差的问题,有一点需要明确:这是使用基于IEEE754 数值的浮点计算的通病,ECMAScript 并非独此一家;其他使用相同数值格式的语言也存在这个问题。
2. 数值范围
由于内存的限制,ECMAScript 并不能保存世界上所有的数值。ECMAScript 能够表示的最小数值保存在 Number.MIN_VALUE 中——在大多数浏览器中,这个值是 5e-324;
表示的最小数值保存在Number.MIN_VALUE;
表示的最大数值保存在Number.MAX_VALUE;
如果某次计算的结果得到了一个超出 JavaScript 数值范围的值,那么这个数值将被自动转换成特殊的 Infinity 值。具体来说,如果这个数值是负数,则会被转换成 -Infinity (负无穷),如果这个数值是正数,则会被转换成 Infinity (正无穷)。
 
如上所述,如果某次计算返回了正或负的 Infinity 值,那么该值将无法继续参与下一次的计算,因为 Infinity 不是能够参与计算的数值。要想确定一个数值是不是有穷的(换句话说,是不是位于最小和最大的数值之间),可以使用 isFinite() 函数。这个函数在参数位于最小与最大数值之间时会返回 true ,如下面的例子所示:

 

尽管在计算中很少出现某些值超出表示范围的情况,但在执行极小或极大数值的计算时,检测监控这些值是可能的,也是必需的。
访问 Number.NEGATIVE_INFINITY 和 Number.POSITIVE_INFINITY 也可以得到负和正 Infinity 的值。可以想见,这两个属性中分别保存着 -Infinity 和Infinity 。
 3.  NaN
 NaN ,即非数值(Not a Number)是一个特殊的数值,这个数值用于表示一个本来要返回数值的操作数未返回数值的情况(这样就不会抛出错误了)。
例如,在其他编程语言中,任何数值除以 0都会导致错误,从而停止代码执行。但在 ECMAScript中,任何数值除以 0会返回 NaN① ,因此不会影响其他代码的执行。
① 原书如此,但实际上只有 0除以 0 才会返回 NaN,正数除以 0 返回 Infinity,负数除以 0返回-Infinity。
 NaN 本身有两个非同寻常的特点。首先,任何涉及 NaN 的操作(例如 NaN /10)都会返回 NaN ,这个特点在多步计算中有可能导致问题。其次, NaN 与任何值都不相等,包括 NaN 本身。例如,下面的代码会返回 false :
 
isNaN() 函数会帮我们确定这个参数是否“不是数值", isNaN() 在接收到一个值之后,会尝试将这个值转换为数值。某些不是数值的值会直接转换为数值,例如字符串 "10" 或 Boolean 值。而任何不能被转换为数值的值都会导致这个函数返回 true 。请看下面的例子:

 

尽管有点儿不可思议,但 isNaN() 确实也适用于对象。在基于对象调用 isNaN()函数时,会首先调用对象的 valueOf() 方法,然后确定该方法返回的值是否可以转换为数值。如果不能,则基于这个返回值再调用 toString() 方法,再测试返回值。而这个过程也是 ECMAScript中内置函数和操作符的一般执行流程,更详细的内容请参见 3.5 节。

 4. 数值转换

有 3 个函数可以把非数值转换为数值: Number() 、 parseInt() 和 parseFloat() 。第一个函数,即转型函数 Number() 可以用于任何数据类型,而另两个函数则专门用于把字符串转换成数值。这 3个函数对于同样的输入会有返回不同的结果。
Number() 函数的转换规则如下。
  1. 如果是 Boolean 值, true 和 false 将分别被转换为 1 和 0。
  2. 如果是数字值,只是简单的传入和返回。
  3. 如果是 null 值,返回 0。
  4. 如果是 undefined ,返回 NaN 。
  5. 如果是字符串,遵循下列规则:
  6. 如果字符串中只包含数字(包括前面带正号或负号的情况),则将其转换为十进制数值,即 "1"会变成 1, "123" 会变成 123,而 "011" 会变成 11(注意:前导的零被忽略了);
  7. 如果字符串中包含有效的浮点格式,如 "1.1" ,则将其转换为对应的浮点数值(同样,也会忽略前导零);
  8. 如果字符串中包含有效的十六进制格式,例如 "0xf" ,则将其转换为相同大小的十进制整数值;
  9. 如果字符串是空的(不包含任何字符),则将其转换为 0;
  10. 如果字符串中包含除上述格式之外的字符,则将其转换为 NaN 。
  11. 如果是对象,则调用对象的 valueOf() 方法,然后依照前面的规则转换返回的值。如果转换的结果是 NaN ,则调用对象的 toString() 方法,然后再次依照前面的规则转换返回的字符串值。

3.4.6  String 类型

ECMAScript 中的这两种语法形式没有什么区别。用双引号表示的字符串和用单引号表示的字符串完全相同
1. 字符字面量
String 数据类型包含一些特殊的字符字面量,也叫转义序列,用于表示非打印字符,或者具有其他用途的字符。这些字符字面量如下表所示:
 
2. 字符串的特点
ECMAScript 中的字符串是不可变的,也就是说,字符串一旦创建,它们的值就不能改变。要改变某个变量保存的字符串,首先要销毁原来的字符串,然后再用另一个包含新值的字符串填充该变量,例如:
实现这个操作的过程如下:首先创建一个能容纳 10 个字符的新字符串,然后在这个字符串中填充 "Java" 和 "Script" ,最后一步是销毁原来的字符串 "Java" 和字符串 "Script" ,因为这两个字符串已经没用了。这个过程是在后台发生的
3. 转换为字符串

每个字符串也都有一个 toString() 方法,该方法返回字符串的一个副本)都有 toString() 方法。但 null 和 undefined 值没有这个方法。

在调用数值的 toString() 方法时,可以传递一个参数:输出数值的基数。默认情况下, toString() 方法以十进制格式返回数值的字符串表示。而通过传递基数, toString() 可以输出以二进制、八进制、十六进制,乃至其他任意有效进制格式表示的字符串值。

在不知道要转换的值是不是 null 或 undefined 的情况下,还可以使用转型函数 String()这个函数能够将任何类型的值转换为字符串。 String() 函数遵循下列转换规则:
  1. 如果值有 toString() 方法,则调用该方法(没有参数)并返回相应的结果;
  2. 如果值是 null ,则返回 "null" ;
  3. 如果值是 undefined ,则返回 "undefined" 。

这里先后转换了 4 个值:数值、布尔值、 null 和 undefined 。数值和布尔值的转换结果与调用toString() 方法得到的结果相同。因为 null 和 undefined 没有 toString() 方法,所以 String()函数就返回了这两个值的字面量。

 要把某个值转换为字符串,可以使用加号操作符(3.5 节讨论)把它与一个字符串( "" )加在一起。

3.4.7  Object 类型

var o = new Object();
var o = new Object; // 有效,但不推荐省略圆括号

3.5 操作符

3.5.1 一元操作符
只能操作一个值的操作符叫做一元操作符。一元操作符是 ECMAScript 中最简单的操作符。
 
1. 递增和递减操作符
前置操作符:在变量的前面 ++ / --
后置操作符:在变量的后面 ++ / --
 
执行前置递增和递减操作时,变量的值都是在语句被求值以前改变的。(在计算机科学领域,这种情况通常被称作副效应。)

后置型递增和递减操作符的语法不变(仍然分别是 ++ 和 -- ),只不过要放在变量的后面而不是前面。后置递增和递减与前置递增和递减有一个非常重要的区别,即递增和递减操作是在包含它们的语句被求值之后才执行的。

 

这里仅仅将前置递减改成了后置递减,就立即可以看到差别。在前面使用前置递减的例子中, num3和 num4 最后都等于 21。而在这个例子中, num3 等于 22, num4 等于 21。差别的根源在于,这里在计算 num3 时使用了 num1 的原始值(2)完成了加法计算,而 num4 则使用了递减后的值(1)。所有这 4 个操作符对任何值都适用,也就是它们不仅适用于整数,还可以用于字符串、布尔值、浮点数值和对象。在应用于不同的值时,递增和递减操作符遵循下列规则。
在应用于一个包含有效数字字符的字符串时,先将其转换为数字值,再执行加减 1 的操作。字符串变量变成数值变量。
  1. 在应用于一个不包含有效数字字符的字符串时,将变量的值设置为 NaN (第 4 章将详细讨论)。字符串变量变成数值变量。
  2. 在应用于布尔值 false 时,先将其转换为 0 再执行加减 1 的操作。布尔值变量变成数值变量。
  3. 在应用于布尔值 true 时,先将其转换为 1 再执行加减 1 的操作。布尔值变量变成数值变量。
  4. 在应用于浮点数值时,执行加减 1 的操作。
  5. 在应用于对象时,先调用对象的 valueOf() 方法(第 5 章将详细讨论)以取得一个可供操作的值。然后对该值应用前述规则。如果结果是 NaN ,则在调用 toString() 方法后再应用前述规则。对象变量变成数值变量。

 2. 一元加和减操作符

一元加操作符以一个加号(+)表示,放在数值前面,对数值不会产生任何影响。
不过,在对非数值应用一元加操作符时,该操作符会像 Number() 转型函数一样对这个值执行转换。
一元减操作符主要用于表示负数,例如将 1 转换成-1。下面的例子演示了这个简单的转换过程:
在将一元减操作符应用于数值时,该值会变成负数(如上面的例子所示)。而当应用于非数值时,一元减操作符遵循与一元加操作符相同的规则,最后再将得到的数值转换为负数,如下面的例子所示:

一元加和减操作符主要用于基本的算术运算,也可以像前面示例所展示的一样用于转换数据类型。

3.5.2 位操作符

 暂无

3.5.3 布尔操作符

1. 逻辑非
逻辑非操作符由一个叹号(!)表示,可以应用于 ECMAScript 中的任何值。无论这个值是什么数据类型,这个操作符都会返回一个布尔值。逻辑非操作符首先会将它的操作数转换为一个布尔值,然后再对其求反。也就是说,逻辑非操作符遵循下列规则:
  1. 如果操作数是一个对象,返回 false ;
  2. 如果操作数是一个空字符串,返回 true ;
  3. 如果操作数是一个非空字符串,返回 false ;
  4. 如果操作数是数值 0,返回 true ;
  5. 如果操作数是任意非 0 数值(包括 Infinity ),返回 false ;
  6. 如果操作数是 null ,返回 true ;
  7. 如果操作数是 NaN ,返回 true ;
  8. 如果操作数是 undefined ,返回 true 。

逻辑非操作符也可以用于将一个值转换为与其相反的布尔值。而同时使用两个逻辑非操作符,实际上就会模拟 Boolean() 转型函数的行为。其中,第一个逻辑非操作会基于无论什么操作数返回一个布尔值,而第二个逻辑非操作则对该布尔值求反,于是就得到了这个值真正对应的布尔值。当然,最终结果与对这个值使用 Boolean() 函数相同,如下面的例子所示:

 

2. 逻辑与
逻辑与操作符由两个和号( && )表示,有两个操作数,如下面的例子所示:

 

逻辑与操作可以应用于任何类型的操作数,而不仅仅是布尔值。在有一个操作数不是布尔值的情况下,逻辑与操作就不一定返回布尔值;此时,它遵循下列规则:
  1. 如果第一个操作数是对象,则返回第二个操作数;
  2. 如果第二个操作数是对象,则只有在第一个操作数的求值结果为 true 的情况下才会返回该
  3. 对象;
  4. 如果两个操作数都是对象,则返回第二个操作数;
  5. 如果有一个操作数是 null ,则返回 null ;
  6. 如果有一个操作数是 NaN ,则返回 NaN ;
  7. 如果有一个操作数是 undefined ,则返回 undefined 。
逻辑与操作属于短路操作,即如果第一个操作数能够决定结果,那么就不会再对第二个操作数求值。对于逻辑与操作而言,如果第一个操作数是 false ,则无论第二个操作数是什么值,结果都不再可能是true 了。来看下面的例子:
不能在逻辑与操作中使用未定义的值。
如果像下面这个例中一样,将 found 的值设置为 false ,就不会发生错误了:

在这个例子中,警告框会显示出来。无论变量 someUndefinedVariable 有没有定义,也永远不会对它求值,因为第一个操作数的值是 false 。而这也就意味着逻辑与操作的结果必定是 false ,根本用不着再对 && 右侧的操作数求值了。在使用逻辑与操作符时要始终铭记它是一个短路操作符。
3. 逻辑或
逻辑或操作符由两个竖线符号( || )表示,有两个操作数,如下面的例子所示:

 

逻辑或的真值表如下:
与逻辑与操作相似,如果有一个操作数不是布尔值,逻辑或也不一定返回布尔值;此时,它遵循下列规则:
  1. 如果第一个操作数是对象,则返回第一个操作数;
  2. 如果第一个操作数的求值结果为 false ,则返回第二个操作数;
  3. 如果两个操作数都是对象,则返回第一个操作数;
  4. 如果两个操作数都是 null ,则返回 null ;
  5. 如果两个操作数都是 NaN ,则返回 NaN ;
  6. 如果两个操作数都是 undefined ,则返回 undefined 。
与逻辑与操作符相似,逻辑或操作符也是短路操作符。也就是说,如果第一个操作数的求值结果为true ,就不会对第二个操作数求值了。下面看一个例子:
这个例子跟前面的例子一样,变量 someUndefinedVariable 也没有定义。但是,由于变量 found的值是 true ,而变量 someUndefinedVariable 永远不会被求值,因此结果就会输出 "true" 。如果像下面这个例子一样,把 found 的值改为 false ,就会导致错误:

 

我们可以利用逻辑或的这一行为来避免为变量赋 null 或 undefined 值。例如:

 

小总结:逻辑与运算符找false,逻辑真运算符找true;
 
3.5.4 乘性操作符

在操作数为非数值的情况下会执行自动的类型转换。如果参与乘性计算的某个操作数不是数值,后台会先使用 Number() 转型函数将其转换为数值。也就是说,空字符串将被当作0,布尔值 true 将被当作 1。

1. 乘法
在处理特殊值的情况下,乘法操作符遵循下列特殊的规则:
  1. 如果操作数都是数值,执行常规的乘法计算,即两个正数或两个负数相乘的结果还是正数,而如果只有一个操作数有符号,那么结果就是负数。如果乘积超过了 ECMAScript 数值的表示范围,则返回 Infinity 或 -Infinity ;
  2. 如果有一个操作数是 NaN ,则结果是 NaN ;
  3. 如果是 Infinity 与 0 相乘,则结果是 NaN ;
  4. 如果是 Infinity 与非 0 数值相乘,则结果是 Infinity 或 -Infinity ,取决于有符号操作数的符号;
  5. 如果是 Infinity 与 Infinity 相乘,则结果是 Infinity ;
  6. 如果有一个操作数不是数值,则在后台调用 Number() 将其转换为数值,然后再应用上面的规则。
2. 除法
除法操作符由一个斜线符号(/)表示,执行第二个操作数除第一个操作数的计算,如下面的例子所示:
与乘法操作符类似,除法操作符对特殊的值也有特殊的处理规则。这些规则如下:
  1. 与乘法操作符类似,除法操作符对特殊的值也有特殊的处理规则。这些规则如下:
  2. 如果操作数都是数值,执行常规的除法计算,即两个正数或两个负数相除的结果还是正数,而如果只有一个操作数有符号,那么结果就是负数。如果商超过了 ECMAScript 数值的表示范围,则返回 Infinity 或 -Infinity ;
  3. 如果有一个操作数是 NaN ,则结果是 NaN ;
  4. 如果是 Infinity 被 Infinity 除,则结果是 NaN ;
  5. 如果是零被零除,则结果是 NaN ;
  6. 如果是非零的有限数被零除,则结果是 Infinity 或 -Infinity ,取决于有符号操作数的符号;
  7. 如果是 Infinity 被任何非零数值除,则结果是 Infinity 或 -Infinity ,取决于有符号操作数的符号;
  8. 如果有一个操作数不是数值,则在后台调用 Number() 将其转换为数值,然后再应用上面的规则。

 3. 求模

求模(余数)操作符由一个百分号( % )表示,用法如下:

 与另外两个乘性操作符类似,求模操作符会遵循下列特殊规则来处理特殊的值:

  1. 与另外两个乘性操作符类似,求模操作符会遵循下列特殊规则来处理特殊的值:
  2. 如果操作数都是数值,执行常规的除法计算,返回除得的余数;
  3. 如果被除数是无穷大值而除数是有限大的数值,则结果是 NaN ;
  4. 如果被除数是有限大的数值而除数是零,则结果是 NaN ;
  5. 如果是 Infinity 被 Infinity 除,则结果是 NaN ;
  6. 如果被除数是有限大的数值而除数是无穷大的数值,则结果是被除数;
  7. 如果被除数是零,则结果是零;
  8. 如果有一个操作数不是数值,则在后台调用 Number() 将其转换为数值,然后再应用上面的规则。
3.5.5 加性操作符
与乘性操作符类似,加性操作符也会在后台转换不同的数据类型。然而,对于加性操作符而言,相应的转换规则还稍微有点复杂。
1. 加法
加法操作符(+)的用法如下所示:
如果两个操作符都是数值,执行常规的加法计算,然后根据下列规则返回结果:
  1. 如果两个操作符都是数值,执行常规的加法计算,然后根据下列规则返回结果:
  2. 如果有一个操作数是 NaN ,则结果是 NaN ;
  3. 如果是 Infinity 加 Infinity ,则结果是 Infinity ;
  4. 如果是 -Infinity 加 -Infinity ,则结果是 -Infinity ;
  5. 如果是 Infinity 加 -Infinity ,则结果是 NaN ;
  6. 如果是+0 加+0,则结果是+0;
  7. 如果是-0 加-0,则结果是-0;
  8.  如果是+0 加-0,则结果是+0。不过,如果有一个操作数是字符串,那么就要应用如下规则:
  9. 如果两个操作数都是字符串,则将第二个操作数与第一个操作数拼接起来;
  10. 如果只有一个操作数是字符串,则将另一个操作数转换为字符串,然后再将两个字符串拼接起来。
  11. 如果有一个操作数是对象、数值或布尔值,则调用它们的 toString() 方法取得相应的字符串值,然后再应用前面关于字符串的规则。对于 undefined 和 null ,则分别调用 String() 函数并取得字符串 "undefined" 和 "null" 。

如果将一个操作数改为字符串 "5" ,结果就变成了 "55" (字符串值),因为第一个操作数也被转换成了 "5" 。
忽视加法操作中的数据类型是 ECMAScript 编程中最常见的一个错误。再来看一个例子:
在这个例子中,变量 message 的值是执行两个加法操作之后的结果。有人可能以为最后得到的字符串是 "The sum of 5 and 10 is 15" ,但实际的结果却是 "The sum of 5 and 10 is 510" 。之所以会这样,是因为每个加法操作是独立执行的。第一个加法操作将一个字符串和一个数值(5)拼接了起来,结果是一个字符串。而第二个加法操作又用这个字符串去加另一个数值(10),当然也会得到一个字符串。如果想先对数值执行算术计算,然后再将结果与字符串拼接起来,应该像下面这样使用圆括号:

 

一对圆括号把两个数值变量括在了一起,这样就会告诉解析器先计算其结果,然后再将结果与字符串拼接起来。因此,就得到了结果 "The sum of 5 and 10 is 15" 。
2. 减法

 减法操作符(-)是另一个极为常用的操作符,其用法如下所示:

与加法操作符类似,ECMAScript 中的减法操作符在处理各种数据类型转换时,同样需要遵循一些特殊规则,如下所示:
  1. 如果两个操作符都是数值,则执行常规的算术减法操作并返回结果;
  2. 如果有一个操作数是 NaN ,则结果是 NaN ;
  3. 如果是 Infinity 减 Infinity ,则结果是 NaN ;
  4. 如果是 -Infinity 减 -Infinity ,则结果是 NaN ;
  5. 如果是 Infinity 减 -Infinity ,则结果是 Infinity ;
  6. 如果是 -Infinity 减 Infinity ,则结果是 -Infinity ;
  7. 如果是+0 减+0,则结果是+0;
  8. 如果是+0 减0,则结果是0;
  9. 如果是0 减0,则结果是+0;
  10. 如果有一个操作数是字符串、布尔值、 null 或 undefined ,则先在后台调用 Number() 函数将其转换为数值,然后再根据前面的规则执行减法计算。如果转换的结果是 NaN ,则减法的结果就是 NaN ;
  11. 如果有一个操作数是对象,则调用对象的 valueOf() 方法以取得表示该对象的数值。如果得到的值是 NaN ,则减法的结果就是 NaN 。如果对象没有 valueOf() 方法,则调用其 toString()方法并将得到的字符串转换为数值。

 
3.5.6 关系操作符
小于(<)、大于(>)、小于等于(<=)和大于等于(>=)这几个关系操作符用于对两个值进行比较,比较的规则与我们在数学课上所学的一样。这几个操作符都返回一个布尔值,如下面的例子所示:
与 ECMAScript中的其他操作符一样,当关系操作符的操作数使用了非数值时,也要进行数据转换或完成某些奇怪的操作。以下就是相应的规则。
  1. 如果两个操作数都是数值,则执行数值比较。
  2. 如果两个操作数都是字符串,则比较两个字符串对应的字符编码值。
  3. 如果一个操作数是数值,则将另一个操作数转换为一个数值,然后执行数值比较。
  4. 如果一个操作数是对象,则调用这个对象的 valueOf() 方法,用得到的结果按照前面的规则执行比较。如果对象没有 valueOf() 方法,则调用 toString() 方法,并用得到的结果根据前面的规则执行比较。
  5. 如果一个操作数是布尔值,则先将其转换为数值,然后再执行比较。
在比较字符串时,实际比较的是两个字符串中对应位置的每个字符的字符编码值。经过这么一番比较之后,再返回一个布尔值。由于大写字母的字符编码全部小于小写字母的字符编码,因此我们就会看到如下所示的奇怪现象:
在这个例子中,字符串 "Brick" 被认为小于字符串 "alphabet" 。原因是字母 B 的字符编码为 66,而字母 a 的字符编码是 97。如果要真正按字母表顺序比较字符串,就必须把两个操作数转换为相同的大小写形式(全部大写或全部小写),然后再执行比较,如下所示:
另一种奇怪的现象发生在比较两个数字字符串的情况下,比如下面这个例子:
这是因为两个操作数都是字符串,而字符串比较的是字符编码( "2" 的字符编码是 50,而 "3" 的字符编码是 51)。
如果像下面例子中一样,将一个操作数改为数值,比较的结果就正常了:
此时,字符串 "23" 会被转换成数值 23,然后再与 3 进行比较,因此就会得到合理的结果。在比较数值和字符串时,字符串都会被转换成数值,然后再以数值方式与另一个数值比较。
任何操作数与 NaN 进行关系比较,结果都是 false 。于是,就出现了下面这个有意思的现象:
按照常理,如果一个值不小于另一个值,则一定是大于或等于那个值。然而,在与 NaN 进行比较时,这两个比较操作的结果都返回了 false 。
 
3.5.7 相等操作符
相等和不相等——先转换再比较,全等和不全等——仅比较而不转换。
1. 相等和不相等
相等操作符由两个等于号( == )表示,如果两个操作数相等,则返回 true 。而不相等操作符由叹号后跟等于号( != )表示,如果两个操作数不相等,则返回 true 。
在转换不同的数据类型时,相等和不相等操作符遵循下列基本规则:
  1. 如果有一个操作数是布尔值,则在比较相等性之前先将其转换为数值—— false 转换为 0,而true 转换为 1;
  2. 如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为数值;
  3. 如果一个操作数是对象,另一个操作数不是,则调用对象的 valueOf() 方法,用得到的基本类型值按照前面的规则进行比较;
这两个操作符在进行比较时则要遵循下列规则。
  1. null 和 undefined 是相等的。
  2. 要比较相等性之前,不能将 null 和 undefined 转换成其他任何值。
  3. 如果有一个操作数是 NaN ,则相等操作符返回 false ,而不相等操作符返回 true 。重要提示:即使两个操作数都是 NaN ,相等操作符也返回 false ;因为按照规则, NaN 不等于 NaN 。
  4. 如果两个操作数都是对象,则比较它们是不是同一个对象。如果两个操作数都指向同一个对象,则相等操作符返回 true ;否则,返回 false 。

2. 全等和不全等
除了在比较之前不转换操作数之外,全等和不全等操作符与相等和不相等操作符没有什么区别。全等操作符由 3 个等于号( === )表示,它只在两个操作数未经转换就相等的情况下返回 true ,如下面的例子所示:

 

不全等操作符由一个叹号后跟两个等于号( !== )表示,它在两个操作数未经转换就不相等的情况下返回 true 。例如:

 

记住: null == undefined 会返回 true ,因为它们是类似的值;但 null === undefined 会返回 false ,因为它们是不同类型的值。
由于相等和不相等操作符存在类型转换问题,而为了保持代码中数据类型的完整性,我们推荐使用全等和不全等操作符。
 
3.5.8 条件操作符(三元表达式)
本质上,这行代码的含义就是基于对 boolean_expression 求值的结果,决定给变量 variable赋什么值。如果求值结果为 true ,则给变量 variable 赋 true_value 值;如果求值结果为 false ,则给变量 variable 赋 false_value 值。再看一个例子:
在这个例子中, max 中将会保存一个最大的值。这个表达式的意思是:如果 num1 大于 num2 (关系表达式返回 true ),则将 num1 的值赋给 max ;如果 num1 小于或等于 num2 (关系表达式返回 false ),则将 num2 的值赋给 max 。
 
3.5.9 赋值操作符
简单的赋值操作符由等于号( = )表示,其作用就是把右侧的值赋给左侧的变量,如下面的例子所示:

 

每个主要算术操作符(以及个别的其他操作符)都有对应的复合赋值操作符。这些操作符如下所示:
  1. 乘/赋值( *= );
  2. 除/赋值( /= );
  3. 模/赋值( %= );
  4. 加/赋值( += );
  5. 减/赋值(- = );
  6. 左移/赋值( <<= );
  7. 有符号右移/赋值( >>= );
  8. 无符号右移/赋值( >>>= )。
设计这些操作符的主要目的就是简化赋值操作。使用它们不会带来任何性能的提升。
3.5.10 逗号操作符
使用逗号操作符可以在一条语句中执行多个操作,如下面的例子所示:
逗号操作符多用于声明多个变量;但除此之外,逗号操作符还可以用于赋值在用于赋值时逗号操作符总会返回表达式中的最后一项,如下面的例子所示:
由于 0 是表达式中的最后一项,因此 num 的值就是 0。虽然逗号的这种使用方式并不常见,但这个例子可以帮我们理解逗号的这种行为。
 

3.6 语句

语句可以很简单,例如通知函数退出;也可以比较复杂,例如指定重复执行某个命令的次数。
3.6.1  if 语句
其中的 condition(条件)可以是任意表达式;而且对这个表达式求值的结果不一定是布尔值。ECMAScript 会自动调用 Boolean() 转换函数将这个表达式的结果转换为一个布尔值。如果对 condition求值的结果是 true ,则执行statement1 (语句1),如果对condition求值的结果是 false ,则执行statement2(语句 2)。而且这两个语句既可以是一行代码,也可以是一个代码块(以一对花括号括起来的多行代码)。请看下面的例子。

 

不过,业界普遍推崇的最佳实践是始终使用代码块,即使要执行的只有一行代码。因为这样可以消除人们的误解,否则可能让人分不清在不同条件下要执行哪些语句。
 
3.6.2  do-while 语句
do-while 语句是一种后测试循环语句,即只有在循环体中的代码执行之后,才会测试出口条件。换句话说,在对条件表达式求值之前,循环体内的代码至少会被执行一次。以下是 do-while 语句的语法:
也就是说do-while语句会先执行循环体内的代码一次再判断条件是否成立,如果成立继续执行,否则不执行。
在这个例子中,只要变量 i 的值小于 10,循环就会一直继续下去。而且变量 i 的值最初为 0,每次循环都会递增 2。
像 do-while 这种后测试循环语句最常用于循环体中的代码至少要被执行一次的情形。
 
3.6.3  while 语句
while 语句属于前测试循环语句,也就是说,在循环体内的代码被执行之前,就会对出口条件求值。因此,循环体内的代码有可能永远不会被执行。以下是 while 语句的语法:
下面是一个示例:
 
3.6.4  for 语句
for 语句也是一种前测试循环语句,但它具有在执行循环之前初始化变量和定义循环后要执行的代码的能力。以下是 for 语句的语法:
以上代码定义了变量 i 的初始值为 0。只有当条件表达式( i<count )返回 true 的情况下才会进入 for 循环,因此也有可能不会执行循环体中的代码。如果执行了循环体中的代码,则一定会对循环后的表达式( i++ )求值,即递增 i 的值。这个 for 循环语句与下面的 while 语句的功能相同:
使用 while 循环做不到的,使用 for 循环同样也做不到。也就是说, for 循环只是把与循环有关的代码集中在了一个位置。
有必要指出的是,在 for 循环的变量初始化表达式中,也可以不使用 var 关键字。该变量的初始化可以在外部执行,例如:
由于 ECMAScript 中不存在块级作用域(第 4 章将进一步讨论这一点),因此在循环内部定义的变量也可以在外部访问到。例如:
此外, for 语句中的初始化表达式、控制表达式和循环后表达式都是可选的。将这三个表达式全部省略,就会创建一个无限循环,例如:
3.6.5  for-in 语句
for-in 语句是一种精准的迭代语句,可以用来枚举对象的属性。以下是 for-in 语句的语法:

 

在这个例子中,我们使用 for-in 循环来显示了 BOM 中 window 对象的所有属性。每次执行循环时,都会将 window 对象中存在的一个属性名赋值给变量 propName 。这个过程会一直持续到对象中的所有属性都被枚举一遍为止。与 for 语句类似,这里控制语句中的 var 操作符也不是必需的。但是,为了保证使用局部变量,我们推荐上面例子中的这种做法。
ECMAScript 对象的属性没有顺序。因此,通过 for-in 循环输出的属性名的顺序是不可预测的。具体来讲,所有属性都会被返回一次,但返回的先后次序可能会因浏览器而异。
 
但是,如果表示要迭代的对象的变量值为 null 或 undefined , for-in 语句会抛出错误。ECMAScript 5 更正了这一行为;对这种情况不再抛出错误,而只是不执行循环体。为了保证最大限度的兼容性,建议在使用 for-in 循环之前,先检测确认该对象的值不是 null 或 undefined 。

 3.6.6  label 语句

使用 label 语句可以在代码中添加标签,以便将来使用。以下是 label 语句的语法:
3.6.7  break 和 continue 语句
break 和 continue 语句用于在循环中精确地控制代码的执行。其中, break 语句会立即退出循环,强制继续执行循环后面的语句。而 continue 语句虽然也是立即退出循环,但退出循环后会从循环的顶部继续执行。请看下面的例子:
break 和 continue 语句都可以与 label 语句联合使用,从而返回代码中特定的位置。这种联合使用的情况多发生在循环嵌套的情况下,如下面的例子所示:
在这个例子中, outermost 标签表示外部的 for 语句。如果每个循环正常执行 10 次,则 num++语句就会正常执行 100 次。换句话说,如果两个循环都自然结束, num 的值应该是 100。但内部循环中的 break 语句带了一个参数:要返回到的标签。添加这个标签的结果将导致 break 语句不仅会退出内部的 for 语句(即使用变量 j 的循环),而且也会退出外部的 for 语句(即使用变量 i 的循环)。为此,当变量 i 和 j 都等于 5 时, num 的值正好是 55。同样, continue 语句也可以像这样与 label 语句联用,如下面的例子所示:

在这种情况下, continue 语句会强制继续执行循环——退出内部循环,执行外部循环。当 j 是 5时, continue 语句执行,而这也就意味着内部循环少执行了 5 次,因此 num 的结果是 95。虽然联用 break 、 continue 和 label 语句能够执行复杂的操作,但如果使用过度,也会给调试带来麻烦。在此,我们建议如果使用 label 语句,一定要使用描述性的标签,同时不要嵌套过多的循环。
3.6.8  with 语句
with 语句的作用是将代码的作用域设置到一个特定的对象中。 with 语句的语法如下:
定义 with 语句的目的主要是为了简化多次编写同一个对象的工作,如下面的例子所示:
上面几行代码都包含 location 对象。如果使用 with 语句,可以把上面的代码改写成如下所示:

 

在这个重写后的例子中,使用 with 语句关联了 location 对象。这意味着在 with 语句的代码块内部,每个变量首先被认为是一个局部变量,而如果在局部环境中找不到该变量的定义,就会查询location 对象中是否有同名的属性。如果发现了同名属性,则以 location 对象属性的值作为变量的值。严格模式下不允许使用 with 语句,否则将视为语法错误。
由于大量使用 with 语句会导致性能下降,同时也会给调试代码造成困难,因此在开发大型应用程序时,不建议使用 with 语句。
 
3.6.9  switch 语句
switch 语句中的每一种情形(case)的含义是:“如果表达式等于这个值(value),则执行后面的语句(statement)”。而 break 关键字会导致代码执行流跳出 switch 语句。如果省略 break 关键字,就会导致执行完当前 case 后,继续执行下一个 case。最后的 default 关键字则用于在表达式不匹配前面任何一种情形的时候,执行机动代码(因此,也相当于一个 else 语句)。
从根本上讲, switch 语句就是为了让开发人员免于编写像下面这样的代码:
虽然 ECMAScript 中的 switch 语句借鉴自其他语言,但这个语句也有自己的特色。首先,可以在switch 语句中使用任何数据类型(在很多其他语言中只能使用数值),无论是字符串,还是对象都没有问题。其次,每个 case 的值不一定是常量,可以是变量,甚至是表达式。
使用表达式作为 case 值还可以实现下列操作:
这个例子首先在 switch 语句外面声明了变量 num 。而之所以给 switch 语句传递表达式 true ,是因为每个 case 值都可以返回一个布尔值。这样,每个 case 按照顺序被求值,直到找到匹配的值或者遇到 default 语句为止(这正是这个例子的最终结果)。
switch 语句在比较值时使用的是全等操作符,因此不会发生类型转换(例如,字符串 "10" 不等于数值 10)。

 3.7 函数

函数对任何语言来说都是一个核心的概念。通过函数可以封装任意多条语句,而且可以在任何地方、任何时候调用执行。ECMAScript 中的函数使用 function 关键字来声明,后跟一组参数以及函数体。函数的基本语法如下所示:
以下是一个函数示例:

 

调用 sayHi() 函数的代码如下所示:
ECMAScript 中的函数在定义时不必指定是否返回值。实际上,任何函数在任何时候都可以通过return 语句后跟要返回的值来实现返回值。请看下面的例子:

这个函数会在执行完 return 语句之后停止并立即退出。因此,位于 return 语句之后的任何代码都永远不会执行。例如:
一个函数中也可以包含多个 return 语句,如下面这个例子中所示:
另外, return 语句也可以不带有任何返回值。在这种情况下,函数在停止执行后将返回 undefined值。这种用法一般用在需要提前停止函数执行而又不需要返回值的情况下。
推荐的做法是要么让函数始终都返回一个值,要么永远都不要返回值。否则,如果函数有时候返回值,有时候又不返回值,会给调试代码带来不便。
3.7.1 理解参数
ECMAScript 函数不介意传递进来多少个参数,也不在乎传进来参数是什么数据类型。
原因是 ECMAScript 中的参数在内部是用一个数组来表示的。函数接收到的始终都是这个数组,而不关心数组中包含哪些参数(如果有参数的话)。如果这个数组中不包含任何元素,无所谓;如果包含多个元素,也没有问题。实际上,在函数体内可以通过 arguments 对象来访问这个参数数组,从而获取传递给函数的每一个参数。
 
其实, arguments 对象只是与数组类似它并不是 Array 的实例),因为可以使用方括号语法访问它的每一个元素(即第一个元素是 arguments[0] ,第二个元素是 argumetns[1] ,以此类推),使用 length 属性来确定传递进来多少个参数。在前面的例子中, sayHi() 函数的第一个参数的名字叫name ,而该参数的值也可以通过访问 arguments[0] 来获取。因此,那个函数也可以像下面这样重写,即不显式地使用命名参数:
通过访问 arguments 对象的 length 属性可以获知有多少个参数传递给了函数。下面这个函数会在每次被调用时,输出传入其中的参数个数:
由此可见,开发人员可以利用这一点让函数能够接收任意个参数并分别实现适当的功能。请看下面的例子:
另一个与参数相关的重要方面,就是 arguments 对象可以与命名参数一起使用,如下面的例子所示:
在重写后的这个 doAdd() 函数中,两个命名参数都与 arguments 对象一起使用。由于 num1 的值与 arguments[0] 的值相同,因此它们可以互换使用(当然, num2 和 arguments[1] 也是如此)。
关于参数还要记住最后一点:没有传递值的命名参数将自动被赋予 undefined 值。这就跟定义了变量但又没有初始化一样。例如,如果只给 doAdd() 函数传递了一个参数,则 num2 中就会保存undefined 值。严格模式对如何使用 arguments 对象做出了一些限制。首先,像前面例子中那样的赋值会变得无效。也就是说,即使把 arguments[1] 设置为 10 , num2 的值仍然还是 undefined 。其次,重写arguments 的值会导致语法错误(代码将不会执行)。
ECMAScript 中的所有参数传递的都是值,不可能通过引用传递参数。
 
3.7.2 没有重载
ECMAScript 函数不能像传统意义上那样实现重载。而在其他语言(如 Java)中,可以为一个函数编写两个定义,只要这两个定义的签名(接受的参数的类型和数量)不同即可。如前所述,ECMAScirpt函数没有签名,因为其参数是由包含零或多个值的数组来表示的。而没有函数签名,真正的重载是不可能做到的。
如果在 ECMAScript中定义了两个名字相同的函数,则该名字只属于后定义的函数。请看下面的例子:
在此,函数 addSomeNumber() 被定义了两次。第一个版本给参数加 100,而第二个版本给参数加200。由于后定义的函数覆盖了先定义的函数,因此当在最后一行代码中调用这个函数时,返回的结果就是 300。

3.8 小结

  1. ECMAScript 中的基本数据类型包括 Undefined 、 Null 、 Boolean 、 Number 和 String 。
  2. 与其他语言不同,ECMScript 没有为整数和浮点数值分别定义不同的数据类型, Number 类型可用于表示所有数值。
  3. ECMAScript 中也有一种复杂的数据类型,即 Object 类型,该类型是这门语言中所有对象的基础类型。
  4. 严格模式为这门语言中容易出错的地方施加了限制。
  5. ECMAScript 提供了很多与 C 及其他类 C 语言中相同的基本操作符,包括算术操作符、布尔操作符、关系操作符、相等操作符及赋值操作符等。
  6. ECMAScript 从其他语言中借鉴了很多流控制语句,例如 if 语句、 for 语句和 switch 语句等。ECMAScript 中的函数与其他语言中的函数有诸多不同之处。
  7. 无须指定函数的返回值,因为任何 ECMAScript 函数都可以在任何时候返回任何值。
  8. 实际上,未指定返回值的函数返回的是一个特殊的 undefined 值。
  9. ECMAScript 中也没有函数签名的概念,因为其函数参数是以一个包含零或多个值的数组的形式传递的。
  10. 可以向 ECMAScript 函数传递任意数量的参数,并且可以通过 arguments 对象来访问这些参数。
  11. 由于不存在函数签名的特性,ECMAScript 函数不能重载。

 

原文地址:https://www.cnblogs.com/sauronblog/p/12009163.html