JavaScript 高级程序设计 - 基础概念篇

JavaScript 简介

JavaScript 诞生于 1995 年,当时, 它的主要目的是处理以前由服务器端语言负责的一些输入验证操作。而今,JavaScript 已经从一个简单的输入验证器发展成为一门强大的编程语言:功能全面,能够处理复杂的计算和交互,拥有了闭包、匿名函数,甚至元编程等特性。

虽然 JavaScript 和 ECMAScript 通常被人们用来表达相同的含义,但 JavaScript 的含义却又要多得多。一个完整的 JavaScript 实例由 ECMAScript、DOM 和 BOM 组成。

ECMAScript

ECMAScript-262 定义的 ECMAScript 与 Web 浏览器没有依赖关系,它定义的只是这门语言的基础,而在此基础上可以构建更完善的脚本语言。常见的 Web 浏览器只是 ECMAScript 实现可能的宿主环境之一 : 宿主环境提供基本的 ECMAScript 实现,同时也会提供该语言的扩展,以便语言与环境之间对接交互。

文档对象模型 DOM

文档对象模型(DOM,Document Object Model)是针对 XML 但经过扩展用于 HTML 的应用程序编程接口。DOM 把整个页面映射为一个多层节点结构。即 HTML 或 XML 页面中的每个组成部分都是某种类型的节点,这些节点又包含着不同类型的数据。而开发人员通过 DOM 提供的 API 可以轻松自如的删除、添加或修改任何节点。

浏览器对象模型 BOM

浏览器对象模型使开发人员可以控制浏览器显示的页面以外的部分。从根本上讲,BOM 只处理浏览器窗口和框架,但是人们习惯上也把所有针对浏览器的 JavaScript· 扩展算作 BOM 的一部分。

在 HTML 中使用 JavaScript

向 HTML 页面中插入 JavaScript 的主要方法,就是使用 <script> 元素。这个元素由 Netscape 创造并在 Netscape Navigator 2 中首先实现。后来,这个元素被加入到正式的 HTML 规范中。HTML 4.01 为 <script> 定义了6个属性

  • async : 表示应该立即下载脚本,但不应妨碍页面中的其它操作。这是一个可选属性,只对外部脚本文件有效。
  • charset : 可选。表示通过 src 属性指定的代码的字符集。
  • defer : 可选。表示脚本可以延迟到文档完全被解析和显示之后再执行。只对外部脚本文件有效。
  • language : 已废弃。原来用于表示编写代码使用的脚本语言。
  • src :可选。表示包含要执行代码的外部文件。
  • type : 可选。可以看成是 language 的替代属性;表示编写代码使用的脚本语言的内容类型(MIME 类型)。虽然 text/javscripttext/ecmascript 都已经不被推荐使用,但人们一直以来使用的都还是 text/javascript 。实际上,服务器在传送 JavaScript 文件时使用的是 MIME 类型通常是 application/x-javascript , 但在 type 中设置这个值却可能导致脚本被忽略。考虑到约定俗成和最大限度的浏览器兼容性,目前 type 属性的默认值依旧还是 text/javascript

使用 <script> 元素的方式有两种 :直接在页面中嵌入 JavaScript 代码和包含外部 JavaScript 文件。

在使用 <script> 元素嵌入 JavaScript 代码时,只需为 <script> 指定 type 属性,然后直接把 JavaScript 代码放在元素内部即可。

1
2
3
<script>
// JavaScript Code
</script>

包含在 <script> 元素内部的 JavaScript 代码将被从上到下依次解释。在解释器对 <scirpt> 元素内部的所有代码求值完毕以前,页面中的其余内容都不会被浏览器加载或显示。

如果要通过 <script> 元素来包含外部 JavaScript 文件,那么 src 属性就是必须的。这个属性的值是一个指向外部 JavaScript 文件的链接。

1
<script type="text/javascript src="example.js"></script>

与解析嵌入式 JavaScript 代码一样,在解析外部 JavaScript 文件(包括下载该文件)时,页面的处理也会暂时停止。

无论如何包含代码,只要不存在 deferasync 属性,浏览器都会按照 <script> 元素在页面中出现的先后顺序对它们依次进行解析。

