JS高程读书笔记-第三章基本概念

book-javascript-3-3

# 3. 基本概念

# 3.1 语法

  1. 区分大小写
  2. 标识符:变量、函数、属性的名字。
    1. 第一个字符必须是字母、下划线(_)或美元符($)
    2. 其他字符可以是字母、下划线、美元符号或数字
    3. ECMAScript 标准中的标识符采用驼峰大小写格式
  3. 注释
  4. 严格模式(ES5 引入)
    1. 它是一个编译指示(pragma),告诉支持的 JavaScript 引擎切换到严格模式,这是为不破坏 ECMAScript 3 语法而特意选定的语法
  5. 语句
    1. 分号结尾,如果省略,则由解析器确定语句的结尾
    2. 分号结尾能增进代码的性能,因为解析器不用花时间推测在哪里插入分号了

# 3.2 关键字和保留字

关键字:这些关键字可用于表示控制语句的开始或结束,或者用于执行特定操作等

保留字:有可能在将来被用作关键字

注意:标识符不应该使用关键字或保留字

# 3.4 数据类型

基本类型

  1. Undefined
  2. Null
  3. Boolean
  4. String
  5. Number
  6. Symbol

复杂类型

  1. Object

ES 中所有值最终都将是上述 6 种数据类型之一

# 3.4.1 typeof 操作符

返回值

  1. undefined ---- 如果值未定义
  2. boolean ---- 如果值为布尔值
  3. string ---- 如果这个值是字符串
  4. number ---- 如果这个值是数值
  5. object ---- 如果值为对象或 null
  6. function ---- 如果这个值是函数

# Undefined 类型

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

注意:在代码中访问一个未声明的变量会报 ReferenceError,而对未声明变量执行 typeof 会返回 undefined

# Null 类型

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

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

# Boolean 类型

对任何数据类型的值调用 Boolean()函数,而且总会返回一个 Boolean 值,至于返回的 这个值是 true 还是 false,取决于要转换值的数据类型及其实际值。

数据类型 转换为 true 的值 转换为 false 的值
Boolean true false
String 任何非空字符串 ""(空字符串)
Number 任何非零数字值(包括无穷大) 0 和 NaN
Object 任何对象 null
Undefined 不适用 undefined

# Number 类型

使用 IEEE754 格式来表示整数和浮点数值

# 整数
  1. 十进制
  2. 十六进制:前导0x,后面跟 0 - 9 或 A - F
  3. 八进制:前导0,后面跟 0 - 7

注意:八进制在严格模式下无效,且第一位必须是 0,如果字面值中由超过了 7,则前导零会被忽略,当作十进制解析

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

# 浮点数

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

# 数值范围

ECMAScript 中能表示的最大值和最小值:

  1. Number.MAX_VALUE
  2. Number.MIN_VALUE

计算:

  1. 超出范围则转换成 Infinity 或 -Infinity。
  2. 正数除以 0 返回 Infinity
  3. 负数除以 0 返回-Infinity

如果某次计算返回了正或负的 Infinity 值,那么该值将无法继续参与下一次的计算, 因为 Infinity 不是能够参与计算的数值,如下例子所示:

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

NaN,即非数值(Not a Number)是一个特殊的数值,这个数值用于表示一个本来要返回数值的操作数 未返回数值的情况(这样就不会抛出错误了)。

计算:

alert(isNaN(NaN)); // true
alert(isNaN(10)); // false(10 是一个数值)
alert(isNaN("10")); // false(可以被转换成数值 10)
alert(isNaN("blue")); // true(不能转换成数值)
alert(isNaN(true)); // false(可以被转换成数值 1)

计算过程:判断值被 Number 调用后的返回值是否不是一个数值

注意:0 除以 0 会返回 NaN

# 数值转换
  1. Number():适用任何类型
  2. parseInt():适用字符串转数值
  3. parseFloat():适用字符串转数值

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

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

由于 Number()函数在转换字符串时比较复杂而且不够合理,因此在处理整数的时候更常用的是 parseInt()函数。

parseInt()函数在转换字符串时,更多的是看其是否符合数值模式。它会忽略字 符串前面的空格,直至找到第一个非空格字符。如果第一个字符不是数字字符或者负号,parseInt() 就会返回 NaN;也就是说,用 parseInt()转换空字符串会返回 NaN(Number()对空字符返回 0)。如 果第一个字符是数字字符,parseInt()会继续解析第二个字符,直到解析完所有后续字符或者遇到了 一个非数字字符。例如,"1234blue"会被转换为 1234,因为"blue"会被完全忽略。类似地,"22.5" 4 会被转换为 22,因为小数点并不是有效的数字字符。

