目录

第三章 - 基本概念

本章内容

  • 语法
  • 数据类型
  • 流控制语句
  • 函数

摘选一些基本概念以供之后参考。

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没有函数签名,真正的重载是不可能做到的。 如前所述,通过检查传入函数中参数的类型和数量并作出不同的反应,可以模仿方法的重载