因此,在包含较多外部 JavaScript 文件的 HTML 页面中,将 <script> 标签放在 <head> 元素中,可能会造成严重的延迟。一般通过将全部 JavaScript 引用放在 <body> 元素中页面内容的后面。

在 HTML 中嵌入 JavaScript 代码虽然没有问题,但一般认为最好的做法还是尽可能使用外部文件来包含 JavaScript 代码。

基本概念

任何语言的核心都必然会描述这门语言最基本的工作原理,而描述的内容通常都要涉及这门语言的语法、操作符、数据类型、内置功能等用于构建复杂解决方案的基本概念。

语法

ECMAScript 的语法大量借鉴了 C 及其它类 C 语言的语法。

区分大小写

ECMAScript 中的一切都区分大小写(变量、函数名和操作符)。

标识符

所谓标识符,就是指变量、函数、属性的名字,或者函数的参数。标识符的组合规则如下:

  • 第一个字符必须是一个字母、下划线或一个美元符号;
  • 其它字符可以是字母、下划线、美元符号或数字。

按照惯例,ECMAScript 标识符采用驼峰大小写格式,也就是第一个字母小写,剩下的每个单词的首字母大写。

严格模式

ECMAScript 5 引入了严格模式的概念。严格模式是为 JavaScript 定义了一种不同的解析与执行模型。

1
"use strict";	// 这是一个编译指示,用于告诉支持的 JavaScript 引擎切换到严格模式

语句

ECMAScript 中的语句以一个分号结尾;如果省略分号,则由解析器确定语句的末尾。虽然语句末尾的分号不是必须的,但建议任何时候都不要省略它。

关键字和保留字

ECMAScript 描述了一组具有特定用途的关键字,这些关键字可用于表示控制语句的开始或结束,或者用于执行特定操作,它们不能用作标识符。 ECMAScript 还描述了一组将来可能被用作关键字的保留字。

变量

ECMAScript 的变量是松散类型的,所谓松散类型就死可以用来保存任何类型的数据。即每个变量仅仅是一个用于保存值得占位符。未被初始化的变量会保存一个特殊的值 :undefined 。ECMAScript 也支持直接初始化变量。

修改变量的值得同时也可以同时修改值得类型,虽然这样做是可行而且有效的,但是并不推荐。

如果定义变量时省略了 var 操作符,变量将会被定义为全局变量。给未经声明的变量赋值在严格模式下会导致抛出 ReferenceError 错误。

数据类型

ECMAScript 中有 5 中简单数据类型 :Undefined、Null、Boolean、Number 和 String。还有一种复杂数据类型 - Object。ECMAScript 不支持任何创建自定义类型的机制,所有值最终都将是上述 6 种数据类型之一。

typeof 操作符

ECMAScript 是松散类型的,因此需要有一种手段来监测给定变量的数据类型,使用 typeof 可以完成这个目的。

  • “undefined” : 未定义
  • “boolean” :布尔值
  • “string” :字符串
  • “number” :数值
  • “object” : 对象或 null
  • “function” : 函数

Undefined 类型

如果使用 var 声明变量但未对其加以初始化时,这个变量的值就是 undefined

值得注意的是,对未初始化的变量执行 typeof 操作符会返回 undefined 值,而对未声明的变量执行 typeof 同样也会返回 undefined 值。这两种变量虽然从技术角度上看有本质区别,但实际上无论对哪种变量也不可能执行真正的操作。

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

Null 类型

从逻辑角度来看,null 值表示一个空对象指针,而这也正是使用 typeof 操作符检测 null 值时会返回 “object” 的原因。

如果定义的变量准备在将来用于保存对象,那么最好将该变量初始化为 null 而不是其它值。这样一来,只要直接检查 null 值就可以知道相应的变量是否已经保存了一个对象对的引用。

实际上,undefined 是派生自 null 值得,因此规定它们的相等性测试返回 true

Boolean 类型

ECMAScript 中的 Boolean 类型只有两个字面值 :truefalse 。这两个值与数字值不是一回事,因此 true 不一定等于 1,而 false 也不一定等于 0 。

虽然 Boolean 类型的字面值只有两个,但 ECMAScript 中所有类型的值都有与这两个 Boolean 值等价的值。使用转型函数 Boolean() 可以将一个值转换为其对应的 Boolean 值。