下面是一些例子:

var num1 = parseInt("1234blue"); // 1234
var num2 = parseInt(""); // NaN
var num3 = parseInt("0xA"); // 10(十六进制数)
var num4 = parseInt(22.5); // 22
var num5 = parseInt("070"); // 56(八进制数)
var num6 = parseInt("70"); // 70(十进制数)
var num7 = parseInt("0xf"); // 15(十六进制数)

指定基数会影响到转换的输出结果。例如:

var num1 = parseInt("10", 2); //2 (按二进制解析)
var num2 = parseInt("10", 8); //8 (按八进制解析)
var num3 = parseInt("10", 10); //10(按十进制解析)
var num4 = parseInt("10", 16); //16(按十六进制解析)

注意:不指定基数意味着让 parseInt()决定如何解析输入的字符串,因此为了避免错误的解析,建议无论在什么情况下都明确指定基数。

与 parseInt()函数类似,parseFloat()也是从第一个字符(位置 0)开始解析每个字符。而且 也是一直解析到字符串末尾,或者解析到遇见一个无效的浮点数字字符为止。也就是说,字符串中的第 一个小数点是有效的,而第二个小数点就是无效的了,因此它后面的字符串将被忽略。举例来说, "22.34.5"将会被转换为 22.34。 除了第一个小数点有效之外,parseFloat()与 parseInt()的第二个区别在于它始终都会忽略前导 的零。parseFloat()可以识别前面讨论过的所有浮点数值格式,也包括十进制整数格式。但十六进制格 式的字符串则始终会被转换成 0。由于 parseFloat()只解析十进制值,因此它没有用第二个参数指定基 数的用法。最后还要注意一点:如果字符串包含的是一个可解析为整数的数(没有小数点,或者小数点后 都是零),parseFloat()会返回整数。以下是使用 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.125e7"); // 31250000

# String 类型

特殊的字符字面量:

字面量 含义
\n 换行
\t 制表
\b 空格
\r 回车
\f 进纸
\ 斜杠
\' 单引号('),在用单引号表示的字符串中使用。例如:'He said, 'hey.''
\" 双引号("),在用双引号表示的字符串中使用。例如:"He said, "hey.""
\xnn 以十六进制代码 nn 表示的一个字符(其中 n 为 0~F)。例如,\x41 表示"A"
\unnnn 以十六进制代码 nnnn 表示的一个 Unicode 字符(其中 n 为 0~F)。例如,\u03a3 表示希腊字符 Σ
var text = "This is the letter sigma: \u03a3.";

这个例子中的变量 text 有 28 个字符,其中 6 个字符长的转义序列表示 1 个字符。

注意:如果字符串中包含双字节字符,那么 length 属性 可能不会精确地返回字符串中的字符数目。

转换成字符串:

  1. toString():几乎每个值都有这个方法
  2. String()

String() 规则:

  1. 如果值有 toString()方法,则调用该方法(没有参数)并返回相应的结果;
  2. 如果值是 null,则返回"null";
  3. 如果值是 undefined,则返回"undefined"。

注意:null 和 undefined 没有 toString() 方法。

# Object 类型

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

  1. constructor: 保存着用于创建当前对象的函数。对于前面的例子而言,构造函数(constructor) 就是 Object()。
  2. hasOwnProperty(propertyName): 用于检查给定的属性在当前对象实例中(而不是在实例 的原型中)是否存在。其中,作为参数的属性名(propertyName)必须以字符串形式指定(例如: o.hasOwnProperty("name"))。
  3. isPrototypeOf(object): 用于检查传入的对象是否是传入对象的原型(第 5 章将讨论原 型)。
  4. propertyIsEnumerable(propertyName): 用于检查给定的属性是否能够使用 for-in 语句 (本章后面将会讨论)来枚举。与 hasOwnProperty()方法一样,作为参数的属性名必须以字符 串形式指定。
  5. toLocaleString(): 返回对象的字符串表示,该字符串与执行环境的地区对应。
  6. toString(): 返回对象的字符串表示。
  7. valueOf(): 返回对象的字符串、数值或布尔值表示。通常与 toString()方法的返回值 相同。

