第三章 类型、值和变量
1、存取字符串、数字或布尔值的属性时创建的临时对象称做包装对象,它只是偶尔用来区分字符串值和字符串对象、数字和数值对象以及布尔值和布尔对象。
由于字符串不是对象,但是却有相应的属性,比如length属性。这时JavaScript就会将字符串通过调用new String()的方式转换成对象,这个对象继承了字符串的方法。同时这个对象是一个临时对象,一旦属性引用结束,这个新创建的对象就会销毁。
2、对象到字符串的转换步骤:
如果对象具有toString()方法,则调用这个方法。如果它返回一个原始值,JavaScript将这个值转换为字符串,并返回这个结果。
如果对象没有toString()方法,或者这个方法并不返回一个原始值,那么JavaScript会调用valueOf()方法。如果存在这个方法,则JavaScript调用它。如果返回值是原始值,JavaScript将这个值转换为字符串,并返回结果。
如果JavaScript无法从以上2个方法获取一个原始值,这时将抛出一个类型错误异常。
3、对象到数字的转换过程:
如果对象具有valueOf()方法,后者返回一个原始值,则JavaScript将这个原始值转换为数字并返回这个数字。
否则,如果对象具有toString()方法,后者返回一个原始值,则将去转换并返回。
否则,抛出类型错误异常。(TypeError)
toString()的作用是返回一个反映这个对象的字符串。同时很多类定义了特定版本的该方法。比如,数组类的toString()方法将每个数组元素转换为一个字符串,并在元素之间添加逗号后合并成结果字符串。
valueOf()方法的并未详细定义:如果存在任意原始值,它就默认将对象转换为表示它的原始值。如果对象是复合值,就简单地返回对象本身。
第四章 表达式和运算符
1、原始表达式是表达式的最小单位---不再包含其他表达式。JavaScript原始表达式包含常量或直接量、关键字和变量。需要注意一点的是,undefined是全局变量,和null不同,不是关键字。当JavaScript代码中出现了标识符,JavaScript会将其当做变量去查找它的值。如果变量名不存在,运算结果为undefined。严格模式中,对不存在的变量进行求值会抛出一个引用错误。
2、数组初始化表达式是通过一对方括号和数组内逗号隔开的列表构成。数组直接量中的列表逗号之间的元素可以省略,这时省略的空位会填充undefined。如;
var array = [1,,,,5]; //三个元素是undefined.
数组直接量的元素列表结尾处可以留下单个逗号,这时并不会创建一个新的值为undefined的元素。
3、属性访问表达式,在"."和"["之前的表达式总是会首先计算。如果计算结果为null或undefined,表达式就会抛出一个类型错误异常,因为这两个值都不能包含任意属性。比如:
var obj;
obj.attr // Uncaught TypeError: Cannot read property 'attr' of null
4、运算符
delete --> 删除属性
typeof -->检测操作数类型
void -->返回undefined值
instanceof -->测试对象类
in -->测试属性是否存在
,-->忽略第一个操作数,返回第二个操作数
需要注意的是,属性访问表达式和调用表达式的优先级要比所有运算符都要高。如:
typeof my.functions[x](y)
//尽管typeof是优先级最高的运算符之一,但typeof是在两次属性访问和函数调用之后执行的。
一元操作符、赋值和三元条件运算符都具有从右至做的结合性。比如:
x = ~-y;
w = x = y = z;
q = a?b:c?d:e?f:g;
//等价于一下代码
x = ~(-y);
w = (x = (y = z));
q = a?b:(c?d:(e?f:g));
1)子表达式的运算顺序
运算符的优先级是严格按照规定,但是并没有规定子表达式的计算过程中的运算顺序。
JavaScript总是严格按照从左至右的顺序来计算表达式。例如:
在表达式w = x + y * z中,将首先计算子表达式w,然后计算x、y和z。然后,y的值和z的值相乘,再加上x的值,最后将其赋值给表达式w所指代的变量或属性。
5、算术表达式
算术运算符进行运算时,所有无法转换为数字的操作符都转换为NaN值。如果操作数或者转换结果为NaN值,算术运算的结果也为NaN。求余结果的符号和第一个操作数的符号保持一致。
NaN + NaN
NaN - NaN
NaN * NaN
NaN / NaN
NaN % NaN
0/0
// 以上运算结果均为NaN
1) "+"运算符
总的来说,加号的转换规则优先考虑字符串链接。如果两个操作数不是类字符串的,那么将进行算术加法运算。
加法操作符的完整行为表现为:
如果其中一个操作数是对象,则对象会遵循对象到原始值得转换规则转换为原始类值:日期对象通过toString()方法执行转换,其他对象则通过valueOf()方法执行转换。由于对数对象不具备可用的valueOf()方法,因此会通过toString()方法来执行转换。
进行对象到原始值的转换后,如果其中一个操作数是字符串的话,另一个操作数也会转换为字符串,然后进行字符串连接。
否则,两个操作数都将转换为数字(或NaN),然后进行加法运算。
举个例子:
1 + 2 // 3:加法
"1" + "2" //"12":字符串连接
"1" + 2 //"12":数字转换为字符串后进行字符串连接
1 + {} // "1[object Object]":对象转换为字符串后进行字符串连接
true + true // 2:布尔值转换为数字相加
2 + null // 2:null转换为0后做加法
2 + undefined // NaN:undefined转换为NaN后相加
2) 一元算术运算符
一元运算符作用于一个单独的操作数,并产生一个新值。必要时,他们会将操作数转换为数字。所以表达式++x不总和x = x + 1完全一样。"++"运算符从不进行字符串连接操作,它总是会将操作数转换为数字并增1。如果x是字符串"1",++x就是先将字符串转换为数字同时增1,即2。而x + 1的结果是字符串"11"。
第五章 语句
严格模式与非严格模式的区别(前三条很重要):
1、在严格模式下禁止使用with语句。
2、在严格模式中,所有变量都要先声明。如果给一个未声明的变量、函数、函数参数、catch从句参数或全局对象的属性赋值,将抛出错误。(非严格模式中,隐式声明的全局变量的方法是给全局对象新添加一个新属性)。
3、在严格模式中,调用的函数中的一个this值是undefined。(非严格模式中,调用的函数中的this总是全局对象,比如window)。
4、严格模式中,当通过call()或apply()来调用函数时,其中的this值就是通过call()或apply()传入的第一个参数。(非严格模式中,null和undefined值被全局对象和转换为对象的非对象值所代替)。
5、严格模式中,给只读属性赋值和给不可扩展的对象创建新成员会抛出类型错误(非严格模式下,只是操作失败,并不报错)。
6、严格模式中,函数里的arguments对象拥有传入函数值的静态副本(?不是很理解)。非严格模式下,arguments里的数组元素和函数参数都是指向同一个值得引用。
7、严格模式下,当delete运算符后跟随非法的标识符(如变量、函数等)时,将抛出语法错误。非严格模式中,这种delete表达式什么也不做,并返回false。
8、严格模式中,在一个对象直接量中定义两个或多个同名属性将产生一个语法错误。(非严格模式不会报错)。同时在es6中,会进行覆盖,后声明的会覆盖先前的。
9、严格模式中,函数声明中存在两个或多个同名的参数将产生一个语法错误(非严格模式不报错)。
10、严格模式不允许使用八进制整数直接量。
第六章 对象
创建对象有三种方式,包括对象字面量、new和Object.create().
Object.create()接受两个参数。第一个参数是这个对象的原型。第二个可选参数用以对对象的属性进行进一步描述。
可以通过传入参数null来创建一个没有原型的新对象,但是通过这种方法创建的对象不会继承任何东西,甚至不包括基础方法。比如toString()。
如果想创建一个普通的空对象(比如{}),需要传入Object.prototype:
var obj = Object.create(Object.prototype); // 和new Object()一样
属性的查询和设置
可以通过点(.)或方括号([])运算符来获取对象属性的值。对于点(.)来说,右侧必须是一个以属性名称命名的简单标识符。对于方括号来说([]),方括号内必须是一个计算结果为字符串的表达式。更严格地讲,表达式必须返回字符串或返回一个可以转换为字符串的值。需要注意的是,如果属性的名字是数字的话,使用点运算符会报错,必须使用方括号进行查询。
方括号运算符的优点在于方括号使用字符串值(字符串值是动态的,可以在运行时更改)。而标识符是静态的,必须写死在程序中。
属性访问错误
查询一个不存在的属性并不会报错,访问不存在的表达式会返回undefined。但是,如果对象不存在,那么试图查询不存在的对象的属性就会报错。程序中可以用短路运算&&防止此类事情发生。obj && obj.attr
当然,给null和undefined设置属性也会报类型错误。在一下场景下给对象o设置属性p会失败:
o中的属性p是只读的:不能给只读属性重新赋值。
o中的属性p是继承属性,并且是只读的:不能通过同名自有属性覆盖只读的继承属性。
o中不存在自有属性p:o没有使用setter方法继承属性p,并且o的可扩展行是false。如果o中不存在p,而且没有setter方法可供调用,则p一定会添加至o中。但如果o不是可扩展的,那么在o中不能定义新属性。(好拗口)
删除属性
delete运算符可以删除对象的属性。操作数应该是一个属性访问表达式。delete运算符只能删除自有属性,不能删除继承属性。当delete表达式删除成功或没有任何副作用(比如删除不存在的属性)时,会返回true。
var o = {};
delete o.x; //删除不存在的属性 返回true
delete o.toString; //删除继承的属性,但无法删除 依旧返回true
delete 1; // 运算符后不是属性访问表达式,没有实际意义,依旧返回true
delete不能删除那些可配置性为false的属性。某些内置对象的属性是不可配置的,比如通过变量声明和函数声明创建的全局对象的属性。
检测属性
首先是in运算符。in运算符的左侧是属性名(需是字符串),右侧是对象。如果对象的自有属性或继承属性中包含则返回true。(敲重点,对象的自有属性或继承属性)
还有hasOwnProperty()方法。该方法用来检测给定的名字是否是对象的自有属性。继承属性会返回false。(敲重点,自有属性返回true,其他的包括继承的返回false)
propertyIsEnumerable()是hasOwnProperty()的增强版,只有检测到是自有属性并且这个属性的可枚举性为true时才返回true。一般来说,JS代码创建的都是可枚举的。
可以使用!==来判断某个属性是否为undefined。但是in可以区分不存在的属性和存在值为undefined的属性。也就是说,某个属性显示赋值为undefined时,in返回true,而不全等则返回false。
var o = {x:1};
"x" in o; // true
"toString" in o // 继承属性,返回true
o.hasOwnProperty("x"); // 自身属性,返回true
o.hasOwnProperty("toString"); // 继承属性,返回false
o.propertyIsEnumerable("x"); //可枚举的自身属性,返回true
枚举属性
for/in循环可以遍历对象中所有可枚举的属性,包括继承的。还有两个ES5定义的枚举属性名称的函数。第一个是Object.keys(),它返回一个数组,数组由对象可枚举的自有属性的名称组成。第二是Object.getOwnPropertyNames(),返回对象的所有自有属性的名称,包括不可枚举的!也就是说两者都是返回自身属性,只不过前者无法返回不可枚举的,后者可以返回不可枚举的。
var o = {x:1,y:2};
Object.keys(a); //["x","y"],返回自身可枚举属性名称的数组
Object.getOwnPropertyNames(a); //["x", "y"]
//本例没有不可枚举属性,所以两者返回相同的数组
对象的三个属性
1)原型属性
对象的原型属性是用来继承属性的。原型属性是在实例对象创建之初就设置好的。通过对象直接量的对象使用Object.prototype作为原型。通过new创建的对象使用构造函数的prototype属性作为原型。通过Object.create()创建的对象使用第一个参数作为原型。
如若检测一个对象是否是另一个对象的原型(或处于原型链中),请使用isPrototypeOf()方法。
var p = {x:1};
var o = Object.create(p); //使用p原型创建对象
p.isPrototypeOf(o); //true
Object.prototype.isPrototypeOf(o); //true 均为继承
第七章 数组
如果省略数组直接量中的某个值,省略的元素的值为undefined。
var count = [1,,3]; // count[1]的值为undefined
var undefs = [,,]; //undefs.length为2,数组直接量的语法允许有可选的结尾逗号,所以[,,]只有2个元素,而不是3个。
数组的元素的读和写
使用[]操作符来访问数组。方括号中是一个返回非负整数值得任意表达式。数组是对象的特殊形式,使用[]访问数组元素就像用方括号访问对象属性一样。数组的特别之处在于,当使用小于2^32的非负整数作为属性名时数组会自动维护其length属性值。举个例子:
var a = [];
a[1] = 1;
a[2] = 2;
a.length // => 3
//也就是说,给数组a赋值小于2^32的非负整数的属性名时,数组会自动扩展为其length属性值。但是,属性值超过2^32的正整数或者负数时,就不会维护为length属性值了。接着看下面的例子
var b = [];
b[Math.pow(2,32)] = 3;
b; // => [4294967296: 3];
b[-1] = 4;
b; // => [4294967296: 3, -1: 4]
所有的索引都是属性名,但只有在0~2^32 - 2之间的整数属性名才是索引。
可以使用负数或者非整数来索引数组。这种情况下,数值会转换为字符串,字符串作为属性名来用。因为此情况为非负整数,所以只能当常规的对象属性,而非数组的索引。举个例子:
a[-1.23] = true; // 创建名为‘-1.23’的属性
a["1000"] = 0; //数组的第1001个元素
a[1.000] //等同于a[1]
数组索引仅仅是对象属性名的一种特殊类型,意味着数组没有越界错误的概念。也就是说,当试图查询任何对象中不存在的属性时,不会报错,仅仅得到undefined值。
稀疏数组
稀疏数组就是包含从0开始的不连续索引的数组。意味着稀疏数组length属性值大于元素的个数。最简单粗暴的创建稀疏数组的方式:var a = new Array(10); 0个元素,长度为10。
足够稀疏的数组通常在实现上比稠密的数组更慢、内存利用率更高,在这样的数组中查找元素的事件与常规对象属性的查找时间一样长。在数组直接量中省略值时不会创建稀疏数组。因为省略的元素在数组中是存在的,值为undefined。这和元素不存在还是有区别的,请看下例:
var a1 = [,,,]; // =>[undefined,undefined,undefined]
var a2 = new Array(3); // =>数组根本没有元素
0 in a1 // =>true;因为a1在索引0处有元素,为undefined
0 in a2 // =>false; a2在索引0处没有元素
数组长度
如果为一个数组元素赋值,它的索引i大于或等于现有数组的长度时,length属性的值将设置为 i + 1。相对的,设置length属性为一个小于当前长度的非负整数n时,当前数组中那些索引值大于等于n的元素将被删除:
var a = [1,2,3,4,5];
a.length = 0; //删除所有元素,a为[]
a.length = 5; //等同于new Array(5)
删除数组元素与为其赋undefined值得类似的。对一个数组元素使用delete不会修改数组的length属性,也不会将元素从高索引出移下来填充已删除属性的空白。如果删除元素,就会变成稀疏数组。
数组方法
1)join()----------不会改变原数组
Array.join()将数组中所有元素都转化为字符串并连接在一起,返回最后生成的字符串。可以指定可选的字符串在生成的字符串中分隔数组元素。默认使用逗号。Array.join()是String.split()方法的逆向操作,后者根据指定正则来分割字符串为一个数组。
var a = [1,2,3];
a.join(); // =>"1,2,3"
a.join(" "); // =>"1 2 3"
var b = new Array(10);
b.join('-') // => "---------"
2)reverse()---------会改变原数组
Array.reverse()方法将数组中的元素颠倒顺序,返回逆序的数组。需要注意的是,该方法是在原先的数组中重新排列。
var a = [1,2,3];
a.reverse(); // => [3,2,1]
a;// => [3,2,1]
3) sort() ---------会改变原数组
该方法将数组中的元素排序并返回排序后的数组。当不带参数调用时,数组元素以字母表顺序排序(有必要时临时转化为字符串进行比较)。如果数组包含undefined元素,会被排到数组的尾部。
该方法可以传一个可选的参数,参数必须是函数。也就是所谓的比较函数比较函数应该具备两个参数a和b,返回值如下:
若a小于b,在排序后的数组中a应该出现在b之前,则返回一个小于0的值。
若a等于b,则返回0。
若a大于b,则返回一个大于0的值。
下面是一个打算数字数组的顺序的方法:
var sortArray = array.sort(function(){
return Math.random() - 0.5;
})
4) concat() ---------不会改变原数组
该方法创建并返回一个新数组,元素包括调用concat()的原始数组的元素和该方法的每个参数。concat()不会修改调用的方法。而且concat()不会递归扁平化数组的数组。如果不传参数,则返回调用该方法的数组,也就相当于对数组进行了一次复制(浅复制)。
var a = [1,2,3];
a.concat(4,5);
a.concat([4,5]); // =>均返回[1,2,3,4,5]
5) slice() ---------不会改变原数组,该单词含义为切片,划分
该方法返回指定数组的一个片段或者子数组。两个参数分别指定了片段的开始和结束的位置。可以简单的理解为左闭右开([start,end))。如果只指定一个参数,返回的数组包含从开始位置到结尾的所有元素。如果单参数为负数,则开始位置从最后一个元素开始。
var a = [1,2,3,4,5];
a.slice(0,3); // =>索引从[0,3),不包含第4个元素,返回[1,2,3]
a.slice(3); // =>从a[3]开始到最后一个元素,返回[4,5]
6) splice() -------------会改变原数组,该单词含义为粘接
该方法能够从数组中删除元素、插入元素到数组中或者同时完成删除和插入。使用该方法后,数组元素会根据需要增加或减小索引值,因此数组的其他部分仍然是连续的。第一个参数指定了插入、删除的起始位置。第二个参数指定应该从数组中删除元素的个数。若省略第二元素,从起始点到结尾所有元素都会被删除。splice()返回一个有删除元素组成的数组,或者如果没删除元素就返回空数组。splice()的前两个参数指定了需要删除的数组元素。后面的任意参数指定了需要插入到数组中的元素,从第一个参数指定的位置开始插入。也就是说,第一个参数是删除或插入的起始位置。举个例子:
var a = [1,2,3,4,5,6,7,8];
a.splice(4); // 指定一个参数,意味着从a[4]开始到末尾都会被删除,此时会返回 //[5,6,7,8],然后原数组a变为[1,2,3,4]
a.splice(1,2); //注意此时原数组为[1,2,3,4],两个参数指定了从a[1]开始删除2个元素,返 //回[2,3],同时原数组变为[1,4]
a.splice(1,0,[1,2],3); //此时方法多于2个参数,意味着会进行删除和插入操作。从a[1]开始删除0个元素,同时在指定位置左侧插入'[1,2],3',由于删除了0个元素,所以会返回[],同时a变为[1,[1,2],3,4]
项目中遇到了对列表上移和下移的需求,刚好用到了splice,大致代码如下:
// 对selectItem数组上移操作
if(!index)return;//如果是第一项就返回,第一项当然不允许上移了
this.selectItem.splice(index-1,0,(this.selectItem[index]));
//既然要上移,那我们肯定要插入到前一项的左侧了,所以第一个参数是index-1,然后第三参数将当前项插入到数组中,注意此时是有2个当前项的,假设我们是对数组的第二项进行操作的,那么现在返回的数组第一项和第三项都是进行上移的那一项,所以我们必须删除重复的。
this.selectItem.splice(index+1,1);
//我们来进行删除操作,注意现在新数组的第一项和第三项都是我们移动的那个值,而当前index为1,所以我们需要操作index+2,也就是第三项,将其删除。//下移
if(index ===this.selectItem.length -1)return;//如果为最后一项 ,不允许下移
this.selectItem.splice(index+2,0,(this.selectItem[index]));
//既然要下移,那么我们就要将当前数据插入到下下位的左侧了,如果是index+1,那还是在原来的地方多了一个重复的数据,等于没移动!
this.selectItem.splice(index,1);
// 这个好理解,直接将原来的值删除即可。
7) push()和pop() ------------会修改原数组
push()在数组尾部添加一个或多个元素,返回数组新的长度。pop()删除数组的最后一个元素,同时返回删除的那个值。来总结一下,两个方法都是在数组尾部操作,而且push支持多个参数,pop不传参数,就算传了参数也会忽略参数,而且只能删除最后一个元素并返回这个值。
8) unshift()和shift() ----------会修改原数组
这两个方法跟上面的方法十分类似,只不过是修改的数组的头部而非尾部。unshift()对应push()方法,在数组头部添加一个或多个元素,并将已存在的元素移动到更高索引的位置来获得足够的空间,最后返回数组新的长度。shift()对应pop()方法,删除第一个元素并返回,将随后的元素下移一个位置填补空缺。
值得注意的一个地方,unshift()使用多个参数调用时,参数是一次性插入的,而不是一个个的插入。这意味着最终的数组中插入元素的顺序和在参数中的顺序保持一致,如果每次插入一个参数,则恰好反过来。来个例子:
var a= [3];
a.unshift(1,2); // 返回数组长度3,此时a为[1,2,3];
a.unshift(1); // 2
a.unshift(2); // 3 此时a为[2,1,3]
这两对好基友可以让数组实现类似队列的效果(FIFO):
push()在数组尾部将元素添加,shift()在数组头部将元素删除;<-------
unshift()在数组头部将元素添加,pop()在数组尾部将元素删除;------->
9) toString()和toLocaleString()
数组同样也拥有toString()方法。该方法将每个元素转化为字符串,有必要的话还会调用元素的toString()方法。并且输出用逗号分隔的字符串列表。要注意的是,输出不包括方括号或者其他任何形式的包裹数组值得分隔符。结果与不使用任何参数调用join()方法返回的字符串相同。
[1,2,3].toString() // '1,2,3'
["a","b","c"].toString() // 'a,b,c'
toLocaleString()是toString()方法本地化版本。
ES5中的数组方法
大多数方法的第一个参数接收一个函数,并且对数组的每个元素或一些元素调用该函数。也就是说,大多是方法是对数组进行遍历的。如果是稀疏数组,对不存在的元素不调用传递的参数。大部分情况下,调用的函数提供三个参数:数组元素、元素的索引和数组本身(item,index,array)。第二个参数是可选的,如果有的话,则调用的函数被看做是第二个参数的方法。也就是说,在调用函数时传递进去的第二个参数作为它的this关键字的值来使用。
ES5的数组方法都不会修改原数组。
1)forEach()
这个已经用的很多了,说一个注意点。forEach()无法提前终止遍历。如果要提前终止,必须把方法放在一个try块中,并能抛出一个异常。
2) map()
该方法将调用的数组的每个元素传递给指定函数,并返回一个新的数组,包含函数的返回值。
所以map()的函数参数应该有返回值。如果是稀疏数组,返回的也是相同方式的稀疏数组:相同的长度,相同的缺失元素。
经实践,如果在函数中进行break,会报错。如果对满足某个条件的元素return,则该元素对应的位置为undefined,如果return其他值,则该位置为对应值,包括return false。
var a = [1,2,3];
a.map(function(x){
if (x === 2) return;
return x + x;
}); // [2, undefined, 6]
a.map(function(x){
if (x === 2) return false;
return x + x;
}); // [2, false, 6]
3) filter()
该方法返回的数组元素是调用的数组的一个子集。如果返回值为true或能转化为true的值,那么传递给判定函数的元素就是这个子集的成员。方法会跳过稀疏数组中缺少的元素,它的返回数组总是稠密的。
4)every()和some()
两个方法是数组的逻辑判定:它们对数组元素应用指定的函数进行判定,返回true或false。
如果数组中的所有元素调用判定函数全部都返回true,every()方法才返回true,其他一律返回false。如果数组中至少有一个元素调用判定函数返回true,它就返回true。只有所有的元素调用判定函数返回false,它才返回false。
var a = [1,2,3,4,5];
a.every(function(x) {return x % 2 === 0;}) // false 不是所有数都是偶数
a.some(function(x) {return x % 2 === 0;}) //true 含有偶数
5) reduce()和reduceRight()
方法使用指定的函数将数组元素进行组合,生成单个值。
reduce()需要两个参数,第一个是执行化简操作的函数。化简函数的任务就是用某种方法把两个值合成为一个值,并返回。第一次调用函数时,第一个参数是一个初始值,就是传递给reduce()的第二个参数。下一次调用中,这个值就是上次化简函数的返回值。举个例子:
var a = [1,2,3,4,5];
var sum = a.reduce(function(x,y){return x+y},0);
在这个例子中,第一次调用化简函数时的参数是0(化简函数的第二个参数,即初始值)和1(数组的第一项),将两个相加返回1,再次调用的时候参数为返回值1和数组的第二项2,然后返回3。依次相加得到15,reduce方法返回这个值。
当不指定初始值调用reduce()时,它将使用数组的第一个元素作为初始值。这意味着第一次调用化简函数的时候就使用第一个和第二个数组元素作为第一和第二个参数。但是在空数组上,不带初始值参数调用reduce()将导致类型错误异常。如果调用它的时候只有一个值(数组只有一个元素并且没有指定初始值,或者有一个空数组并且指定了一个初始值),reduce()只是简单地返回那个值而不会调用化简函数。
reduceRight()与reduce()不同的是,它按照数组索引从高到低来处理数组。
5) indexOf()与lastIndexOf()
方法搜索整个数组中具有给定值得元素,返回找到的第一个元素的索引或没有找到就返回-1。
indexOf()从头至尾搜索,lastIndexOf()相反。
两个方法不接受一个函数作为参数。他们的参数是这样的:第一个参数是需要搜索的值,第二个参数是可选的,指定数组中的一个索引,从索引出开始搜索。第二个参数也可以是负数,代表相对数组末尾的偏移量。