数据类型 转换为 true 的值 转换为 false 的值
Boolean true false
String 任何非空字符串 “”(空字符串)
Number 任何非零数字值 0 和 NaN
Object 任何对象 null
Undefined n/a undefined

Number 类型

ECMAScript 中的 Number 类型使用 IEEE754 格式来表示整数和浮点数值。为了支持各种数值类型,ECMA 定义了不同的数值字面量格式。

1
2
3
var intNum = 55;	// 整数
var octalNum = 070; // 八进制整数 56
var hexNum = 0xA; // 十六进制整数 10

需要注意的是,八进制字面量在严格模式下是无效的,会导致支持的 JavaScript 引擎抛出错误。

在进行算数运算时,所有以八进制和十六进制表示的数值最终都将被转换为十进制数值。

浮点数值

保存浮点数值需要的内存空间是保存整数值的两倍,ECMAScript 会不失时机的将浮点数值转换为整数值。

对于那些极大或极小的数值,可以用科学表示法表示的浮点数值表示。

1
var floatNum = 3.14e7;	// 等价于 3.14 * 10^7

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

数值范围

由于内存的限制,ECMAScript 并不能保存所有的数值。它能够表示的最小数值为 Number.MIN_VALUE ,在大多数浏览器中,这个值是 5e-234 。能够表示的最大数值为 Number.MAX_VALUE ,在大多数浏览器中,这个值是 1.976931348623157e+308 。如果某次计算的结果得到了一个 超出 JavaScript 数值范围的值,那么这个数值将被自动转换为特殊的 Infinity 值。如果某次计算返回了正或负的 Infinity 值,那么该值将无法继续参与下一次的计算,因为 Infinity 不是能够参与计算的数值。

可以使用 isFinite() 函数确定一个数值是不是无穷的。

NaN

NaN,即非数值,它是一个特殊的数值,这个数值用于表示一个本来要返回数值的操作数未返回数值的情况。比如在 ECMAScript 中,0 除以 0 会返回 NaN,正数除以 0 会返回 Infinity ,负数除以 0 返回 -Infinity

NaN 本省有两个非同寻常的特点。

首先,任何涉及 NaN 的操作都会返回 NaN,这个特点在多步计算中可能导致问题。

其次,NaN 与任何值都不相等,包括 NaN 本身。

针对 NaN 的特点,ECMAScript 定义了 isNaN() 函数。这个函数接受一个参数,该参数可以是任何类型,而函数会帮我们确定这个参数是否 “不是数值”。 isNaN() 在接收到一个值后,会尝试将这个值转换为数值。某些不是数值的值会直接转换为数值,而任何不能被转换为数值的值都会导致这个函数返回 true

数值转换

有三个函数可以将非数值转换为数值 :Number() , paresInt() , parseFloat()Number() 可以用于任何数据类型,而 parseInt() , parseFloat() 则用于把字符串转换成数值。

Number() 函数的转换规则如下 :

  • truefalse 将分别转换为 1 和 0
  • null 返回 0
  • undefined 返回 NaN
  • 如果是字符串,则
    • 如果只包含数字,返回十进制数值
    • 如果为十六进制格式,返回相同大小的十进制数值
    • 如果是空字符串,则返回 0
    • 如果包含除上述格式之外的字符,返回 NaN

由于 Number() 函数在转换字符串时比较复杂而且不够合理,因此处理整数的时候更常用的是 parseInt() 函数。它会忽略字符串前面的空格,直到找到第一个非空格字符。如果第一个字符不是数字字符或者负号,则返回 NaN。如果第一个字符是数字字符,parseInt() 会继续解析第二个字符,直到解析玩所有后续字符或者遇到了一个非数字字符。

需要注意的是,”1.1” 这样的字符串会被 parseInt() 转换为 1 ,因为小数点并不是有效的数字字符。

还可以为 parseInt() 函数指定第二个参数 :转换时使用的基数。

1
var num = parseInt("0xAF", 16);	// 以十六进制转换字符串	

parseInt() 不同的是,parseFloat() 只解析十进制值。

String 类型

String 类型用于表示由零活多个 16 位 Unicode 字符组成的字符序列,即字符串。字符串可以由双引号或单引号表示。

字符字面量

String 数据类型包含一些特殊的字符字面量,也叫转移序列,用于表示非打印字符,或者具有其它用途的字符。