注意: 从技术角度讲,ECMA-262 中对象的行为不一定适用于 JavaScript 中的其他对象。 浏览器环境中的对象,比如 BOM 和 DOM 中的对象,都属于宿主对象,因为它们是 由宿主实现提供和定义的。ECMA-262 不负责定义宿主对象,因此宿主对象可能会也 可能不会继承 Object。

# 3.5 操作符

ECMA-262 描述了一组用于操作数据值的操作符,包括:

  1. 算术操作符(如加号和减号)
  2. 位操作符
  3. 关系操作符
  4. 相等操作符

ECMAScript 操作符的与众不同之处在于,它们能够适用于很多值,例如字符串、数字值、布尔值,甚至对象。不过,在应用于对象时,相应的操作符通常都会调用对象的 valueOf() 和(或)toString()方法,以便取得可以操作的值。

# 一元操作符

  • 递增和递减操作符(++, --)

在应用于不同的值时,递增和递减操作符遵循下列规则。

  1. 在应用于一个包含有效数字字符的字符串时,先将其转换为数字值,再执行加减 1 的操作。字 符串变量变成数值变量。
  2. 在应用于一个不包含有效数字字符的字符串时,将变量的值设置为 NaN(第 4 章将详细讨论)。 字符串变量变成数值变量。
  3. 在应用于布尔值 false 时,先将其转换为 0 再执行加减 1 的操作。布尔值变量变成数值变量。
  4. 在应用于布尔值 true 时,先将其转换为 1 再执行加减 1 的操作。布尔值变量变成数值变量。
  5. 在应用于浮点数值时,执行加减 1 的操作。
  6. 在应用于对象时,先调用对象的 valueOf()方法(第 5 章将详细讨论)以取得一个可供操作的 值。然后对该值应用前述规则。如果结果是 NaN,则在调用 toString()方法后再应用前述规则。对象变量变成数值变量
var s1 = "2"; s1++; // 3
var s2 = "z"; s2++; // NaN
var b = false; b++; // 1
var f = 1.1; f--; // 0.10000000000000009
var o = {
  valueOf: function() {
    return -1;
  }
};
o--; // -2
  • 一元加和减操作符(+, -)

对非数值应用一元加操作符时,该操作符会像 Number()转型函数一样对这个值执行转换。 换句话说,布尔值 false 和 true 将被转换为 0 和 1,字符串值会被按照一组特殊的规则进行解析,而 对象是先调用它们的 valueOf()和(或)toString()方法,再转换得到的值。

# 位操作符

位操作符用于在最基本的层次上,即按内存中表示数值的位来操作数值。ECMAScript 中的所有数 值都以 IEEE-754 64 位格式存储,但位操作符并不直接操作 64 位的值。而是先将 64 位的值转换成 32 位 的整数,然后执行操作,最后再将结果转换回 64 位。对于开发人员来说,由于 64 位存储格式是透明的, 因此整个过程就像是只存在 32 位的整数一样。

对于有符号的整数,32 位中的前 31 位用于表示整数的值。第 32 位用于表示数值的符号:0 表示正 数,1 表示负数。这个表示符号的位叫做符号位,符号位的值决定了其他位数值的格式。其中,正数以 纯二进制格式存储,31 位中的每一位都表示 2 的幂。

负数同样以二进制码存储,但使用的格式是二进制补码。计算一个数值的二进制补码,需要经过下 列 3 个步骤:

(1) 求这个数值绝对值的二进制码(例如,要求18 的二进制补码,先求 18 的二进制码); (2) 求二进制反码,即将 0 替换为 1,将 1 替换为 0; (3) 得到的二进制反码加 1。

在 ECMAScript 中,当对数值应用位操作符时,后台会发生如下转换过程:64 位的数值被转换成 32 位数值,然后执行位操作,最后再将 32 位的结果转换回 64 位数值。

位操作符:

  1. 按位非(NOT),符号:
  2. 按位与(AND),符号:&
  3. 按位或(OR),符号:
  4. 按位异或(XOR),符号 ^
  5. 左移,符号 <<,右侧填充0
  6. 有符号位右移,号 >>,左侧补0,保留符号位
  7. 无符号位右移,号 >>>,左侧补0,32位整体右移,对正数来说与有符号位右移一样。

# 布尔操作符

  1. 逻辑非

符号:!

操作数:1个,例 !a

