第三章 - 基本概念
本章内容
- 语法
- 数据类型
- 流控制语句
- 函数
摘选一些基本概念以供之后参考。
3.4数据类型
3.4.1 typeof操作符
typeof 操作符的操作数可以是变量(message),也可以是数值字面量。typeof 是一个操作符而不是函数,因此例子中的圆括号尽管可以使用,但不是必需的。
3.4.2 Undefined类型
Undefined 类型只有一个值,即特殊的 undefined。在使用 var 声明变量但未对其加以初始化时,这个变量的值就是 undefined。
3.4.3 Null类型
从逻辑角度来看,null 值表示一个空对象指针,而这也正是使用 typeof 操作符检测 null 值时会返回"object"的原因。 如果定义的变量准备在将来用于保存对象,那么最好将该变量初始化为 null而不是其他值。 下表给出了各种数据类型及其对应的转换规则。
数据类型 | 转换为true值 | 转换为false值 |
---|---|---|
Boolean | truw | false |
String | 非空字符串 | ‘'(空字符串) |
Number | 非零数字 | 0和NaN |
Object | 任何对象 | null |
undefined | N/A | undefined |
3.4.5 Number类型
-
浮点数值 由于保存浮点数值需要的内存空间是保存整数值的两倍,因此 ECMAScript 会不失时机地将浮点数值转换为整数值。如果浮点数值本身表示的就是一个整数(如 1.0),那么该值也会被转换为整数。 可以**用 e 表示法(即科学计数法)**表示的浮点数值表示。
-
数值范围 ECMAScript 能够表示的最小数值保存在 Number.MIN_VALUE 中——在大多数浏览器中,这个值是 5e-324;能够表示的最大数值保存在Number.MAX_VALUE 中——在大多数浏览器中,这个值是 1.7976931348623157e+308。超过的数值会被转化为-Infinity(负无穷),Infinity(正无穷)。
-
NaN 这个数值用于表示一个本来要返回数值的操作数未返回数值的情况(这样就不会抛出错误了)。 两个特点:首先,任何涉及 NaN 的操作(例如 NaN/10)都会返回 NaN;其次,NaN 与任何值都不相等,包括 NaN 本身。 针对 NaN 的这两个特点,ECMAScript 定义了 isNaN()函数:这个函数接受一个参数,该参数可以是任何类型,而函数会帮我们确定这个参数是否“不是数值”。任何不能被转换为数值的值都会导致这个函数返回 true(比如NaN, 字母字符串)。
-
数值转换 有 3 个函数可以把非数值转换为数值:Number()、parseInt()和 parseFloat()。 Number()可以用于任何数据类型,而另两个函数则专门用于把字符串转换成数值。 Number()函数的转换规则如下。
- 如果是 Boolean 值,true 和 false 将分别被转换为 1 和 0。
- 如果是数字值,只是简单的传入和返回。
- 如果是 null 值,返回 0。
- 如果是 undefined,返回 NaN。
- 如果是字符串, 有“数字”的能转则转(规则繁杂)。 **在处理整数的时候更常用的是parseInt()函数。**parseInt()函数在转换字符串时,更多的是看其是否符合数值模式。它会忽略字符串前面的空格,直至找到第一个非空格字符。如果第一个字符不是数字字符或者负号,parseInt()就会返回 NaN; 规则举例:
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(十六进制数)
与 parseInt()函数类似,parseFloat()也是从第一个字符(位置 0)开始解析每个字符。除了第一个小数点有效之外,parseFloat()与 parseInt()的第二个区别在于它始终都会忽略前导的零。几个典型示例:
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
3.4.6 String类型
- 字符字面量 \n 换行, \t 制表, \b 空格, \r 回车, \f 进纸, \ 斜杠 ; \xnn 以十六进制代码nn表示的一个字符(其中n为0~F)。例如,\x41表示"A" ; \unnnn 以十六进制代码nnnn表示的一个Unicode字符(其中n为0~F)。例如,\u03a3表示希腊字符Σ 。
- 字符串特点 字符串一旦创建,它们的值就不能改变。要改变某个变量保存的字符串,首先要销毁原来的字符串,然后再用另一个包含新值的字符串填充该变量。
- 转换为字符串 两种方式。第一种是使用几乎每个值都有的 toString()方法(继承自object):默认情况下,toString()方法以十进制格式返回数值的字符串表示。而通过传递基数,toString()可以输出以二进制、八进制、十六进制,乃至其他任意有效进制格式表示的字符串值。 还可以使用转型函数 String(),String()函数遵循下列转换规则:
- 如果值有 toString()方法,则调用该方法(没有参数)并返回相应的结果;
- 如果值是 null,则返回"null";
- 如果值是 undefined,则返回"undefined"。
3.4.7 Object类型
Object 的每个实例都具有下列属性和方法:
- constructor:保存着用于创建当前对象的函数。对于前面的例子而言,构造函数(constructor)就是 Object()。
- hasOwnProperty(propertyName):用于检查给定的属性在当前对象实例中(而不是在实例的原型中)是否存在。其中,作为参数的属性名(propertyName)必须以字符串形式指定(例 如:o.hasOwnProperty(“name”))。
- isPrototypeOf(object):用于检查传入的对象是否是传入对象的原型(第 5 章将讨论原型)。
- propertyIsEnumerable(propertyName):用于检查给定的属性是否能够使用 for-in 语句(本章后面将会讨论)来枚举。与 hasOwnProperty()方法一样,作为参数的属性名必须以字符 串形式指定。
- toLocaleString():返回对象的字符串表示,该字符串与执行环境的地区对应。
- toString():返回对象的字符串表示。
- valueOf():返回对象的字符串、数值或布尔值表示。通常与 toString()方法的返回值相同
3.5 操作符
3.5.1 一元操作符
3.5.2 位操作符
位操作符用于在最基本的层次上,即按内存中表示数值的位来操作数值。ECMAScript 中的所有数值都以 IEEE-754 64 位格式存储,但位操作符并不直接操作 64 位的值。而是先将 64 位的值转换成 32 位的整数,然后执行操作,最后再将结果转换回 64 位。 对于有符号的整数,32 位中的前 31 位用于表示整数的值。第 32 位用于表示数值的符号:0 表示正数,1 表示负数。这个表示符号的位叫做符号位,符号位的值决定了其他位数值的格式。 负数同样以二进制码存储,但使用的格式是二进制补码。 计算一个数值的二进制补码三步骤: (1) 求这个数值绝对值的二进制码(例如,要求18 的二进制补码,先求 18 的二进制码); (2) 求二进制反码,即将 0 替换为 1,将 1 替换为 0; (3) 得到的二进制反码加 1。 如-18 的二进制表示,即11111111111111111111111111101110。这里不展示举例过程。不过ECMAScript 会尽力向我们隐藏所有这些信息,我们看到的只是这个负数绝对值的二进制码前面加上了一个负号。。
- 按位非(NOT) 按位非操作符由一个波浪线(~)表示。二进制上取反,表现出来就是按位非操作的本质:操作数的负值减 1。如num1 = 25, num2 = ~nums1 == -26。
- 按位与(AND) 按位与操作符由一个和号字符(&)表示,它有两个操作符数。按位与操作只在两个数值的对应位都是 1 时才返回 1,任何一位是 0,结果都是 0。
- 按位或(OR) 按位或操作符由一个竖线符号(|)表示,同样也有两个操作数。按位或操作在有一个位是 1 的情况下就返回 1,而只有在两个位都是 0 的情况下才返回 0。
- 按位异或(XOR) 按位异或操作符由一个插入符号(^)表示,也有两个操作数。操作在两个数值对应位上只有一个 1 时才返回 1,如果对应的两位都是 1 或都是 0,则返回 0。
- 左移 左移操作符由两个小于号(«)表示,这个操作符会将数值的所有位向左移动指定的位数。左移操作会以 0 来填充这些空位,以便得到的结果是一个完整的 32 位二进制数。注意,左移不会影响操作数的符号位。
- 有符号的右移 有符号的右移操作符由两个大于号(»)表示,这个操作符会将数值向右移动,但保留符号位(即正负号标记)。 在移位过程中,原数值中也会出现空位,此时 ECMAScript 会用符号位的值来填充所有空位,以便得到一个完整的值。
- 无符号右移 无符号右移操作符由 3 个大于号(»>)表示,这个操作符会将数值的所有 32 位都向右移动。 对正数来说,无符号右移的结果与有符号右移相同。但对负数的结果就不一样了。由于负数以其绝对值的二进制补码形式表示,因此就会导致无符号右移后的结果非常之大。 var oldValue = -64; // 等于二进制的 11111111111111111111111111000000 var newValue = oldValue »> 5; // 等于十进制的 134217726
3.5.3 布尔操作符
- 逻辑非
逻辑非操作符遵循下列规则:
- 如果操作数是任意非 0 数值(包括 Infinity),返回 false;
- 如果操作数是 null,返回 true;
- 如果操作数是 NaN,返回 true;
- 如果操作数是 undefined,返回 true。 同时使用两个逻辑非操作符,实际上就会模拟 Boolean()函数的行为。
- 逻辑与
- 逻辑或
3.5.4 乘性操作符
乘法*,除法/,求模%。 在操作数为非数值的情况下会执行自动的类型转换,后台会先使用 Number()转型函数将其转换为数值。
3.5.6 关系操作符
小于(<)、大于(>)、小于等于(<=)和大于等于(>=)这几个关系操作符。 当关系操作符的操作数使用了非数值时,所遵循的规则:
- 如果两个操作数都是数值,则执行数值比较。
- 如果两个操作数都是字符串,则比较两个字符串对应的字符编码值。
- 如果一个操作数是数值,则将另一个操作数转换为一个数值,然后执行数值比较。
- 如果一个操作数是对象,则调用这个对象的 valueOf()方法,用得到的结果按照前面的规则执行比较。如果对象没有 valueOf()方法,则调用 toString()方法,并用得到的结果根据前面的规则执行比较。
- 如果一个操作数是布尔值,则先将其转换为数值,然后再执行比较。
3.5.7 相等操作符
最早的 ECMAScript 中的相等和不等操作符会在执行比较之前,先将对象转换成相似的类型。最后,ECMAScript 的解决方案就是提供两组操作符:相等和不相等——先转换再比较,全等和不全等——仅比较而不转换。
-
相等和不相等(先转换后比较) 相等操作符由(==)表示,如果两个操作数相等,则返回 true。不相等操作符由(!=)表示,如果两个操作数不相等,则返回 true。
-
全等和不全等(不转换仅比较) 全等操作符由 3 个等于号(===)表示。不全等操作符由一个叹号后跟两个等于号(!==)表示。
3.6 语句
3.6.6 label语句
使用 label 语句可以在代码中添加标签,可以在将来由 break 或 continue 语句引用。在函数前加label:
outermost:
for (var i=0; i < 10; i++) {
for (var j=0; j < 10; j++) {
if (i == 5 && j == 5) {
continue outermost;
}
num++;
}
}
3.6.8 with语句
with 语句的作用是将代码的作用域设置到一个特定的对象中。with 语句的语法如下:
with (expression) statement;
定义 with 语句的目的主要是为了简化多次编写同一个对象的工作,如下面的例子所示:
var qs = location.search.substring(1);
var hostName = location.hostname;
var url = location.href;
上面几行代码都包含 location 对象。如果使用 with 语句,可以把上面的代码改写成如下所示:
with(location){
var qs = search.substring(1);
var hostName = hostname;
var url = href;
}
由于大量使用 with 语句会导致性能下降,同时也会给调试代码造成困难,因此在开发大型应用程序时,不建议使用 with 语句。
3.7.1 理解参数
ECMAScript 函数不介意传递进来多少个参数,也不在乎传进来参数是什么数据类型。原因是 ECMAScript 中的参数在内部是用一个数组来表示的。实际上,在函数体内可以通过 arguments 对象来访问这个参数数组,从而获取传递给函数的每一个参数:
function doAdd(num1, num2) {
if(arguments.length == 1) {
alert(num1 + 10);
} else if (arguments.length == 2) {
alert(arguments[0] + num2);
}
}
关于 arguments 的行为,还有一点比较有意思。那就是它的值永远与对应命名参数的值保持同步。 关于参数还要记住最后一点:没有传递值的命名参数将自动被赋予 undefined 值。
3.7.2 没有重载
在其他语言(如 Java)中,可以为一个函数编写两个定义,只要这两个定义的签名(接受的参数的类型和数量)不同即可。 JS没有函数签名,真正的重载是不可能做到的。 如前所述,通过检查传入函数中参数的类型和数量并作出不同的反应,可以模仿方法的重载。