字面量 含义
\n 换行
\t 制表
\b 空格
\r 回车
\\ 斜杠
\' 单引号
\" 双引号
\xnn 以十六进制代码表示的一个字符
\unnn 以十六进制代码表示的一个 Unicode 字符
\f 进纸

这些字符字面量可以出现在字符串中的任意位置,而且也将被作为也给字符来解析。

字符串的特点

ECMAScript 中的字符串是不可变的,也就是说,字符串一旦创建,它们的值就不能改变。要改变某个变量保存的字符串,首先要销毁原来的字符串,然后再用另一个包含新值得字符串填充该变量。

转换为字符串

几乎每个值都有 toString() 方法,它将返回相应值得字符串表现(nullundefined 没有这个方法 )。多数情况下,调用 toString() 方法不必传递参数,但是在调用数值的 toString() 方法时,可以传递一个表示输出数值基数的参数。

在不知道要转换的值是不是 nullundefined 的情况下,还可以使用转型函数 String() ,这个函数能够将任何类型的值转换为字符串 :

  • 如果值有 toString() 方法,则调用该方法
  • 如果值是 null ,则返回 “null”
  • 如果值是 “undefined” ,则返回 “undefined”

Object 类型

ECMAScript 中的对象其实就是一组数据和功能的集合。对象可以通过执行 new 操作符后跟要创建的对象类型的名称来创建。而创建 Object 类型的实力并为其添加属性和方法,就可以创建自定义对象。

在 ECMAScript 中,Object 类型是所有它的实例的基础。

Object 的每个实例都具有下列属性和方法 :

  • constructor : 保存着用于创建当前对象的函数。
  • hasOwnProperty(propertyName) : 用于检查给定的属性在当前的对象实例中是否存在。
  • isPrototypeOf(object) : 用于检查传入的对象是否是传入的对象的原型。
  • propertyIsEnumerable(propertyName) : 用于检查给定的属性是否能够使用 for-in 语句来枚举。
  • toLocalString() : 返回与执行环境的地区对应的字符串表示。
  • toString() : 返回对象的字符串表示。
  • valueOf() : 返回对象的字符串、数值或布尔值表示。

操作符

一元操作符

递增和递减操作符

前置递增/递减操作符,变量的值都是在包含它的语句被求值之前改变的,而后置递增/递减操作符的则是在包含它的语句被求值之后改变变量的值。

而且递增/递减操作符不仅适用于整数,还可以用于字符串、布尔值、浮点数职和对象 :

  • 在应用于一个包含有效数字字符的字符串时,先将其转换为数字值,再执行加减 1 的操作。字符串变量变成数值变量。
  • 在应用于一个不包含有效数字字符的字符串时,将变量的值设置为 NaN。字符串变量变成数值变量。
  • 在应用于布尔值时,先将其转换为 1 或 0,再执行加减 1 的操作。布尔值变量变成数值变量。
  • 在应用于对象时,先调用对象的 valueOf() 方法以取得一个可供操作的值。然后对该值应用前述规则。如果结果是 NaN,则在调用 toString() 方法后再应用前述规则。对象变量变成数值变量。
加和减操作符

+ 放在数值之前,对数值不会产生影响。不过,对非数值应用 + 时,它会像 Number() 转型函数一样对这个值进行转换。

- 主要用于表示负数。对非数值应用时,先转换为数值,再转换为负数。

位操作符

位操作符用于在最基本的层次上,即按内存中表示数值的位来操作数值。ECMAScript 中的所有数值都以 IEEE-754 64 位格式存储,但位操作符并不直接操作 64 位的值,而是先将 64 位的值转换成 32 位的整数,然后执行操作,最后再讲结果转换回 64 位。

对于有符号的整数,32 位中的前 32 位用来表示整数的值,第 32 位用于表示数值的符号 :0 表示正数,1 表示负数。这个表示符号的位叫做符号位,符号位的值决定了其它位数值的格式。其中,正数以纯二进制格式存储,32 位中的每一位都表示 2 的幂,没有用到的位以 0 填充,即忽略不计。负数同样以二进制码存储,但使用的格式是二进制补码。

要注意的是,在处理有符号整数时,是不能访问位 31 的。

对数值进行位操作时,NaN 和 Infinity 会被当做 0 来处理。