规则:

  • 如果操作数是一个对象,返回 false;
  • 如果操作数是一个空字符串,返回 true;
  • 如果操作数是一个非空字符串,返回 false;
  • 如果操作数是数值 0,返回 true;
  • 如果操作数是任意非 0 数值(包括 Infinity),返回 false;
  • 如果操作数是 null,返回 true;
  • 如果操作数是 NaN,返回 true;
  • 如果操作数是 undefined,返回 true。

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

  1. 逻辑与

符号:&&

操作数:2个,例 a && b

规则:

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

逻辑与属于短路操作符,即第一个操作数如果为 false,则不会进行第二个操作数的求值了。

  1. 逻辑或

符号:||

操作数:2个,例 a || b

规则:

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

逻辑或也属于短路操作符,即第一个操作数如果为 true,则不会进行第二个操作数的求值了。

# 乘性操作符

ECMAScript 定义了 3 个乘性操作符:乘法、除法和求模。

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

  1. 乘法

符号:*

规则:

  • 如果操作数都是数值,执行常规的乘法计算,即两个正数或两个负数相乘的结果还是正数,而 如果只有一个操作数有符号,那么结果就是负数。如果乘积超过了 ECMAScript 数值的表示范围, 则返回 Infinity 或-Infinity;
  • 如果有一个操作数是 NaN,则结果是 NaN;
  • 如果是 Infinity 与 0 相乘,则结果是 NaN;
  • 如果是 Infinity 与非 0 数值相乘,则结果是 Infinity 或-Infinity,取决于有符号操作数 的符号;
  • 如果是 Infinity 与 Infinity 相乘,则结果是 Infinity;
  • 如果有一个操作数不是数值,则在后台调用 Number()将其转换为数值,然后再应用上面的规则。
  1. 除法

符号:/

规则:

  • 如果操作数都是数值,执行常规的除法计算,即两个正数或两个负数相除的结果还是正数,而 如果只有一个操作数有符号,那么结果就是负数。如果商超过了 ECMAScript 数值的表示范围, 则返回 Infinity 或-Infinity;
  • 如果有一个操作数是 NaN,则结果是 NaN;
  • 如果是 Infinity 被 Infinity 除,则结果是 NaN;
  • 如果是零被零除,则结果是 NaN;
  • 如果是非零的有限数被零除,则结果是 Infinity 或-Infinity,取决于有符号操作数的符号;
  • 如果是 Infinity 被任何非零数值除,则结果是 Infinity 或-Infinity,取决于有符号操作数的符号;
  • 如果有一个操作数不是数值,则在后台调用 Number()将其转换为数值,然后再应用上面的规则。
  1. 求模

符号:%

  • 如果操作数都是数值,执行常规的除法计算,返回除得的余数;
  • 如果被除数是无穷大值而除数是有限大的数值,则结果是 NaN;
  • 如果被除数是有限大的数值而除数是零,则结果是 NaN;
  • 如果是 Infinity 被 Infinity 除,则结果是 NaN;
  • 如果被除数是有限大的数值而除数是无穷大的数值,则结果是被除数;
  • 如果被除数是零,则结果是零;
  • 如果有一个操作数不是数值,则在后台调用 Number()将其转换为数值,然后再应用上面的规则。

# 加性操作符

  1. 加法

符号:+

规则:

  • 如果有一个操作数是 NaN,则结果是 NaN;
  • 如果是 Infinity 加 Infinity,则结果是 Infinity;
  • 如果是-Infinity 加-Infinity,则结果是-Infinity;
  • 如果是 Infinity 加-Infinity,则结果是 NaN;
  • 如果是+0 加+0,则结果是+0;
  • 如果是-0 加-0,则结果是0;
  • 如果是+0 加-0,则结果是+0。 不过,如果有一个操作数是字符串,那么就要应用如下规则:
  • 如果两个操作数都是字符串,则将第二个操作数与第一个操作数拼接起来;
  • 如果只有一个操作数是字符串,则将另一个操作数转换为字符串,然后再将两个字符串拼接 起来。
  • 如果有一个操作数是对象、数值或布尔值,则调用它们的 toString()方法取得相应的字符串值, 然后再应用前面关于字符串的规则。对于 undefined 和 null,则分别调用 String()函数并取得字符 串"undefined"和"null"。
  1. 减法

符号:-

