ES5: ECMAScript标准的第五个版本
严格模式
什么是: 比旧的js运行机制要求更严格的运行机制。
为什么: 因为旧的js运行机制有很多广受诟病的缺陷!
何时:项目中,都要在严格模式下开发!
如何启用严格模式:只要在当前作用域的顶部添加"use strict";,那么,当前作用域内的程序,就运行在严格模式下了!
严格模式的新规定:
(1). 禁止给未声明的变量赋值:
a. 问题: 旧的js中,一个变量即使从未声明过,也可以用=强行赋值!但是!这个变量会被自动创建到全局!——极容易全局污染!
b. 解决: 只要启用严格模式,就不允许给未声明过的变量赋值!只要赋值就报错!"xxx is not defined"
c. 示例: 不小心拼错变量名了!
1
2
3
4
5
6 Document
7
8
9
23
24
25 运行结果:
26 Uncaught ReferenceError: web2011 is not defined
(2). 所有静默失败升级为错误:
a. 什么是静默失败:旧js中,有些操作不成功!然后还不报错!
b. 问题: 极其不便于代码的调试
c. 解决: 启用严格模式后,所有的静默失败,都变成明确的报错!
d. 优点: 极其便于程序的调试!
e. 示例: 尝试修改只读属性:
1
2
3
4
5
6 Document
7
8
9
31
32
33 运行结果:
34 Uncaught TypeError: Cannot assign to read only property 'eid'
(3). 普通函数调用或匿名函数自调或回调函数中的this不再指向window,而是undefined。
a. 旧js中,普通函数调用或匿名函数自调或回调函数中,因为之前既没有.又没有new,所以this都默认指向window!——语法规定,没有为什么!
b. 问题: 经常因为错误的使用this,导致全局污染!
c. 解决: 启用严格模式,可让普通函数调用和匿名函数自调中的this->undefined,不再指向window
d. 优点: 避免因为错误使用this导致全局污染
1
2
3
4
5
6 Document
7
8
9
46
47
48 运行结果:
49 Uncaught TypeError: Cannot set property 'sname' of undefined
(4). 禁用了arguments.callee
a. 什么是arguments.callee: 专门在一个函数内,自动获得当前函数本身的关键字。
b. 为什么: 递归调用时,如果在函数内很深处写死函数名,那么,一旦将来函数名改变,而忘记修改内部写死的函数名!就会立刻出错!
c. 何时: 今后只要递归调用时,都要在当前函数内用arguments.callee代替写死的函数名
d. 优点: 函数内部不用写死函数名,arguments.callee也能自动获得当前函数名——即使修改了函数名,也不用修改内部的函数名!
e. 问题: arguments.callee明明是好东西,为什么严格模式要禁用arguments.callee?
f. 原因: 递归调用效率极低,重复计算量太大!所以,严格模式不建议频繁使用递归调用!
g. 解决: 绝大多数递归算法,都可以用循环代替!——难度极高!——但是,没有任何重复计算,效率极高!
h. 总结:
1). 今后,大多数递归算法,虽然有重复计算,但是效率还是可以接收的!我们就首选递归算法。只不过,在函数内部,暂时写死函数名。
2). 除非,递归极大的影响了效率!才被迫寻求用循环方式解决!
示例: 分别使用递归和循环实现计算斐波那契数列中第n个数
1
2
3
4
5
6 Document
7
8
9
55
56
57 运行结果:
58 1 1 2 3 55
保护对象
问题: 旧js中,一个对象的属性值或结构,都可随意修改。毫无自保能力!
解决: ES5标准中提供了保护对象的机制
保护单个属性:
(1). ES5标准中,将对象中每个属性,底层都变成了一个缩微的小对象:
(2). 每个属性的缩微小对象中都包含4个成员:
(3). 如何查看每个属性的缩微小对象及其内部4个成员:(了解)
var 缩微小对象=Object.getOwnPropertyDescriptor(对象, "属性名")
获得对象自己的属性的描述信息
(4). 如何修改开关的值来保护对象的属性:
a. 如果只修改一个属性的开关:
重新定义属性
Object.defineProperty(对象名, "属性名", {
开关名: true或false,
... : ...
})
b. 问题: 我们可以关闭开关,别人同样可以重新打开开关!
c. 解决: 只要关闭writable开关或enumerable开关时,都要同时关闭configurable:false。就禁止修改前两个开关了!——双保险!
d. 示例: 使用defineProperty()保护对象属性
1
2
3
4
5
6 Document
7
8
9
61
62
63 运行结果:
64 {value: 1001, writable: true, enumerable: true, configurable: true}
65 configurable: true
66 enumerable: true
67 value: 1001
68 writable: true
69 __proto__: Object
70 eid : 1001
71 ename : 埃里克
72 {eid: 1001, ename: "埃里克", salary: 12000}
73 eid: 1001
74 ename: "埃里克"
75 salary: 12000
76 __proto__: Object
77 12000
e. 问题: Object.defineProperty()一次只能修改一个属性的多个开关。如果对象中,多个属性都需要保护,则需要把Object.defineProperty()重复写很多遍!——繁琐!
f. 解决: 今后多数情况,一个对象中,都有多个属性需要保护,则应该换成:
Object.defineProperties(对象, {
属性名: {
开关: true或false,
... : ...
},
属性名: {
开关: true或false,
... : ...
}
})
g. 示例: 使用defineProperties()保护对象属性
1
2
3
4
5
6 Document
7
8
9
58
59
60 运行结果:
61 eid : 1001
62 ename : 埃里克
63 {eid: 1001, ename: "埃里克", salary: 12000}
64 eid: 1001
65 ename: "埃里克"
66 salary: 12000
67 __proto__: Object
68 12000
(5). 问题: 如果用更灵活的自定义规则保护属性值,则开关就无法实现了
(6). 解决: 今后,只要用自定义的规则,灵活保护属性值时,都要用访问器属性。
访问器属性
.什么是访问器属性: 自己不实际保存属性值,仅提供对另一个数据属性的保护!——保镖
.如何定义访问器属性: 2步:
1). 为对象添加一个新的半隐藏的属性,将旧对象中要保护的属性值转移到这个新的半隐藏属性中保存:
Object.defineProperty(对象, "_原属性名",{
value: 旧对象.要保护的属性,
writable: true, 可以修改
enumerable: false, 实际保存属性值的属性,应该隐姓埋名
configurable:false //双保险
})
2). 为对象添加一个与原属性同名的新访问器属性(替身)代替旧属性:
Object.defineProperty(对象, "原属性名",{
//i. 为替身属性请保镖:
//①一个保镖必须叫get,是一个函数,专门负责从受保护的_属性中读取出现在的属性值,返回给外部
get:function(){
return this._属性名;
},
//②一个保镖必须叫set,也是一个函数,专门负责接收外部传来的新值,经过验证后决定是否保存到受保护的_属性中
set:function(value){ //将来value可自动接到外界传来的新值
if(value符合要求){
this._属性=value
}else{
不保存,且报错!
}
},
// ii. 因为访问器属性作为替身,必须抛头露面,所以enumerable:true;因为访问器属性替身和保镖不能随意删除,所以configurable:false。
enumerable:true,
configurable:false
//说明: 因为访问器属性不实际保存属性值,所以访问器属性没有value和writable。
})
.外界如何使用访问器属性: 因为外界根本对访问器属性不知情!所以,外界使用访问器属性替身的用法和使用普通的属性用法完全一样!
1). 外界想获取访问器属性的属性值时: 对象.属性名
底层: 当外界想获取访问器属性的属性值时,底层会自动调用访问器属性的get()方法,从实际保存数据的半隐藏的属性中,读取出现在的属性值,return给外部!
2). 外界想修改访问器属性的属性值时: 对象.属性名=新值
底层: 当外界想修改访问器属性的属性值时,底层会自动调用set()方法,并且自动将=右边的新值,传递给set()方法的value形参。在set()内部,先验证value的属性值.是否符合要求,再决定是实际保存属性值,还是报错!
示例: 使用访问器属性保护年龄属性:
1
2
3
4
5
6 Document
7
8
9
63
64
65 运行结果:
66 自动调用了eric.eage中的get(),return 25
67 25
68 自动调用eric.eage中的set(26),经过验证
69 26符合要求,才实际保存到_eage属性中
70 自动调用了eric.eage中的get(),return 26
71 26
72 自动调用eric.eage中的set(-2),经过验证
73 -2不符合要求,不保存!直接报错!
74 Uncaught Error: 年龄超范围!
保护对象结构: 3个级别
(1). 防扩展:
a. 问题: 如果别人觉得你的属性管的太严,不好用!恶意添加一个类似的属性,另起炉灶!
b. 解决: 可以禁止给对象添加新属性:
Object.preventExtensions(对象)
(2). 密封: 既防扩展,又防删除
a. 问题: 对象中几乎所有的属性都禁止删除!那么,难道要给每个属性都加configurable:false吗!?
b. 解决: 只要密封一个对象,就可以禁止用户删除对象中任何一个属性!
Object.seal(对象)
c. 原理: seal()自动做了2件事:
1). 自动调用了preventExtensions,禁止添加新属性
2). 自动为每个对象设置configurable:false,所有属性禁止删除
d. 强调: 程序中的密封,只禁止添加和删除属性,不禁止修改属性值!属性值还是可以随意修改的!——多数情况,属性值都是可以修改的!
e. 总结: 今后大多数对象,只要保护到密封(seal)级别,就够了。
(3). 冻结: 既禁止添加删除属性,又禁止修改属性值——很少用
a. 如何: Object.freeze(对象)
冻结
b. 原理: freeze自动做了三件事:
1). 也自动调用preventExtensions,禁止扩展新属性
2). 也自动设置所有属性的configurable:false,所有属性禁止删除
3). 还自动设置所有属性的writable:false,所有属性禁止修改!
示例: 分别使用三个级别保护对象的结构
1
2
3
4
5
6 Document
7
8
9
60
61
62 运行结果:
63 {eid: 1001, ename: "埃里克"}
64 eid: 1001
65 ename: "埃里克"
Object.create()
问题: 通常创建一个子对象,继承父对象,都要通过用new来调用构造函数创建。
比如: var lilei=new Student(...)
结果:会新建一个lilei对象,自动继承Student的原型对象
但是:如果没有构造函数,也想创建子对象,继承父对象?
解决:如果没有构造函数,只有一个父对象,也想创建子对象,继承父对象时,可用Object.create()
如何:
(1). 基本:只创建一个空对象,继承父对象
a. var 新子对象=Object.create(父对象)
b. 原理: 2件事: 完整了new的前2步!
1). 创建一个新的子对象
2). 自动设置子对象继承父对象
c. 问题: 新对象是空的。如果新对象也想有自己的自有属性,怎么办?
(2). 高级: 创建空对象,继承父对象,并给新对象添加自有属性
a. var 新对象=Object.create(父对象,{
//defineProperties中写的格式一样
新属性名:{
value:属性值,
writable:true,
enumerable:true
},
新属性名:{
value:属性值,
writable:true,
enumerable:true
},
})
b. 原理: 3件事: 完整了new的前3步!
1). 创建一个新的子对象
2). 自动设置子对象继承父对象
3). 给新子对象添加自有属性
示例: 使用Object.create代替new创建子对象继承父对象
1
2
3
4
5
6 Document
7
8
9
33
34
35 运行结果:
36 {phone: "iPhone12", bao: "LV"}
37 bao: "LV"
38 phone: "iPhone12"
39 __proto__:
40 car: "infiniti"
41 money: 10000000000
42 __proto__: Object
43 iPhone12 LV
44 10000000000 "infiniti"
替换this
问题: 有时,函数内的this指向的对象不是我们需要的!难道我们就只能被动接受?
解决: 今后只要函数内的this执行的对象不是我们想要的!都有办法更换成我们想要的对象!
如何:3种:
(1). 只在一次调用函数时,临时替换一次this:
a. 要调用的函数.call(替换this的对象)
b. 原理: call做了2件事:
1). 先调用函数执行,立刻执行
2). 将函数内的this都替换为()中想要的对象。
c. 问题: 有些函数调用时,需要传入实参值!
d. 解决: 其实,call()中,可以在替换this的对象后,接着写实参值!
要调用的函数.call(替换this的对象, 实参值1, 实参值2, ... )
e. 原理: call做了3件事:
1). 先调用函数执行,立刻执行
2). 将函数内的this都替换为()中想要的对象。
3). 将实参值传给函数的形参变量!
f. 示例: 定义计算函数,可以计算每个员工的工资:
1
2
3
4
5
6 Document
7
8
9
40
41
42 运行结果:
43 Li Lei的总工资是:13000
44 undefined的总工资是:NaN
45 Han Meimei的总工资是:12000
(2)用apply 替换this:
g. 问题: 有时,函数需要传入多个实参值,但是,多个实参值却是放在一个数组中给的!出现了不一致!
h. 解决: 都可用apply代替call
要调用的函数.apply(替换this的对象, 包含实参值数组 )
i. 原理: apply做了3件事:
1). 先调用函数执行,立刻执行
2). 将函数内的this都替换为()中想要的对象。
3). 先将数组自动拆散为多个实参值,再分别传给函数的多个形参变量!
j. 示例: 使用apply,先打散数组,再传参:
1
2
3
4
5
6 Document
7
8
9
34
35
36 运行结果:
37 Li Lei的总工资是:13000
38 Han Meimei的总工资是:12000
(3)用bind 永久替换this:
a. 问题: 如果一个函数需要反复使用,而每次使用时,都要用call或apply替换this!
b. 根源: 为什么每次都要替换this!函数本不属于对象,而对象强行要调用函数!用着就很不方便!
c. 解决: 如果可以创建一个对象专属的函数,永久将this替换为某个对象,则再使用时!就不用反复替换this了!
d. 如何: 2步:
1). 创建原函数副本,并永久替换this为指定对象
var 新函数=原函数.bind(替换this的对象)
强调: bind()并不会调用函数立刻执行,而是仅仅创建函数的一个一模一样的副本而已。只不过,把新函数中的this永久替换为指定的对象。
2). 调用新函数,传参,但是无需再call或apply替换this了!
新函数(传参);
e. 原理: bind()做2件事:
1). 创建一个新函数的一模一样的副本
2). 自动永久替换新函数的this为指定的对象
f. 强调: 用bind()永久绑定的this,再用call或apply,也是无法重新替换的!
g. 问题: 有些函数的个别实参值,也是几乎固定不变的!每次调用时,都要反复输入固定不变的实参值,也挺麻烦!
h. 解决: 其实, bind()不但可以永久绑定this,而且,还可以永久绑定部分实参值!
i. 如何: 2步:
1). 先创建函数副本,并永久绑定this和部分实参值
var 新函数副本=原函数.bind(替换this的对象, 固定的实参值1, ...)
2). 调用函数副本时,只需要传入剩余的可能变化的实参值即可!
新函数副本(剩余实参值,...)
j. 原理: bind()做3件事:
1). 创建一个新函数的一模一样的副本
2). 自动永久替换新函数的this为指定的对象
3). 永久绑定部分实参值
k. 示例: 为lilei创建专属的函数副本,永久绑定this和底薪
1
2
3
4
5
6 Document
7
8
9
51
52
53 运行结果:
54 Li Lei的总工资是:13000
55 Li Lei的总工资是:14000
56 Li Lei的总工资是:15000
57 Li Lei的总工资是:12000
58 Li Lei的总工资是:13000
59 Li Lei的总工资是:14000
60 Li Lei的总工资是:15000
数组函数
判断:
(1). 判断数组中是否所有元素都符合要求
a. var 判断结果=数组.every(
function(当前元素值, 当前下标位置, 当前数组){
return 判断条件, 只判断当前一个元素是否符合要求!
}
)
b. 原理:
1). every内部自带for循环,自动遍历当前数组中每个元素
2). 每遍历一个元素,就自动调用一次回调函数,并且自动按顺序传入三个实参值:
i. 第一个实参值: 传入当前元素值
ii. 第二个实参值: 传入当前下标位置
iii. 第三个实参值: 传入当前数组对象
3). 在回调函数内部,利用我们提供的自定义的判断条件,判断当前元素是否符合要求!并返回判断结果给every()函数
4). every()函数拿到判断结果后:
i. 如果every(),拿到false,说明当前元素不符合要求!就立刻退出循环,并返回整体判断结果为false!说明当前数组中不是所以元素都符合要求的!
ii. 如果every(),拿到true,说明当前元素符合要求!但是,暂时无法判断后续其它元素是否都符合要去,所以要继续循环判断下一个元素。如果直到循环结束,所有元素经过判断都返回true!every()才返回整体判断结果为true。说明当前数组中所有元素都符合要求!
c. 示例: 判断是否数组中所有元素都是偶数
1
2
3
4
5
6 Document
7
8
9
36
37
38 运行结果:
39 arr1.every()自动调用了一次回调函数,
40 自动传入(当前元素值=1, 当前下标=0, 当前数组=1,2,3,4,5),
41 本次判断结果:false
42 false
43 arr2.every()自动调用了一次回调函数,
44 自动传入(当前元素值=2, 当前下标=0, 当前数组=2,4,6,4,2),
45 本次判断结果:true
46 arr2.every()自动调用了一次回调函数,
47 自动传入(当前元素值=4, 当前下标=1, 当前数组=2,4,6,4,2),
48 本次判断结果:true
49 arr2.every()自动调用了一次回调函数,
50 自动传入(当前元素值=6, 当前下标=2, 当前数组=2,4,6,4,2),
51 本次判断结果:true
52 arr2.every()自动调用了一次回调函数,
53 自动传入(当前元素值=4, 当前下标=3, 当前数组=2,4,6,4,2),
54 本次判断结果:true
55 arr2.every()自动调用了一次回调函数,
56 自动传入(当前元素值=2, 当前下标=4, 当前数组=2,4,6,4,2),
57 本次判断结果:true
58 true
(2). 判断数组中是否包含符合要求的元素
a. var 判断结果=数组.some(
function(当前元素值, 当前下标位置, 当前数组){
return 判断条件, 只判断当前一个元素是否符合要求!
}
)
b. 原理:
1). some内部自带for循环,自动遍历当前数组中每个元素
2). 每遍历一个元素,就自动调用一次回调函数,并且自动按顺序传入三个实参值:
i. 第一个实参值: 传入当前元素值
ii. 第二个实参值: 传入当前下标位置
iii. 第三个实参值: 传入当前数组对象
3). 在回调函数内部,利用我们提供的自定义的判断条件,判断当前元素是否符合要求!并返回判断结果给some()函数
4). some()函数拿到判断结果后:
i. 如果some (),拿到true,说明当前元素符合要求!就立刻退出循环,并返回整体判断结果为true!说明当前数组中包含符合要求的元素!
ii. 如果some(),拿到false,说明当前元素不符合要求!但是,暂时无法判断后续其它元素是否都不符合要求,所以要继续循环判断下一个元素。如果直到循环结束,所有元素经过判断都返回false!some()才返回整体判断结果为false。说明当前数组中不包含符合要求的元素!
c. 示例: 判断一个数组中是否包含奇数:
1
2
3
4
5
6 Document
7
8
9
37
38
39 运行结果:
40 arr1.some()自动调用了一次回调函数,
41 自动传入(当前元素值=1, 当前下标=0, 当前数组=1,2,3,4,5),
42 本次判断结果:true
43 true
44 arr2.some()自动调用了一次回调函数,
45 自动传入(当前元素值=2, 当前下标=0, 当前数组=2,4,6,4,2),
46 本次判断结果:false
47 arr2.some()自动调用了一次回调函数,
48 自动传入(当前元素值=4, 当前下标=1, 当前数组=2,4,6,4,2),
49 本次判断结果:false
50 arr2.some()自动调用了一次回调函数,
51 自动传入(当前元素值=6, 当前下标=2, 当前数组=2,4,6,4,2),
52 本次判断结果:false
53 arr2.some()自动调用了一次回调函数,
54 自动传入(当前元素值=4, 当前下标=3, 当前数组=2,4,6,4,2),
55 本次判断结果:false
56 arr2.some()自动调用了一次回调函数,
57 自动传入(当前元素值=2, 当前下标=4, 当前数组=2,4,6,4,2),
58 本次判断结果:false
59 false
遍历: 对数组中每个元素执行相同的操作,包括2个:
(1). forEach: 单纯代替for循环,遍历数组中每个元素
a. 问题:
1). for的问题: 语法已经固定了,没有简写的余地了
2). for in的问题: 语法虽然简单,但是in的遍历,可能超出数字下标的范围,继续遍历父对象中的成员——不保险!
b. 解决: 今后如果只是想单纯遍历索引数组中每个元素,都可以用forEach代替for和for in
c. 如何: 数组.forEach(function(当前元素值, 当前下标位置, 当前数组){
对当前元素值执行相同的操作
})
d. 原理:
1). forEach内部也自带for循环,自动遍历数组中每个元素
2). 每遍历一个元素,都自动调用一次回调函数,每次调用回调函数时,都自动传入三个值:
i. 第一个实参值: 传入当前元素值
ii. 第二个实参值: 传入当前下标位置
iii. 第三个实参值: 传入当前数组对象
3). 在回调函数内部,可对当前元素值执行操作。
e. 示例: 点名:
1
2
3
4
5
6 Document
7
8
9
34
35
36 运行结果:
37 平平-到!
38 安安-到!
39 吉吉-到!
(2). map: 基于原数组内容,经过修改后,生成一个新数组,原数组保持不变!
a. var 新数组=原数组.map(function(当前元素值, 当前下标, 当前数组){
return 将当前元素值经过修改后,获得的一个新元素值
})
b. 原理:
1). 先创建一个新的空数组等待。
2). map内部也自带for循环,自动遍历数组中每个元素
3). 每遍历一个元素,都自动调用一次回调函数,每次调用回调函数时,都自动传入三个值:
i. 第一个实参值: 传入当前元素值
ii. 第二个实参值: 传入当前下标位置
iii. 第三个实参值: 传入当前数组对象
4). 在回调函数内部,可对当前元素值执行修改操作,获得新的元素值,返回给map()。
5). map接到回调函数返回的新元素值之后,会将新元素值,自动添加到新数组中相同的位置上!
6). 遍历结束!新数组中放满了修改后的新元素值。然后map()就会将新数组返回!
c. 示例: 将原数组中每个元素值*2,获得一个新数组,原数组保持不变
1
2
3
4
5
6 Document
7
8
9
21
22
23 运行结果:
24 arr.map()自动调用了一次回调函数。自动传入(yuansu=1,xiabiao=0,shuzu=1,2,3,4,5)。返回了2
25 arr.map()自动调用了一次回调函数。自动传入(yuansu=2,xiabiao=1,shuzu=1,2,3,4,5)。返回了4
26 arr.map()自动调用了一次回调函数。自动传入(yuansu=3,xiabiao=2,shuzu=1,2,3,4,5)。返回了6
27 arr.map()自动调用了一次回调函数。自动传入(yuansu=4,xiabiao=3,shuzu=1,2,3,4,5)。返回了8
28 arr.map()自动调用了一次回调函数。自动传入(yuansu=5,xiabiao=4,shuzu=1,2,3,4,5)。返回了10
29 [2, 4, 6, 8, 10]
30 [1, 2, 3, 4, 5]
(3). 过滤:复制出数组中符合条件的个别元素,放入新数组中返回
(1). var 新数组=原数组.filter(function(当前元素值, 当前下标, 当前数组){
return 判断条件,判断当前元素值是否符合要求
})
(2). 强调: filter()是复制出符合要求的元素,原数组保持不变!
(3). 原理:
a. 先创建一个新的空数组等待。
b. filter内部也自带for循环,自动遍历数组中每个元素
c. 每遍历一个元素,都自动调用一次回调函数,每次调用回调函数时,都自动传入三个值:
1). 第一个实参值: 传入当前元素值
2). 第二个实参值: 传入当前下标位置
3). 第三个实参值: 传入当前数组对象
d. 在回调函数内部,只判断当前一个元素是否符合要求,并返回判断结果为filter()。
e. filter接到回调函数返回的判断结果后:
1). 只有判断结果为true的元素,才会被filter自动追加到新数组中
2). 如果判断结果为false,则什么也不干,继续遍历下一个元素
f. 遍历结束!新数组中只放了所有符合要求的元素。然后filter()就会将新数组返回!
示例: 过滤出数组中的偶数,放入新数组中返回,原数组保持不变
1
2
3
4
5
6 Document
7
8
9
22
23
24 运行结果:
25 arr.filter()自动调用了一次回调函数,自动传入(elem=1,i=0,arr=1,2,3,4,5),判断结果为:false
26 arr.filter()自动调用了一次回调函数,自动传入(elem=2,i=1,arr=1,2,3,4,5),判断结果为:true
27 arr.filter()自动调用了一次回调函数,自动传入(elem=3,i=2,arr=1,2,3,4,5),判断结果为:false
28 arr.filter()自动调用了一次回调函数,自动传入(elem=4,i=3,arr=1,2,3,4,5),判断结果为:true
29 arr.filter()自动调用了一次回调函数,自动传入(elem=5,i=4,arr=1,2,3,4,5),判断结果为:false
30 (2) [2, 4]
31 (5) [1, 2, 3, 4, 5]
(4). 汇总: 对数组中所有元素值进行统计,最终得出一个结论
(只介绍对数组内容求和)
(1). var 统计结果=数组.reduce(
//第一个参数,是一个回调函数
function(临时汇总值, 当前元素值, 当前位置, 当前数组){
return 将当前元素值汇总到临时汇总值上,形成了新的临时汇总值。
},
//第二个参数,是统计的起始值
起始值
)
(2). 原理:
a. 先创建一个新的变量等待,变量的初始值就是reduce的第二个参数设置的统计起始值。
b. reduce内部也自带for循环,自动遍历数组中每个元素
c. 每遍历一个元素,都自动调用一次回调函数,每次调用回调函数时,都自动传入四个值:
1). 第一个实参值: 将保存在变量中的临时汇总值传给第一个形参
2). 第二个实参值: 传入当前元素值
3). 第三个实参值: 传入当前下标位置
4). 第四个实参值: 传入当前数组对象
d. 在回调函数内部,将当前元素值汇总到临时汇总值中,获得新的临时汇总值。并将新的临时汇总值,返回给reduce()函数
e. reduce接到回调函数返回的临时汇总值之后,会将临时汇总值保存到开局时创建的变量中暂存,为继续汇总下一个元素值做准备。
f. 遍历结束!开局时创建的变量里保存的就是这个数组的统计结果,并返回。
示例: 对数组中所有元素求和
1
2
3
4
5
6 Document
7
8
9
23
24
25 运行结果:
26 arr.reduce()自动调用一次回调函数。自动传入四个值(上次汇总值=0,elem=1,i=0,arr=1,2,3,4,5),将当前元素值汇总到上次汇总值中,形成新的临时汇总值1
27 arr.reduce()自动调用一次回调函数。自动传入四个值(上次汇总值=1,elem=2,i=1,arr=1,2,3,4,5),将当前元素值汇总到上次汇总值中,形成新的临时汇总值3
28 arr.reduce()自动调用一次回调函数。自动传入四个值(上次汇总值=3,elem=3,i=2,arr=1,2,3,4,5),将当前元素值汇总到上次汇总值中,形成新的临时汇总值6
29 arr.reduce()自动调用一次回调函数。自动传入四个值(上次汇总值=6,elem=4,i=3,arr=1,2,3,4,5),将当前元素值汇总到上次汇总值中,形成新的临时汇总值10
30 arr.reduce()自动调用一次回调函数。自动传入四个值(上次汇总值=10,elem=5,i=4,arr=1,2,3,4,5),将当前元素值汇总到上次汇总值中,形成新的临时汇总值15