默认情况下,ECMAScript 中的所有整数都是有符号整数。不过,当然也存在无符号整数,对于无符号整数来说,第 32 位不再表示符号,因为无符号整数只能是整数。

按位非

按位非操作符由 ~ 表示,执行按位非的结果就是返回数值的反码。即操作数的负值减 1.

按位与

按位与操作符由 & 表示,它有两个操作符数。从本质上来讲,按位与操作就是将两个数值的每一位对齐,然后对相同位置上的两个数执行 AND 操作。

按位或

按位或操作符由 | 表示,同样有两个操作数。从本质上来讲,按位或操作就是将两个数值的每一位对齐,然后对相同位置上的两个数执行 OR 操作。

按位异或

按位异或操作符由 ^ 表示,同样有两个操作数。从本质上来讲,按位异或操作就是将两个数值的每一位对齐,然后对相同位置上的两个数执行 XOR 操作。

左移

左移操作符由 << 表示,它会将数值的所有位向左移动指定的位数,出现的空位用 0 进行填充。

注意,左移不会影响操作数的符号位。

有符号右移

右移操作符由 >> 表示,它会将数值的所有位向右移动指定的位数,但保留符号位,原数值中出现的空位以符号位的值来填充所有空位。

无符号右移

无符号左移由 <<< 表示,它会将数值的所有 32 位都向右移动。对正数来说,无符号右移的结果与有符号右移相同。但对负数来说,其结果就不一样了,而且无符号右移会将负数的二进制码当成整数的二进制码,因此导致无符号右移后的结果非常之大。

布尔操作符

逻辑非

逻辑非操作符由 ! 表示,可以应用于 ECMAScript 中的任何值。无论这个值是什么数据类型,这个操作符都会返回一个布尔值。逻辑非操作符首先会将它的操作数转换为一个布尔值,然后再对其求反。

逻辑非操作符也可以用于讲一个值转换为与其对应的布尔值。而同时使用两个逻辑非操作符,实际上就会模拟 Boolean() 转型函数的行为。

逻辑与

逻辑与操作符由 && 表示,有两个操作数。逻辑与操作可以应用于任何类型的操作数,而不仅仅是布尔值。在有一个操作数不是布尔值的情况下,逻辑与操作就不一定返回布尔值 :

  • 如果第一个操作数是对象,则返回第二个操作数
  • 如果第二个操作数是对象,则只有在第一个操作数的求值结果为 true 的情况下才会返回该对象
  • 如果两个操作数都是对象,则返回第二个操作数
  • 如果有一个操作数是 null , NaNundefined,则返回 null , NaNundefined

逻辑与操作属于短路操作,即如果第一个操作数能够决定结果,那么就不会对第二个操作数求值。

注意,不能在逻辑与操作中使用未定义的值。

逻辑或

逻辑或操作由 || 表示,有两个操作数。与逻辑与操作相似,如果有一个操作数不是布尔值,逻辑或也不一定返回布尔值 :

  • 如果第一个操作数是对象,则返回第一个操作数
  • 如果第一个操作数的求值结果为 false ,则返回第二个操作数
  • 如果两个操作数都是对象,则返回第一个操作数
  • 如果两个操作数都是 null , NaNundefined ,则返回 null , NaNundefined

逻辑或操作符也是短路操作符,也就是说,如果第一个操作数的求值结果为 true ,就不会对第二个操作数求值了。

乘性操作符

ECMAScript 定义了 3 个乘性操作符 :乘法、除法和求模,在操作数为非数值的情况下会执行自动的类型转换。

乘法

乘法操作符由 * 表示,用于计算两个数值的乘积。

  • 如果乘积结果超过了 ECMAScript 的数值表示范围,则返回 Infinity-Infinity
  • 如果有一个操作数是 NaN ,则结果是 NaN
  • Infinity 与 0 相乘,结果是 NaN
  • Infinity 与非 0 数值相乘,结果是 Infinity-Infinity ,取决于有符号操作数的符号
  • 如果 InfinityInfinity 相乘,则结果是 Infinity
  • 如果有一个操作数不是数值,则使用 Number() 将其转换为数值
除法

