第3章 基本概念
数据类型
ECMAScript中有5种简单数据类型(也称基本数据类型):Undefined、Null、Boolean、Number和String。还有一种复杂数据类型——Object,本质上是由一组无序的名值对组成的。
typeof操作符
注意:typeof是一个操作符而不是函数
- “undefined”——如果这个值示定义;
- “boolean” ——如果这个值是布尔;
- “string”——如果这个值是字符串;
- “number”——如果这个值是数值;
- “object”——如果这个值是对象或null;
- “function”——如果这个值是函数;
*注:函数是一种特殊的对象
从技术角度讲,函数是ECMASscript中是对象,不是一种数据类型。然而,函数也确实有一些特殊的属性,因为通过typeof操作来区分函数和其他对象是有必要的
Undefined类型
在使用var 声明变量但未对其加以初始化时,这个变量的值就是undefined
var message;
alert("message == undefined"); // true
这个例子与下面这个例子是等价的:
var message = undefined;
alert(message == undefined); // true
*注:第3版引入这个值是为了正式区分空对象指针与未经初始化的变量。
一般而言,不存在需要显示地把一个变量设置为undefined值的情况。字面值undefined的主要目的是用于比较,而ECMA-262第3版之前的版本中并没有规定这个值。第3版引入这个值是为了正式区分空对象指针与未经初始化的变量。
对未初始化的变量执行typeof操作符会返回undefined值,而对未声名的变量执行typeof操作符同样也会返回undefined值
*注:初始化变量能让我们知道检测的变量还没有被声明,而不是尚未声名。
Null类型
Null类型表示一个空对象指针,而这也正是使用typeof操作符检测null值时会返回”object”的原因。
只要意在保存对象的变量还没有真正保存对象(准备将来用于保存对象),就应该明确地让该变量保存null值。这样做不仅可以体现null作为空对象指针的惯例,而且也有且于进一步区分null和undefined。
Boolean类型
该类型只有两个字面值:true和false。这两个值与数字值不是一回事,因此true不一定等于1,而false也不一定等于0。
需要注意的是,Boolean类型的字面值true和false是区分大小写的。也就是说,True和False(以及其他的混合大小写形式)都不是Boolean值,只是标识符
可以对任何数据类型的值调用Boolean()函数,而且总会返回一个Boolean值。
数据类型 | 转换为true的值 | 转换为false的值 |
---|---|---|
Boolean | true | false |
String | 任何非空字符串 | “”(空字符串) |
Number | 任何非零数字值(包括无穷大) | 0和NaN |
Object | 任何对象 | null |
Undefined | n/a(不适用) | undefined |
Number类型
这种类型使用IEEE754格式来表示整数和浮点值
以八进制表示,字面值的第一位必须是0,然后是八进制数序列(0-7)。如果字面值中的数值超出了范围,那么前导零将被忽略,后面的数值将被当作十进制数值解析。
八进制字面量在严格模式下是无效的,会导致支持该模式的JavaScript引擎抛出错误。
十六进制字面值的前两位必须是0x,后跟任何十六进制数字(0F)。字母A~F可以大写,也可以小写
在进行算术计算时,所有以八进制和十六进制表示的数值最终都将被转换成十进制数值。
1.浮点数
- 如果浮点数本身就是一个整数(如果1.0),那么这个值就会作为整数值来保存
- 在默认情况下ECMASscript会将那些小数点后面带有6个零以上的浮点数值以及e表示法表示(例如,0.0000003会被转换成3e-7)
- 浮点数值的最高数度是17位小数,但精确度不如整数(例如0.1+0.2 = 0.30000000000000004)
if (a + b == 0.3) { // 不要做这样的测试
alert(“you got 0.3”);
}
2.数值范围
最小值保存在Number.MIN_VALUE中,这个值是5e-324,如果某次计算得到了一个超出JavaScript数值范围的值,如果这个数值是负数,则会被转换成-Infinity(负无穷)。
最大值保存在Number.MAX_VALUE中,这个值是1.7976931348623157e+308,如果某次计算得到了一个超出JavaScript数值范围的值,如果这个数值是正数,则会被转换成Infinity(正无穷)。
要想确定一个数值是不是位于最小和最大数值之间,可以使用isFinite()函数
3.NaN
NaN,即非数值(Not a Number)是一个特殊的数值,这个数值用于表示一个本来要返回数值的操作数未返回数值的情况(这样就不会抛出错误了)。
任何涉及NaN的操作(例如NaN/10)都会返回NaN,其次,NaN与任何值都不相等,包括NaN本身。
可以通过isNaN()函数判断是否”不是数值”。
4.数值转换
有3个函数可以把非数值转换成数值:Number()、parseInt()和parseFloat()。第一个函数,即转型函数Number可以用于任何数据类型,而另外两个函数则专门用于把字符串转换成数值。
Number()函数的转换规则如下:
- 如果是Boolean值,true和flase分别转换为1和0。
- 如果是数字型,只是简单的传入和返回。
- 如果是null值,返回0。
- 如果是undefined,返回NaN。
- 如果是字符串,遵循以下规则:
- 如果字符串中包含数字,则将其转换为十进制数值(忽略前导0)。
- 如果字符串中包括有效的浮点格式,则将其转换为对应的浮点数值(忽略前导0)。
- 如果字符串中包括有效的十六进制格式,将其转换成十进制整数值。
- 如果字符串是空的,则将其转换为0;
- 如果字符串包含除上述格式之外的字符,则将其转换为NaN。
- 如果是对象,则调用对象的valuerOf()方法,然后依照前面的规则转换返回值。如果转换的结果是NaN,则调用对象的toString()方法。然后再次依照前面的规则转换返回的字符串值
parseInt()方法 把一个字符串中在效的整数字符转换为一个整数(无法转换布尔类型,转换成NaN)。
在ECMAScript 3 javaScript引擎中,”070”被当成八进制字面量,而在ECMAScript 5 javaScript引擎中,parseInt已经不具有解析八进制的能力,因此前导的零会被认为无效,从而将这个值当成”70”,结果就得到十进制70,即使是在非严格格式下也会如此。
可以为这个函数提供第二个参数:转换时使用的基数(即多少进制),如果解析的值是十六进制格式的字符串,那么指定基数16(同样可以用2,8,19)作为第二个参数,例如:
var num = parseInt("AF", 16); // 175,字符串前面可以不带前面的"0x"
parseFloat()方法 把一个字符串中在效的浮点数字符转换为一个浮点数(只有第一个小数点是有效的),由于parseFloat()只解析十进制,因些它没有用第二个参数指定基数的方法,并且始终忽略前导零。
String类型
String类型用于表示由零或多个16位Unicode字符组成的字符序列,即字符串。字符串可以由双引号(”)或单引号(’)表示,不过,以双引开头的字符串也必须以双引号结尾,而以单引号开头的字符串也必须 以单引号结尾。
1.字符字面量
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.";
2.转换为字符串
要把一个值转换为一个字符串有两种方式。第一种是使用几乎每个值(数值、布尔值 、对象、字符串值)都有的toString()方法,但null 和undefined值没有这个方法。
第二种方法,在不知道转换的值是不是null或undefined的情况下,还可以使用转型函数String(),这个函数能够将任何类型的值转换为字符串。String()函数遵循下列转换规则:
- 如果值有toString()方法,则调用该方法(没有参数)并返回相应的结果;
- 如果值是null,则返回”null”;
- 如果值是undefined,则返回”undefined”;
*注:要把某个值转换为字符串,可以使用加号操作符把它与一个字符串(” “)加在一起
Object类型
ECMAScript中的对象其实就是一组数据和功能的集合。对象可以通过执行new操作符后跟要创建的对象类型的名称来创建。而创建Object类型的实例并为其添加属性和(或)方法,就可以创建自定义对象,如下所示:
var o = new Object();
如果不给构造函数传递参数,则可以省略后面的那一对圆括号,但不推荐这么做。
Object的每个实例都具有下列属性和方法:
- constructor:保存着用于创建当前对象的函数。对于前面的例子而言,构造函数(constructor)就是Object()。
- hasOwnProperty(object):用于检查给定的属性在当前对象实例中(而不是在实例原型中)是否存在。其中,作为参数的属性名(propertyName)必须以字符串形式指定(例如:o.hasOwnProperty(“name”))。
- isPrototypeOf(object):用于检查传入的对象是否是当前对象的原型。
- propertyIsEnumerable(propertyName):用于检查给定的属性是否能够使用for-in语句来枚举。与hasOwnProperty()方法一样,作为参数的属性名必须以字符串形式指定。
- toLocaleString():返回对象的字符串表示,该字符串与执行环境的地区对应。
- toString():返回对象的字符串表示 。
- valueOf():返回对象的字符串、数值或布尔值表示。通常与toString()方法的返回值相同。
由于在ECMAScript中Object是所有对象的基础,因此所有对象都具有这些基本的属性和方法。
操作符
ECMA-262描述了一组用于操作数据值的操作符,包括算术操作符(如加号和头号)、位操作符、关系操作符和相等操作符,它们能够适用于很多值,例如字符串、数字值、布尔值、甚至对象。不过,在应用于对象时,相应的操作符通常都会调用对象的valueOf()和(或)toString()方法,以便取得可以操作的值。
一元操作符
1.递增和递减操作符
递增和递减操作符分前置型和后置型。顾名思义,前置型应该位于要操作的变量之前,而后置型则应该位于要操作的变量之后。因此,在使用前置递增操作符给一个数值加1是地,要把两个加号(++)放在这个数值变量之前,如下所示:
var age = 29;
++age;
执行这个前置递增操作与执行以下的操作效果相同:
var age = 29;
age = age + 1;
执行前置递增和递减操作时,变量的值都是在语句被求值以前改变的。(在计算机科学领域中,这种情况通常被称作副效应)。
由于前置递增和递减操作与执行语句的优先级相等,因此整个语句会从左至右被求值。例:
var num1 = 2;
var num2 = 20;
var num3 = nmu1 + num2; // 等于21
var num4 = num1 + num2; // 等于21
后置递增和递减与前置递增和递减有一个非常重要的区别,即递增和递减操作是在包含它们的语句求值之后才执行的。这个区别在某些情况下不是问题,例如:
var age = 29;
age++;
把递增操作符放在变量后面并不会改变语句的结果,因为递增是这条语句的唯一操作。但是,当语句中还包含其操作时,上述区别就会非常明显了,例:
var num1 = 2;
var num2 = 20;
var num3 = num1-- + num2; // 22
var num4 = num1 + num2; // 21
在应用于不同的值时,递增和递减操作符遵循下列规则:
- 在应用于一个包含有效数字字符的字符串时,先将其转换为数字值,再执行加减1。字符串变量变成数值变量。
- 在应用一个不包含有效数字字符的字符串时,将变量的值设置为NaN。字符串变量变成数值变量。
- 在应用于布尔值false时,先将其转换为0再执行加减1的操作。布尔值变量变成数值变量。
- 在应用于布尔值true时,先将其转换为1再执行加减1的操作。布尔值变量变成数值变量。
- 在应用于浮点数值时,执行加减1的操作(注意浮点舍入会有错误)。
- 在应用于对象时,先调用对象的valueOf()方法以取得一个可供操作的值。然后对该值应用前述规则。如果结果是NaN,则调用toString()方法后再应用前述规则。对象变量变成数值变量。
应用于对象的例子:
var o = {
valueOf: function () {
return -1;
}
};
o--;
2.一元加和减操作符
在对非数值应用一元加操作符时,该操作符会像Number()转型函数一样对这个值执行转换。下面的例子展示了对不同数据类型应该一元加操作符的结果:
var s1 = "01";
var s2 = "1.1";
var s3 = "z";
var b = "false";
var f = 1.1;
var o ={
valueOf: function () {
return -1;
}
}
s1 = +s1; // 值变成数值1
s2 = +s2; // 值变成数值1.1
s3 = +s3; // 值变成NaN
b = +b; // 值变成数值0
f = +f; // 值未变,仍然是1.1
o = +o; // 值变成数值-1
一元减操作符应用于数值时,该值会变成负数。而当应用于非数值时,一元减操作符遵循与一元加操作符相同的规则,最后再将得到的数值转换为负数。
位操作符
位操作符用于最基本的层次上,即按内存中表示数值的位来操作数值。位操作符并不直接操作64位的值,而是先将64位的值转换成32位的整数,然后执行操作,最后再将结果转换回64位,但这个转换过程中也导致了一个严重的副效应,即在对特殊的NaN和Infinity值应用位操作时,这两个值都会被当成0来处理。
对于有符号的整数,32位中的前31位用于表示整数的值。第32位用于表示数值的符号(从右往左数):0表示正数,1表示负数。这个表示符号的位叫做符号位,符号位的值决定了其他位数值的格式。其中,正数以纯二进制格式存储。
负数同样以二进制码存储。但使用的格式是二进制补码。计算一个数值的二进制补码,需经过下列3个步骤:
(1)求这个数值绝对值的二进制码;
(2)求二进制反码,即0替换为1,将1替换为0;
(3)得到的二进制反码加1。
要根据这3个步骤求得-18的二进制码,首先就要先求得18的二进制码,即:
0000 0000 0000 0000 0000 0000 0001 0010
然后,求其二进制反码,即0和1互换:
1111 1111 1111 1111 1111 1111 1110 1101
最后,二进制反码加1:
1111 1111 1111 1111 1111 1111 1110 1110
ECMAScript会尽力向我们隐藏所有这些信息。换句话说,在以二进制字符串形式输出一个负数时,我们看到的只是这个负数绝对值的二进制码加上了一个负号。如下面的例子所示:
var num = -18;
alert(num.toString(2)); // "-10010"
1.按位非(NOT)
按位非操作符由一个波浪线(~)表示,执行按位非的结果就是返回数值的反码。
var num1 = 25;
var num2 = ~num1;
alert(num2); // -26
下面的代码也能得到相同的结果:
var num1 = 25;
var num2 = -num1 - 1;
alert(num2); // -26
虽然以上代码也能返回同样的结果,但由于按位非是在数值表示的最底层执行操作,因此速度更快。
2.按位与(AND)
按位与操作符由一个和号字符(&)表示,它有两个操作符数。从本质上讲与操作就是将两个数值的对应位作比较,都是1时才返回1,任何一位为0.,结果都是0。下面看一个例子:
var result = 25 & 3;
alert(result); // 1
其原理底层操作:
25 = 0000 0000 0000 0000 0000 0000 0001 1001
3 = 0000 0000 0000 0000 0000 0000 0000 0011
AND = 0000 0000 0000 0000 0000 0000 0000 0001
因此最终结果等于1。
3.按位或(OR)
按位或操作符由一个竖线符号(|)表示,同样也有两个操作数。按位或在有一个1时的情况下返回1,而只有在两个位都是0的情况下才返回0。
var result = 25 | 3;
alert(result); // 27
4.按位异或(XOR)
按位异或操作符由一个插入符号(^)表示,也有两个操作数。按位异或与按位或的不同之处在于,这个操作在两个数值对应位上只有一个1时才返回1,如果对应的两位都是1或都是0,则返回0。
var result = 25 ^ 3;
alert(result); // 26
5.左迁
左迁操作符由两个小于号(<<)表示,这个操作符会将数值的所有位向左移动指定的位数。例如:
var oldValue = 2; // 等于二进制的10
var newValue = oldValue << 5; // 等于二进制的1000000,十进制的64
左操作符会以0来填充这些空位,以便得到的结果是一个完整的32位进制数。如果超过了32位,将会得到0。
注意:右移不会影响操作数的符号位。换句话说,如果将-2向左移动5位,结果将是-64,而非64。
6.有符号右移
有符号的右移操作符由两个大于号(>>)表示,这个操作符会将数值向右移动,但保留符号位(即正负号标记)。有符号的右移操作与左移操作恰好相反。例如:
var oldValue = 64; // 等于二进制的1000000
var newValue = oldValue >> 5; // 等于二进制的10,即十进制的2
同样右移操作符也会以0来填充这些空位,只不过这次的空位出现在原数值的左侧、符号位的右侧。
7.无符号右移
无符号右移操作符由3个大于号(>>>)表示,这个操作符会将数值的所有32位都向右移。对正数来说,无符号右移的结果与有符号右移相同。但对负数的结果就不一样了,无符号右移会把负数的二进制码当成正数的二进制码,由于负数以其绝对值的二进制补码表示,因此就会导致无符号右移的结果非常之大:
var oldValue = -64; // 等于二进制的1111111111111111111111111000000
var newValue = oldValue >>> 5; // 等于二进制的134217726
布尔操作符
1.逻辑非
逻辑非操作符由一个叹号(!)表示,可以应用于ECMAScript中的任何值。无论这个值是什么数据类型,这个操作符都会返回一个布尔值。逻辑非操作符首先会将它的操作数转换为一个布尔值,然后再对其求反。逻辑非遵循下列规则:
- 操作数是一个对象,返回false;
- 操作数是一个空字符串,返回true;
- 操作数是一个非空字符串,返回false;
- 操作数是数值0,返回true;
- 操作数是任意非0的数值(包括Infinity),返回false;
- 操作数是null,返回true;
- 操作数是NaN,返回true;
- 操作数是undefined,返回true;
*注:同时使用两个逻辑非操作符,实际上就会模拟Boolean()转型函数的行为。
2.逻辑与
逻辑与操作符由两个和号(&&)表示,有两个操作数。逻辑与操作可以应用于任何类型的操作数,而不仅仅是布尔值。在有一个操作数不是布尔值的情况下,逻辑与操作就不一定返回布尔值。它遵循以下规则:
- 如果第一个操作数为true,则返回第二个操作数,否则则返回第一个操作数。
- 如果其中有一个操作数是null,则返回null;
- 如果其中有一个操作数是NaN,则返回NaN;
- 如果其中有一个操作数是undefined,则返回undefined.
逻辑与操作符属于短路操作,即如果第一个操作数能够决定结果,那么就不会再对第二个操作数求值。
3.逻辑或
逻辑或操作符由两个竖线符号(||)表示,有两个操作符。
逻辑或与逻辑与操作相似,如果有一个操作数不是布尔值,逻辑或也不一定返回布尔值。它遵循以下规则 :
- 如果第一个操作数为true,则返回第一个操作数,否则则返回第二个操作数。
- 如果两个操作数都是null,则返回null;
- 如果两个操作数都是NaN,则返回NaN;
- 如果两个操作数都是undefined,则返回undefined.
逻辑或操作符也是短路操作,即第一个操作数的求值结果为true,那么就不会再对第二个操作数求值。
我们可以利用逻辑或的这一行为来避免为变量赋值null或undefined值。
乘性操作符
如果参与乘性计算的某个操作数不是数值,后台会先使用Number()转型函数将其转换为数值。也就是说,空字符串将被当作0,布尔值true将被当作1。
1.乘法
乘法操作符由一个星号(*)表示,用于计算两个数值的乘积。
在处理特殊值的情况下,乘法操作符遵循下列特殊的规则:
- 如果有一个操作数是NaN,则结果是NaN;
- 如果是Infinity与0相乘,则结果是NaN;
- 如果Infinity与非0数值相乘,则结果是Infinity或-Infinity,取决与有符号操作数的符号;
- 如果Infinity与Infinity相乘,则结果是Infinity;
- 如果有一个操作数不是数值,则在后台调用Number()将其转换为数值,然后再应用上面规则。
2.除法
除法操作符由一个斜线符号(/)表示,执行第一个操作数除第二个操作数的计算。
与乘法操作符类似,除法操作符对特殊的值也有特殊的处理规则。这些规则如下:
- 如果有一个操作数是NaN,则结果是NaN;
- 如果是Infinity被Infinity除,则结果是NaN;
- 如果是零被零除,则结果是NaN;
- 如果是非零的有限数被零除,则结果是Infinity或-Infinity,取决于有符号操作数的符号;
- 如果是Infinity被任何非零数值除,则结果是Infinity或-Infinity,取决于有符号操作数的符号;
3.求模
求模(余数)操作符由一个百分号(%)表示。
规则如下:
- 如果操作数都是数值,执行常规的除法计算,返回除得的余数;
- 如果被除是无穷大值而除数是有限大值,则结果是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;
- 如果两个操作数都是字符串,则将第一个操作数与第二个操作数拼接起来;
- 如果只有一个操作数是字符串,则将另一个操作数转换成字符串,然后拼接起来。
2.减法
规则如下:
- 如果有一个操作数是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()方法以取得表示该对象的数值。如果对象没有valueOf()方法,则调用其toString()方法并将得到的字符串转换为数值。
关系操作符
小于(<)、大于(>)、小于等于(<=)、大于等于(>=)这几个关系操作符用于对两个值进行比较,并且都会返回一个布尔值。、
当关系操作符的操作数使用了非数值时,也要进行数据转换。遵循以下规则:
- 如果两个操作数都是数值,则执行数值比较。
- 如果两个操作数都是字符串,则比较两个字符串对应的字符编码值。
- 如果一个操作数是数值,则将另一个操作数转换为一个数值,然后执行数值比较
- 如果一个操作数是对象,则调用这个对象的valueOf()方法,用得到的结果按照前面的规则执行比较。如果对象没有valueOf()方法,则调用toString()方法,再进行比较。
- 任何操作数与NaN进行关系比较,结果都是false。
在比较字符串时,实际比较的是两个字符串中对应位置的每个字符的字符编码值。由于大写字母的字符编码全部小于小写字母的字符编码
,如果要进行比较需转换成相同的大小写形式,然后再执行比较,如下所示:
var result = "Brick".toLowerCase() < "alphabet".toLowerCase(); // flase
相等操作符
相等和不相等——先转换再比较,全等和不全等——仅比较而不转换。
1.相等和不相等
相等操作符(==),不相等操作符(!=),这两个操作符都会先转换操作数(通常称为强制转型),然后再比较相等性。
转换不同类型时,遵循下列原则 :
- 如果有一个操作数是布尔值,fase 转换为0,true 转换为1进行比较。
- 一个是字符串,一个是数值,会先将字符串转换为数值进行比较。
- 如果一个是对象,另一个操作数不是,则调用对象的valueOf()方法,用得到的基本类型值按照前面的规则进行比较。
两个操作符在进行比较时则要遵循以下原则:
- null和undefined是相等的
- 要比较相等性之前,不能将null和undefined转换成其他任何值。
- 如果一个操作数是NaN,相等操作符也返回false,而不相等操作符返回true。
重要提示:即使两个数都是NaN,相等操作符也返回fase,不相等返回true
。 - 如果两个操作数都是对象,则比较它们是不是同一个对象。如果是返回true。
2.全等和不全等
它只在两个操作符未经转换就相等的情况下返回true。
null == undefined 会返回 true,因为它们是类似的值;但null === undefined会返回false,因为它们是不同类型的值。
*注:由于相等和不相等操作符存在类型转换问题,而为了保持代码中数值类型的完整性,我们推荐使用全等和不全等操作符。
条件操作符
// 在这个例子中,max将会保存一个最大值
var max = (num1 > num2) ? num1: num2;
赋值操作符
在等于号(=)前面添加乘性操作符、加性操作符、位操作符,就可以完成复合赋值操作,例如:
// 两者相等
var num = num +10;
var num += 10;
设计这些操作符的主要目的就是简化赋值操作。使用它们不会带来任何性能上的提升。
逗号操作符
逗号操作符多用于声名多个变量;但除此之外 ,逗号操作符还可以用于赋值。逗号操作符总会返回表达式中的最后一项,例:
var num = (1, 2, 3, 4, 5); // num 的值为5
语句
if语句
if (condition) statement1 else statement2
ECMAScript会自动调用Boolean()转换函数将这个表示式的结果转换为一个布尔值。
使用if语句推荐始终使用代码块方式,即使要执行的只有一行代码
。因为这样代码看起来更清晰。
if (i > 5) {
alert("棒!");
}
do-while语句
do-while 语句是一种后测试循环语句,即只有在循环体中的代码执行之后,才会测试出口条件。循环体内的代码至少会被执行一次。示例:
var i = 0;
do {
i += 2;
} while (i < 10);
alert(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);
}
使用while循环做不到的,使用for循环同样也做不到。for循环只是把与循环有关的代码集中在了一个位置
。
由于ECMAScritp中不存在块级作用域(第4章进一步讨论这一点),因此在循环内部定义的变量也可以在外部访问到。
此外,for语句中的初始化表达式、控制表达式和循环后表达式都是可选的。将这三个表达式全部省略,就会创建一个无限循环。
for-in语句
for-in语句是一种精准的迭代语句,可以用来枚举对象
的属性。示例:
// 这里 var 操作符也不是必须的,为了保证使用局部变量,推荐使用这种方式
for (var propName in windown) {
document.write(propName);
}
ECMAScript 对象的属性没有顺序,因此,通过 for-in 循环输出的属性名的顺序是不可预的,返回的先后次序可能会因浏览器而异。
如果表示要迭代的对象的变量值为 null 或 undefined, for-in 语句会抛出错误。为了保证最大限度的兼容性,建议在使用 for-in 循环之前,先检测确认该对象的值不是 null 和 undefined。
label语句
使用label语句可以在代码中添加标签,以便将来使用。示例:
start:
for (var i = 0; i < count; i++) {
alert(i);
}
加标签的语句一般都要与for语句等循环语句配合使用。
break和continue语句
break 语句会立即退出循环,强制继续执行循环后面的语句。而continue语句虽然也是立即退出循环,但退出循环后会从循环的顶部继续执行。示例:
var num = 0;
outermost:
for (var i = 0; i < 10; i++) {
for (var j = 0; j < 10; j++) {
if (i == 5 && j == 5) {
continue outermost;
}
num++
}
}
alert(num); // 95
使用label语句,一定要使用描述性标签,同时不要嵌套过多的循环。
with语句
with 语句的作用是将代码的作用域设置到一个特定的对象中。示例:
var qs = location.search.substring(1);
var hostName = location.hostname;
var url = location.href;
// 上下代码结果一致
with (location) {
var qs = search.substring(1);
var hostName = hostname;
var url = href;
}
严格模式一不允许使用with语句,否则将视为语法错误。
*注:由于大量使用 with 语句会导致性能下降,同时也会给调试代码造成困难,因此在开发大型应用程序时,不建议使用 with 语句。
switch语句
使用 switch 语句可以用来替换频繁的 if else 语句,例如:
if (i == 25) {
alert("25");
} else if (i == 35) {
alert("35");
} else if (i == 45) {
alert("45");
} else {
alert("Other");
}
// 而与此等价的 switch 语句
switch (i) {
case 25:
alert("25");
break;
case 35:
alert("35");
break;
case 45:
alert("45");
break;
default:
alert("Other");
}
可以在 switch 语句中使用任何数据类型,无论是字符串还是对象都没问题。其次,每个 case 的值不一定是常量,也可以是变量,甚至是表达式。
*注:switch 语句在比较值时使用的是全等操作符,因此不会发生类型转换。
函数
ECMAScript 中的函数使用 function 关键字来声名,后跟一组参数以及函数体。
ECMAScript 中的函数在定义时不必指定是否返回值。
*注:推荐的做法是要么让函数始终都返回一个值,要么永远都不要返回值。否则,如果函数有时候返回值,有时候又不返回值,会给调试代码带来不便。
严格模式对函数有一些限制:
- 不能把函数命名为 eval 或 arguments;
- 不能把参数命名为 eval 或 arguments;
- 不能出现两个命名参数同名的情况。
理解参数
ECMAScript 函数不介意传递进来多少个参数,也不在乎传进来参数是什么数据类型,原因是ECMAScript 中的参数在内部是用一个数组来表示的。函数体内可以通过 arguments 对象来访问这个参数数组。
- 用来封闭实参的对象
- 当函数形参确定不了的时候,可以使用arguments
- arguments是一个类数组对象,它也可以通过索引来操作数据,也可以获取长度
- 在调用函数时,我们所传递的实参都会在arguments中保存
- 我们即使不定义形参,也可以通过arguments来使用实参
- arguments[0]表示第一个实参
- arguments[1]表示第二个实参
- arguments[1]表示第二个实参
- 属性对应一个函数对象,就是当前正在指向的函数的对象
命名的参数只提供便利,但不是必需的。通过访问 arguments 对象的 length 属性可以获知有多少个参数传递给了函数,开发人员可以利用这点让函数接收任意个参数并分别实现适当的功能。例:
function doAdd() {
if (arguments.length == 1) {
alert(arguments[0] + 10);
} else if (arguments.length == 2) {
alert(arguments[0] + arguments[1]);
}
}
doAdd(10); // 20
doAdd(30, 20); // 50
arguments 的值永远与对应命名参数的值保持同步。修改 arguments 数值中的值,就等同于修改了对应参数的值,但他们的内存空间依然是独立的 。
*注:ECMAScript 中的所有参数传递的都是值,不可能通过引用传递参数。
没有重载
在ECMAScript 中定义两个名字相同的函数,则该名字只属于后定义的函数。
通过检查传入函数中参数的类型和数量并作出不同反应,可以模仿方法的重载。
第4章 变量、作用域和内存问题
基本类型和引用类型的值
基本类型值指的是简单的数值段,而引用类型值指那些可能由多个值构成的对象。
其中 5 种基本类型是按值访问的,因为可以操作保存在变量中的实际的值。引用类型的值是保存在内存中(JavaScript不允许访问内存中的位置)的对象,在操作对象时,实际上是在操作对象的引用而不是实际的对象(当复制保存着对象的某个变量时,操作的是对象的引用。但在为对象添加属性时,操作的是实际的对象
)。
*注:在很多语言中,字符串以对象的形式来表示,因此被认为是引用类型。ECMAScript 放弃了这一传统。
动态的属性
对于引用类型的值,我们可以为其添加属性和方法,也可以改变和删除其属性和方法。但是,我们不能给基本类型的值添加属性,尽管这样做不会导致任何错误。这说明只能给引用类型值动态地添加属性,以便将来使用。
复制变量值
如果从一个变量向另一个变量复制基本类型
的值,会在变量对象上创建一个新值,然后把该值复制到为新变量分配的位置上。
如果从一个变量向另一个变量复制引用类型
的值,不同的是放到新变量空间的值实际上是一个指针,而这个指针指向存储在堆中的一个对象。两个变量实际上引用同一个对象。
传递参数
在向参数传递基本类型的值时,被传递的值会被复制给一个局部变量。在向参数传递引用类型的值时,会把这个值在内存中的地址复制给一个局部变量,因此这个局部变量的变化会反映在函数的外部,示例:
function doAdd() {
function addTen(num) {
num += 10;
return num;
}
var count = 20;
var result = addTen(count);
alert(count); //20,没有变化
alert(result); //30
使用引用类型值时:
function doAdd() {
function setName(obj) {
obj.name = "Nicholas";
}
var person = new Object();
setName(person);
alert(person.name); // "Nicholas"
有很多开发人员错误地认为:在局部作用域中修改的对象会在全局作用域中反映出来,就说明参数是按引用传递的。为了证明对象是按值传递的,示例:
function setName(obj) {
obj.name = "Nicholas";
// 修改了以下部分
obj = new Object();
obj.name = "Greg";
}
var person = new Object();
setName(person);
alert(person.name); // "Nicholas"
实际上,当在函数内部重写obj 时,这个变量引用的就是一个局部对象了。而这个局部对象会在函数执行完毕后立即被销毁。
检测类型
确定一个值是哪种引用类型可以使用instanceof 操作符。
如果变量是给定引用类型(根据它的原型链来识别;第6 章将介绍原型链)的实例,那么instanceof 操作符就会返回true。如果使用instanceof 操作符检测基本类型的值,则该操作符始终会返回false,因为基本类型不是对象。
执行环境及作用域
每个执行环境都有一个与之关联的变量对象(variable object),环境中定义的所有变量和函数都保存在这个对象中。全局执行环境是最外围的一个执行环境。某个执行环境中的所有代码执行完毕后,该环境被销毁,保存在其中的所有变量和函数定义也随之销毁(全局执行环境直到应用程序退出——例如关闭网页或浏览器——时才会被销毁)。
如果这个环境是函数,则将其活动对象(activation object)作为变量对象。活动对象在最开始时只包含一个变量,即arguments 对象(这个对象在全局环境中是不存在的)。作用域链中的下一个变量对象来自包含(外部)环境,而再下一个变量对象则来自下一个包含环境。这样,一直延续到全局执行环境。
var color = "blue";
function changeColor(){
var anotherColor = "red";
function swapColors(){
var tempColor = anotherColor;
anotherColor = color;
color = tempColor;
// 这里可以访问color、anotherColor 和tempColor
}
// 这里可以访问color 和anotherColor,但不能访问tempColor
swapColors();
}
// 这里只能访问color
changeColor();
每个环境都可以向上搜索作用域链,以查询变量和函数名;但任何环境都不能通过向下搜索作用域链而进入另一个执行环境。
延长作用域链
有些语句可以在作用域链的前端临时增加一个变量对象,在两种情况下会发生这种现象。
- try-catch 语句的catch 块;
- with 语句。
这两个语句都会在作用域链的前端添加一个变量对象。示例:
function buildUrl() {
var qs = "?debug=true";
with(location){
var url = href + qs;
}
return url;
}
没有块级作用域
示例:
if (true) {
var color = "blue";
}
alert(color); // "blue"
这里是在一个if 语句中定义了变量color。如果是在C、C++或Java 中,color 会在if 语句执行完毕后被销毁。但在JavaScript 中,if 语句中的变量声明会将变量添加到当前的执行环境(在这里是全局环境)中。
1.声名变量
使用var 声明的变量会自动被添加到最接近的环境中。如果初始化变量时没有使用var 声明,该变量会自动被添加到全局环境。
*注:不声明而直接初始化变量是一个常见的错误做法,因为这样可能会导致意外。我们建议在初始化变量之前,一定要先声明。
2.查询标识符
如果如果局部环境中存在着同名标识符,就不会使用位于父环境中的标识符。
垃圾收集
JavaScript 具有自动垃圾收集机制,找出那些不再继续使用的变量,然后释放其占用的内存。为此,垃圾收集器会按照固定的时间间隔(或代码执行中预定的收集时间),周期性地执行这一操作。
1.标记清除
JavaScript 中最常用的垃圾收集方式是标记清除。当变量进入环境(例如,在函
数中声明一个变量)时,就将这个变量标记为“进入环境”。而当变量离开环境时,则将其标记为“离开环境”。
垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记(当然,可以使用任何标记方式)。然后,它会去掉环境中的变量以及被环境中的变量引用的变量的标记。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。
2.引用计数
另一种不太常见的垃圾收集策略叫做引用计数(reference counting)。引用计数的含义是跟踪记录每个值被引用的次数。
引用计数有一个严重的问题,循环引用。循环引用指的是对象A 中包含一个指向对象B 的指针,而对象B 中也包含一个指向对象A 的引用,因为它们的引用次数永远不会是0。
为了避免类似这样的循环引用问题,可以使用下面的代码消除前面例子创建的循环引用:
myObject.element = null;
element.someObject = null;
3.性能问题
垃圾收集器是周期性运行的,而且如果为变量分配的内存数量很可观,那么回收工作量也是相当大的。
在这种情况下,确定垃圾收集的时间间隔是一个非常重要的问题。
IE7改变了工作方式:如果垃圾收集例程回收的内存分配量低于15%,则变量、字面量和(或)数组元素的临界值就会加倍。如果例程回收了85%的内存分配量,则将各种临界值重置回默认值。
4.管理内存
分配给Web浏览器的可用内存数量通常要比分配给桌面应用程序的少。这样做的目的主要是出于安全方面的考虑,目的是防止运行JavaScript 的网页耗尽全部系统内存而导致系统崩溃。因此,确保占用最少的内存可以让页面获得更好的性能。
一旦数据不再有用,最好通过将其值设置为null 来释放其引用——这个做法叫做解除引用。解除引用的真正作用是让值脱离执行环境,以便垃圾收集器下次运行时将其回收
。
第5章 引用类型
引用类型有时候也被称为对象定义
,因为它们描述的是一类对象所具有的属性和方法。对象是某个特定引用类型的实例
。新对象是使用new 操作符后跟一个构造函数
来创建的。构造函数本身就是一个函数,只不过该函数是出于创建新对象的目的而定义的。
Object 类型
在对象字面量中,使用逗号来分隔不同的属性,在最后一个属性后面添加逗号,会在IE7 及更早版本和Opera 中导致错误。
在使用对象字面量语法时,属性名也可以使用字符串。如果要使用一些特殊的变量名时,则必须加引号。
*注:在通过对象字面量定义对象时,实际上不会调用Object 构造函数(Firefox 2 及更早版本会调用Object 构造函数;但Firefox 3 之后就不会了)。
在JavaScript 也可以使用方括号表示法来访问对象的属性。在使用方括号语法时,应该将要访问的属性以字符串的形式放在方括号中,示例:
alert(person["name"]); // "Nicholas"
alert(person.name); // "Nicholas"
从功能上看,这两种访问对象属性的方法没有任何区别。但方括号语法的主要优点是可以通过变量来访问属性,例如:
var propertyName = "name";
alert(person[propertyName]); // "Nicholas"
如果属性名中包含会导致语法错误的字符,或者属性名使用的是关键字或保留字,也可以使用方括号表示法。例如:
person["first name"] = "Nicholas";
通常,除非必须使用变量来访问属性,否则我们建议使用点表示法。
Array 类型
ECMAScript 数组的每一项可以保存任何类型的数据。ECMAScript 数组的大小随着数据的添加自动增长以容纳新增数据。
使用构造函数创建数组时,如果传递的是数值,则会按照该数值创建包含给定项数的数组。
使用字面量创建数组时,可能会引起错误:
var values = [1,2,]; // 可能会创建一个包含2 或3 项的数组
var options = [,,,,,]; // 可能会创建一个包含5 或6 项的数组
*注:与对象一样,在使用数组字面量表示法时,也不会调用Array 构造函数(Firefox 3及更早版本除外)。
数组的项数保存在其length 属性中,这个属性始终会返回0 或更大的值,数组的length 属性很有特点——它不是只读的
。因此,通过设置这个属性,可以从数组的末尾移除项或向数组中添加新项。请看下面的例子:
var colors = ["red", "blue", "green"]; // 创建一个包含3 个字符串的数组
colors.length = 2;
alert(colors[2]); // undefined
检测数组
判断某个对象是不是数组,对于一个网页,或者一个全局作用域而言,使用instanceof 操作符就能得到。
instanceof 操作符的问题在于,它假定只有一个全局执行环境。如果网页中包含多个框架,那实际上就存在两个以上不同的全局执行环境,从而存在两个以上不同版本的Array 构造函数。如果你从一个框架向另一个框架传入一个数组,那么传入的数组与在第二个框架中原生创建的数组分别具有各自不同的构造函数。
为了解决这个问题,ECMAScript 5 新增了Array.isArray()方法。这个方法的目的是最终确定某个值到底是不是数组,而不管它是在哪个全局执行环境中创建的。
转换方法
所有对象都具有toLocaleString()、toString()和valueOf()方法。例:
var colors = ["red", "blue", "green"]; // 创建一个包含3 个字符串的数组
alert(colors.toString()); // red,blue,green
console.log(colors.valueOf()); // Array(3) [ "red", "blue", "green" ]
alert(colors); // red,blue,green
toLocaleString()和toString()的区别:
- 如果是toString(),会直接返回标准的格式;
- 如果是toLocaleString(),先判断是否指定语言环境(locale),指定的话则返回当前语言环境下的格式设置(options)的格式化字符串;没有指定语言环境(locale),则返回一个使用默认语言环境和格式设置(options)的格式化字符串。
var number = 1337;
var date = new Date();
var myArr = [number, date, “foo”];
var str = myArr.toLocaleString();
console.log(str); // 1,337,2020/6/25 下午5:53:20,foo
使用join()方法,则可以使用不同的分隔符来构建这个字符串。join()方
法只接收一个参数,即用作分隔符的字符串,然后返回包含所有数组项的字符串。
*注:如果数组中的某一项的值是null 或者undefined,那么该值在join()、toLocaleString()、toString()和valueOf()方法返回的结果中以空字符串表示。
栈方法
栈是一种LIFO(Last-In-First-Out,后进先出)的数据结构,也就是最新添加的项最早被移除。通过push()和pop()方法,以便实现类似栈的行为。
push()
方法可以接收任意数量的参数,把它们逐个添加到数组末尾,并返回修改后数组的长度。而pop()
方法则从数组末尾移除最后一项,减少数组的length 值,然后返回移除的项。示例:
var colors = new Array(); // 创建一个数组
var count = colors.push("red", "green"); // 推入两项
alert(count); // 2
var item = colors.pop(); // 取得最后一项
alert(item); // "black"
alert(colors.length); // 2
队列方法
队列数据结构的访问规则是FIFO(First-In-First-Out,先进先出)。队列在列表的末端添加项,从列表的前端移除项。结合使用shift()和push()方法,可以像使
用队列一样使用数组。示例:
shift()
方法能够移除数组中的第一个项并返回该项,同时将数组长度减1。
var colors = new Array(); //创建一个数组
var count = colors.push("red", "green"); //推入两项
alert(count); //2
var item = colors.shift(); //取得第一项
alert(item); //"red"
alert(colors.length); //2
ECMAScript 还为数组提供了一个unshift()方法。顾名思义,unshift()
与shift()的用途相反:它能在数组前端添加任意个项并返回新数组的长度。因此,同时使用unshift()和pop()方法,可以从相反的方向来模拟队列。
重排序方法
数组中已经存在两个可以直接用来重排序的方法:reverse()和sort()。
reverse()
方法会反转数组项的顺序,该方法会直接修改原数组。示例:
var values = [1, 2, 3, 4, 5];
values.reverse();
alert(values); // 5,4,3,2,1
sort()
方法默认按升序排列数组项,sort()方法会调用每个数组项的toString()转型方法,然后比较得到的字符串。即使数组中的每一项都是数值,sort()方法比较的也是字符串,该方法会影响原数组。示例:
var values = [0, 1, 5, 10, 15];
values.sort();
alert(values); // 0,1,10,15,5
因此sort()方法可以接收一个比较函数作为参数,以便我们指定哪个值位于哪个值的前面。比较函数接收两个参数,并根据函数的返回值来决定元素的顺序。如果返回一个大于0的值,则元素会交换位置,如果返回一个小于0的值,则元素位置不变,如果返回一个等于0的值,也不交换位置。示例:
function compare(value1, value2) {
if (value1 < value2) {
return -1;
} else if (value1 > value2) {
return 1;
} else {
return 0;
}
}
var values = [0, 1, 5, 10, 15];
values.sort(compare);
alert(values); // 0,1,5,10,15
可以使用一个更简单的比较函数。这个函数只要用第二个值减第一个值即可。
// 降序,如果是升序改变return值就行
function compare(value1, value2){
return value2 - value1;
}
操作方法
concat()
方法可以基于当前数组中的所有项创建一个新数组。这个方法可以连接两个或多个数组,并将新的数组返回,该方法不会对原数组产生影响。示例:
var colors = ["red", "green", "blue"];
var colors2 = colors.concat("yellow", ["black", "brown"]);
alert(colors); // red,green,blue
alert(colors2); // red,green,blue,yellow,black,brown
slice()
方法可以接受一或两个参数,即要返回项的起始和结束位置。在只有一个参数的情况下,slice()方法返回从该参数指定位置开始到当前数组末尾的所有项。如果有两个参数,该方法返回起始和结束位置之间的项——但不包括结束位置的项,该方法不会改变原数组。示例:
var colors = ["red", "green", "blue"];
var colors2 = colors.concat("yellow", ["black", "brown"]);
alert(colors); // red,green,blue
alert(colors2); // red,green,blue,yellow,black,brown
参数可以传递一个负值,如果传递一个负值,则从后往前计算。
splice()
可以用于删除数组中的指定元素,并向数组中添加新元素。第一个参数示开始位置的索引,包含开始索引,第二个参数表示删除的数量,第三个参数及以后的参数,可以向数组中开始位置索引前边插入新元素。使用此方法会影响到原数组,并返回删除的元素。示例:
var colors = ["red", "green", "blue"];
var removed = colors.splice(0,1); // 删除第一项
alert(colors); // green,blue
alert(removed); // red,返回的数组中只包含一项
removed = colors.splice(1, 0, "yellow", "orange"); // 从位置1 开始插入两项
alert(colors); // green,yellow,orange,blue
alert(removed); // 返回的是一个空数组
removed = colors.splice(1, 1, "red", "purple"); // 插入两项,删除一项
alert(colors); // green,red,purple,orange,blue
alert(removed); // yellow,返回的数组中只包含一项
位置方法
indexOf()
和lastIndexOf()
。这两个方法都接收两个参数:要查找的项和开始查找的位置(可选的)。如果字符串中含有该内容,则会返回其第一次出现的索引,如果没有找到指定的内容,则返回-1。不同的是indexOf()是从前往后找,而lastIndexOf是从后往前找。示例:
var numbers = [1,2,3,4,5,4,3,2,1];
alert(numbers.indexOf(4)); // 3
alert(numbers.lastIndexOf(4)); // 5
alert(numbers.indexOf(4, 4)); // 5
alert(numbers.lastIndexOf(4, 4)); // 3
迭代方法
ECMAScript 5 为数组定义了5 个迭代方法。传入这些方法中的函数会接收三个参数:数组项的值、该项在数组中的位置和数组对象本身。
- every():对数组中的每一项运行给定函数,如果该函数对每一项都返回true,则返回true。
- filter():对数组中的每一项运行给定函数,返回该函数会返回true 的项组成的数组。
- forEach():对数组中的每一项运行给定函数。这个方法没有返回值。
- map():对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组。
- some():对数组中的每一项运行给定函数,如果该函数对任一项返回true,则返回true。
var numbers = [1,2,3,4,5,4,3,2,1];
var everyResult = numbers.every(function(item, index, array) {
return (item > 2);
});
alert(everyResult); // false
var someResult = numbers.some(function(item, index, array) {
return (item > 2);
});
alert(someResult); // true
var filterResult = numbers.filter(function(item, index, array) {
return (item > 2);
});
alert(filterResult); // [3,4,5,4,3]
var mapResult = numbers.map(function(item, index, array) {
return item * 2;
});
alert(mapResult); // [2,4,6,8,10,8,6,4,2]
// 它只是对数组中的每一项运行传入的函数
numbers.forEach(function(item, index, array) {
//执行某些操作
})
支持这些迭代方法的浏览器有IE9+、Firefox 2+、Safari 3+、Opera 9.5+和Chrome。
归并方法
ECMAScript 5 还新增了两个归并数组的方法:reduce()和reduceRight()。这两个方法都会迭代数组的所有项,然后构建一个最终返回的值。其中,reduce()
方法从数组的第一项开始,逐个遍历到最后。而reduceRight()
则从数组的最后一项开始,向前遍历到第一项。示例:
var values = [1,2,3,4,5];
var sum = values.reduce(function(prev, cur, index, array){
return prev + cur;
});
alert(sum); //15
var sum = values.reduceRight(function(prev, cur, index, array){
return prev + cur;
});
alert(sum); //15
使用reduce()还是reduceRight(),主要取决于要从哪头开始遍历数组。除此之外,它们完全相同。
支持这两个归并函数的浏览器有IE9+、Firefox 3+、Safari 4+、Opera 10.5 和Chrome。