规则:

  • 如果两个操作符都是数值,则执行常规的算术减法操作并返回结果;
  • 如果有一个操作数是 NaN,则结果是 NaN;
  • 如果是 Infinity 减 Infinity,则结果是 NaN;
  • 如果是-Infinity 减-Infinity,则结果是 NaN;
  • 如果是 Infinity 减-Infinity,则结果是 Infinity;
  • 如果是-Infinity 减 Infinity,则结果是-Infinity;
  • 如果是+0 减+0,则结果是+0;
  • 如果是+0 减-0,则结果是-0;
  • 如果是0 减0,则结果是+0;
  • 如果有一个操作数是字符串、布尔值、null 或 undefined,则先在后台调用 Number()函数将 其转换为数值,然后再根据前面的规则执行减法计算。如果转换的结果是 NaN,则减法的结果 就是 NaN;
  • 如果有一个操作数是对象,则调用对象的 valueOf()方法以取得表示该对象的数值。如果得到 的值是 NaN,则减法的结果就是 NaN。如果对象没有 valueOf()方法,则调用其 toString() 方法并将得到的字符串转换为数值。

# 关系操作符

  1. 大于
  2. 小于
  3. 大于等于
  4. 小于等于

规则:

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

在使用关系操作符比较两个字符串时,会执行一种奇怪的操作。很多人都会认为,在比较字符串值 时,小于的意思是“在字母表中的位置靠前”,而大于则意味着“在字母表中的位置靠后”,但实际上完 全不是那么回事。在比较字符串时,实际比较的是两个字符串中对应位置的每个字符的字符编码值。经 过这么一番比较之后,再返回一个布尔值。由于大写字母的字符编码全部小于小写字母的字符编码,因 此我们就会看到如下所示的奇怪现象:

var result = "Brick" < "alphabet"; //true
// B 的字符编码为 66,而 a 的字符编码是 97

var result = "23" < "3"; // true
// "2"的字符编码是 50,而"3"的字符编码是 51

var result = "23" < 3; // false
// 字符串"23"会被转换成数值 23,然后再与 3 进行比较

var result = "a" < 3; // false
// 因为"a"被转换成了 NaN,任何操作数与 NaN 进行关系比较,结果都是 false
// 所以
var result1 = NaN < 3; //false
var result2 = NaN >= 3; //false

# 相等操作符

最早的 ECMAScript 中的相等和不等 操作符会在执行比较之前,先将对象转换成相似的类型。后来,有人提出了这种转换到底是否合理的质疑。

最后,ECMAScript 的解决方案就是提供两组操作符:相等和不相等——先转换再比较,全等和不 全等——仅比较而不转换。

  1. 相等和不相等

符号:==!=

操作数:2个,先强制转型,然后再比较它们的相等性

规则:

  • 如果有一个操作数是布尔值,则在比较相等性之前先将其转换为数值——false 转换为 0,而true 转换为 1;
  • 如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为数值;
  • 如果一个操作数是对象,另一个操作数不是,则调用对象的 valueOf()方法,用得到的基本类 型值按照前面的规则进行比较; 这两个操作符在进行比较时则要遵循下列规则。
  • null 和 undefined 是相等的。
  • 要比较相等性之前,不能将 null 和 undefined 转换成其他任何值。
  • 如果有一个操作数是 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
  1. 全等和不全等

符号:===!==

规则:

  1. 不转换类型,直接进行比较

# 条件操作符

即三目运算符,语法:

variable = boolean_expression ? true_value : false_value;

# 赋值操作符

符号:=,例 a = b

# 逗号操作符

使用逗号操作符可以在一条语句中执行多个操作,如下面的例子所示:

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

逗号操作符多用于声明多个变量;但除此之外,逗号操作符还可以用于赋值。在用于赋值时,逗号 操作符总会返回表达式中的最后一项,如下面的例子所示:

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

# 3.6 语句

ECMA-262 规定了一组语句(也称为流控制语句)。从本质上看,语句定义了 ECMAScript 中的主要 语法,语句通常使用一或多个关键字来完成给定任务。语句可以很简单,例如通知函数退出;也可以比 较复杂,例如指定重复执行某个命令的次数。

# if 语句

if (condition) statement1 else statement2

其中的 condition(条件)可以是任意表达式;而且对这个表达式求值的结果不一定是布尔值。 ECMAScript 会自动调用 Boolean()转换函数将这个表达式的结果转换为一个布尔值。如果对 condition 求值的结果是 true,则执行 statement1(语句 1),如果对 condition 求值的结果是 false,则执行 statement2(语句 2)。而且这两个语句既可以是一行代码,也可以是一个代码块(以一对花括号括起来的多行代码)。