除法操作符由 / 表示,执行第二个操作数除第一个操作数的计算。

  • 如果商超过了 ECMAScript 的数值表示范围,则返回 Infinity-Infinity
  • 如果有一个操作数是 NaN ,则结果是 NaN
  • InfinityInfinity 除,结果是 NaN
  • 如果 0 被 0 除,结果是 NaN
  • 如果非零的有限数被零除,则结果是 Infinity-Infinity ,取决于有符号的操作数
  • 如果 Infinity 被任何非 0 数值除,则结果是 Infinity-Infinity ,取决于有符号的操作数
  • 如果有一个操作数不是数值,则使用 Number() 将其转换为数值
求模

求模操作符由 % 表示。

  • 如果被除数是无穷大值而出除数是有限大的数值,则结果是 NaN
  • 如果被除数是有限大的数值而除数是 0 ,则结果是 NaN
  • 如果是 InfinityInfinity 除,则结果是 NaN
  • 如果被除数是有限大的数值而除数是无穷大的数值,则结果是被除数
  • 如果被除数是 0 ,则结果是0
  • 如果有一个操作数不是数值,则使用 Number() 将其转换为数值

加性操作符

加法

加法操作符由 + 表示。

如果两个操作数都是数值,执行常规的加法计算,然后按照下列规则返回结果 :

  • 如果一个操作数是 NaN ,则结果是 NaN
  • 如果是 InfinityInfinity ,则结果是 Infinity
  • 如果是 -Infinity-Infinity ,则结果是 -Infinity
  • 如果是Infinity-Infinity ,则结果是 NaN

如果有一个操作数是字符串 :

  • 如果两个操作数都是字符串,则将它们拼接起来
  • 如果只有一个操作数是字符串,则将另一个操作数转换为字符串,然后再将两个字符串拼接起来

如果有一个操作数是对象、数值或布尔值,则调用它们的 toString() 方法取得相应的字符串值,然后再应用前述规则。

对于 undefinednull ,则分别调用 String() 函数并取得字符串 “undefined” 和 “null” 。

减法

减法操作符由 - 表示。

  • 如果有一个操作数是 NaN ,则结果是 NaN
  • 如果是 InfinityInfinity ,则结果是 NaN
  • 如果是 -Infinity-Infinity ,则结果是 NaN
  • 如果是 Infinity-Infinity ,则结果是 Infinity
  • 如果是 -InfinityInfinity ,则结果是 Infinity
  • 如果有一个操作数是字符串、布尔值、nullundefined ,则先调用 Number() 函数将其转换为数值,然后在按照前述规则执行减法计算。如果转换的结果是 NaN ,则减法的结果就是 NaN
  • 如果有一个操作数是对象,则调用对象的 valueOf() 方法以取得表示该对象的数值。如果得到的是 NaN ,则减法的结果就是 NaN 。如果对象没有 valueOf() 方法,则调用其 toString() 方法并将得到的字符串转换为数值

关系操作符

与 ECMAScript 中其它操作符一样,当关系操作符的操作数使用了非数值时,也要进行数据转换或完成某些奇怪的操作 :

  • 如果两个操作数都是数值,则执行数值比较
  • 如果两个操作数都是字符串,则比较两个字符串对应的字符编码值
  • 如果一个操作数是数值,则将另一个操作数转换为一个数值,然后执行数值比较
  • 如果一个操作数是对象,则调用这个对象的 valueOf() 方法,用得到的结果按照前述规则执行比较。如果对象没有 valueOf() 方法,则调用 toString() 方法,并用得到的结果根据前述规则执行比较
  • 如果一个操作数是布尔值,则先将其转化为数值,然后再执行比较

在比较两个字符串时,实际比较的是两个字符串中对应位置的每个字符的字符编码值,因此一般需要将两个操作数转换为相同的大小写形式,然后再执行比较。

相等操作符

相等和不相等

ECMAScript 中的相等操作符由 == 表示,如果两个操作数相等,则返回 true 。而不相等操作符由 != 表示,如果两个操作数不相等,则返回 true 。这两个操作符都会先转换操作数,然后在比较它们的相等性。

在转换不同的数据时,需遵守以下规则 :

  • 如果有一个操作数是布尔值,则在比较相等性之前先将其转换为数值 – false 转换为 0 ,true 转换为 1
  • 如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为数值
  • 如果一个操作数是对象,另一个操作数不是,则调用对象的 valueOf() 方法,用得到的基本类型值按照前述规则进行比较

