JavaScript 中级(二)

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
View Code

(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'
View Code

(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
View Code

(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
View Code

保护对象

问题: 旧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
View Code

   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
View Code

         (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: 年龄超范围!
View Code

保护对象结构: 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: "埃里克"
View Code

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"
View Code

替换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
View Code

  (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
View Code

  (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
View Code

数组函数

判断: 

(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
View Code

(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
View Code

遍历: 对数组中每个元素执行相同的操作,包括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 吉吉-到!
View Code

(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]
View Code

(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]
View Code

(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
View Code

你可能感兴趣的:(java,面试,filter,vue,dom)