# do while 语句

do-while 语句是一种后测试循环语句,即只有在循环体中的代码执行之后,才会测试出口条件。换句话说,在对条件表达式求值之前,循环体内的代码至少会被执行一次。以下是 do-while 语句的示例:

var i = 0;

do {
  i += 2
} while (i < 10);

# while 语句

while 语句属于前测试循环语句,也就是说,在循环体内的代码被执行之前,就会对出口条件求值。因此,循环体内的代码有可能永远不会被执行。

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

# for 语句

for 语句也是一种前测试循环语句,但它具有在执行循环之前初始化变量和定义循环后要执行的代 码的能力。

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

# for-in 语句

for-in 语句是一种精准的迭代语句,可以用来枚举对象的属性。

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

注意:ECMAScript 对象的属性没有顺序。因此,通过 for-in 循环输出的属性名的顺序是不可预测的。 具体来讲,所有属性都会被返回一次,但返回的先后次序可能会因浏览器而异。

如果表示要迭代的对象的变量值为 null 或 undefined,for-in 语句会抛出错误。

ECMAScript 5 更正了这一行为;对这种情况不再抛出错误,而只是不执行循环体。为了保证最大限度的兼容性,建议在使用 for-in 循环之前,先检测确认该对象的值不是 null 或 undefined。

# label 语句

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

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

这个例子中定义的 start 标签可以在将来由 break 或 continue 语句引用。加标签的语句一般都 要与 for 语句等循环语句配合使用。

# break 和 continue 语句

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

# with 语句

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

定义 with 语句的目的主要是为了简化多次编写同一个对象的工作,如下面的例子所示:

var qs = location.search.substring(1);
var hostName = location.hostname;
var url = location.href;

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

使用 with 语句关联了 location 对象。这意味着在 with 语句的代码块 内部,每个变量首先被认为是一个局部变量,而如果在局部环境中找不到该变量的定义,就会查询 location 对象中是否有同名的属性。如果发现了同名属性,则以 location 对象属性的值作为变量的值。

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

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

# switch 语句

switch 语句与 if 语句的关系最为密切,而且也是在其他语言中普遍使用的一种流控制语句。

# 3.7. 函数

严格模式对函数有一些限制:

  • 不能把函数命名为 eval 或 arguments;
  • 不能把参数命名为 eval 或 arguments;
  • 不能出现两个命名参数同名的情况。

如果发生以上情况,就会导致语法错误,代码无法执行。

# 参数

通过类数组对象 arguments 访问函数参数。

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

每次执行这个 doAdd()函数都会重写第二个参数,将第二个参数的值修改为 10。因为 arguments 对象中的值会自动反映到对应的命名参数,所以修改 arguments[1],也就修改了 num2,结果它们的 值都会变成 10。不过,这并不是说读取这两个值会访问相同的内存空间;它们的内存空间是独立的,但 它们的值会同步。另外还要记住,如果只传入了一个参数,那么为 arguments[1]设置的值不会反应到 命名参数中。这是因为 arguments 对象的长度是由传入的参数个数决定的,不是由定义函数时的命名 参数的个数决定的。

关于参数还要记住最后一点:没有传递值的命名参数将自动被赋予 undefined 值。这就跟定义了 变量但又没有初始化一样。例如,如果只给 doAdd()函数传递了一个参数,则 num2 中就会保存 undefined 值。

严格模式对如何使用 arguments 对象做出了一些限制。首先,像前面例子中那样的赋值会变得无 效。也就是说,即使把 arguments[1]设置为 10,num2 的值仍然还是 undefined。其次,重写 arguments 的值会导致语法错误(代码将不会执行)。

ECMAScript 中的所有参数传递的都是值,不可能通过引用传递参数。

# 没有重载

ECMAScript 函数不能像传统意义上那样实现重载。而在其他语言(如 Java)中,可以为一个函数 编写两个定义,只要这两个定义的签名(接受的参数的类型和数量)不同即可。如前所述,ECMAScirpt 函数没有签名,因为其参数是由包含零或多个值的数组来表示的。而没有函数签名,真正的重载是不可 能做到的。

如果在 ECMAScript 中定义了两个名字相同的函数,则该名字只属于后定义的函数。