这两个操作符在进行比较时要遵守下列规则 :

  • nullundefined 时相等的
  • 比较相等性之前,不能将 nullundefined 转换为其它任何值
  • 如果有一个操作数是 NaN ,则相等操作符返回 false ,而不相等操作符返回 true
  • 如果两个数都是对象,则比较它们是否是同一个对象。如果两个操作数都指向同一个对象,则相等操作符返回 true ;否则, 返回 false

特殊情况 :

1
2
3
4
5
6
7
8
9
10
11
null == undefined 	// true
true == 1 // true
true == 2 // false
"NaN" == NaN // false
5 == NaN // false
undefined == 0 // false
NaN == NaN // false
null == 0 // false
NaN != NaN // true
"5" == 5 // true
false == 0 // true
全等和不全等

除了在比较之前不能转换为操作数之外,全等和不全等操作符与相等和不相等操作符没有什么区别。全等操作符由 === 表示,它只在两个操作数未经转换就想等的情况下返回 true

由于相等和不相等操作符存在类型转换问题,为了保持代码中数据类型的完整性,推荐使用全等和不全等操作符。

条件操作符

1
variable = boolean_expression ? true_value : false_value;	

基于对 boolean_expression 的求值结果,决定给变量 variable 赋什么值。

语句

if 语句、do-while 语句、while 语句、for 语句、for-in 语句与其它语言并没有不同,在此不做介绍。

label 语句

使用 label 语句可以在代码中添加标签,以便将来使用。

breakcontinue 语句

breakcontinue 语句用于在循环中精确地控制代码的执行。break 语句会立即退出循环,强制继续执行循环后面的语句。而 continue 语句虽然也是立即退出循环,但退出循环后会从循环的顶部继续执行。

with 语句

with 语句的作用是将代码的作用域设置到一个特定的对象中 :

1
with (expression) statment;

定义 with 语句的目的主要是为了简化多次编写同一个对象的工作。

1
2
3
4
5
6
7
8
9
10
11
var qs = location.search.substring(1);
var hostName = location.hostName;
var url = location.href;

// 使用 with 语句改写

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

严格模式下不允许使用 with 语句,视为语法错误。

由于大量使用 with 语句会导致性能下架,同时也会给调试代码造成困难,因此在开发大型应用程序时,不建议使用 with 语句。

swithc 语句

ECMAScript 中的 switch 有两个特点 :

  • 可以在 switch 中使用任何数据类型
  • 每个 case 的值不一定是常量,可以是变量,甚至是表达式

需要主要的是,switch 语句在比较值时使用的是全等操作符,不会发生类型转换。

函数

ECMAScript 中的函数使用 function 关键字来声明,后跟一组参数及函数体。

1
2
3
function functionName (arg_0, arg_1, ...) {
statments
}

ECMAScript 中的函数在定义时不必指定是否返回值。

return 语句也可以不带有任何返回值。在这种情况下,函数在停止执行后将返回 undefined 值。这种用法一般用在需要提前停止函数执行而又不需要返回值的情况下。

严格模式下 :

  • 不能把函数或参数命名为 eval 或 arguments

  • 不能出现两个命名参数同名的情况

参数

ECMAScript 函数的参数不介意传递进来多少个参数,也不在乎传进来的参数是什么数据类型。ECMAScript 中函数的参数在内部是用一个数组来表示的,函数接收到的始终都是这个数组,而不关心数组中包含哪些参数。在函数体内,可以通过 arguments 对象来访问这个参数数组,从而获得传递给函数的每一个参数。

命名的参数只提供便利,但并不是必需的。而且在 ECMAScript 中,解析器不会验证命名参数。

没有传递值得命名参数将自动被赋予 undefined 值。

严格模式对如何使用 arguments 对象做出了一些限制 :无法对命名参数进行赋值;重写 arguments 的值会导致语法错误。

arguments 本质上并不是一个数组,只是与数组类似,你可以使用方括号语法来访问它的每一个元素。

没有重载

ECMAScript 中函数的参数有包含零或多个值得数组来表示,所以没有函数签名。而没有函数签名,真正的重载是不可能做到的。如果在 ECMAScript 中定义了同名函数,则该名字只属于后定义的函数。

作者

Y2hlbmdsZWk=

发布于

2017-05-01

更新于

2021-09-01

许可协议