主题
3-6数组方法进阶及案例
本页对应 第三章JavaScript进阶,共 6 段视频,约 48 分钟。
学习目标
- 能写出
reduce的语法,说明回调函数中上一次值、当前值与初始值的作用,并正确实现数组求和。 - 能根据有无初始值推导
reduce的执行过程,准确说明遍历次数、每次返回值及最终结果。 - 能使用
reduce对对象数组按指定属性(如salary)累加,并正确设置初始值为0。 - 能区分
find与findIndex的返回值,说明找不到时分别返回undefined和-1,并与filter的返回值和查找方式作对比。 - 能使用
every和some检测数组元素是否满足条件,并解释二者在“全部满足”和“任一满足”场景下的布尔返回值差异。 - 能使用
Array.from将类数组转换为真数组,说明其静态方法调用方式、返回真数组且不修改原类数组。
知识点
从“蛋炒饭”理解面向过程
为什么需要:当问题步骤清晰、目标单一,尤其是贴近硬件或性能敏感的场景,先把流程拆成步骤最直接。面向过程不是“落后”,而是适合“按顺序把事情做完”。
怎么想:分析解决问题需要哪些步骤,把每一步封装成函数,使用时按顺序调用。就像蛋炒饭:准备食材、打蛋、炒蛋、下饭、调味、出锅,步骤之间靠数据传递衔接。

怎么做:先列步骤,再写函数,最后在主流程中依次调用。转写稿里 reduce 求和的推导也是这种思路:没有初始值时,上一次值 prev 以数组第一个元素为准,[1,5,8] 先算 1+5=6,再算 6+8=14,三个元素只遍历两次;有初始值 10 时,第一次是 10+1=11,再 11+5=16,再 16+8=24,有几个元素就遍历几次。
易错点:不要把面向过程等同于“低级”;步骤之间若数据传递不清,函数会越写越乱;复用性较差,步骤一变就要改多处。
面向对象:把事务分解成对象分工
为什么需要:大型软件项目需要多人合作,若所有逻辑都堆在步骤里,维护成本高。面向对象把事务分解成一个个对象,让对象成为功能中心,具有明确分工。
怎么想:先识别有哪些对象、每个对象负责什么、对象之间如何协作。对象不只是一堆数据,还包括操作数据的方法。

怎么做:在 JavaScript 中,本节学习目标是理解基于原型的面向对象编程语法实现,并理解封装、继承、多态三个特征。封装是隐藏细节、暴露接口;继承是复用共性;多态是同一接口有不同表现。
易错点:对象不是越多越好,职责不清会让系统更乱;继承不是把代码复制一遍;封装不等于随便加个函数;多态也不是简单重载。还要注意“编程”不要写成“编程序”。
编程思想对比:选择而非站队
为什么需要:知道优缺点,才能按场景选方案,而不是把某一种思想绝对化。
怎么想:面向过程性能通常更高,适合跟硬件联系紧密的东西,例如单片机;缺点是灵活性和复用性较差。面向对象易维护、易复用、易扩展,借助封装、继承、多态可以设计低耦合系统;缺点是性能通常比面向过程低。
怎么做:小脚本、流程固定的逻辑可优先面向过程;多人协作、需求易变、对象关系明显的系统可优先面向对象。笔记也提醒:生活离不开蛋炒饭,也离不开盖浇饭,选择不同而已;只不过前端不同于其他语言,面向过程更多。
易错点:不要认为面向对象一定性能高;也不要认为面向过程不能复用。真正要避免的是“为了面向对象而面向对象”。
用转写稿的 reduce 调试过程看“步骤化”与“对象化”
为什么需要:转写稿中的数组方法调试,能帮助我们看清“步骤化执行”和“按对象属性处理”的差别。比如薪资案例里,数组元素不是数字而是对象,不能直接拿对象累加。
怎么想:reduce 的回调函数第一个参数是上一次返回值 prev,第二个是当前元素 current;初始值会影响结果。对象数组必须给初始值 0,并累加 current.salary,否则第一次 prev 会变成第一个对象,无法相加。转写稿中“李四1500”与后续“1万+1500=25000”矛盾,按推导应纠正为 15000。
怎么做:
js
const total = [
{ name: '张三', salary: 10000 },
{ name: '李四', salary: 15000 },
{ name: '王五', salary: 20000 }
].reduce((prev, current) => prev + current.salary, 0);
console.log(total); // 45000调试时先打印 prev、current,确认第一次是 0 和第一个对象,第二次是 10000 和第二个对象,最后得到 45000。同音字也要纠正:伪数组不是“尾数组”,NodeList 不是“not list”,push 不是“po”,findIndex 返回索引,找不到返回 -1;every 要求每个都满足,some 只要一个满足;Array.from 可把伪数组转成真数组,且不修改原伪数组。
易错点:省略初始值导致对象累加失败;把 find 返回元素和 findIndex 返回索引混淆;把 filter 返回数组和 find 返回元素混淆;忘记 return。掌握这些调试过程后,再回到面向对象学习,就更容易理解“对象有属性、有方法、按职责协作”的意义。
先明确:本节要解决“封装”问题
为什么需要:面向对象思想里,封装很重要。若每个对象都单独写姓名、年龄和 sayHi,重复代码多,也不好维护。
怎么想:把公共属性和方法抽取出来,封装到一个“模板”里,再用这个模板创建具体对象。这个模板就是构造函数。
怎么做:用 function Person(name, age) { ... } 定义构造函数,在内部用 this.name、this.age 保存属性,用 this.sayHi = function () { ... } 添加方法。
易错点:构造函数是“用来创建实例的模板”,不要把它和普通函数调用混为一谈;本节示例统一用 new Person(...) 创建实例。
用构造函数创建实例对象
为什么需要:有了构造函数,创建多个同类对象时只需传入不同数据。
怎么想:new Person('张三', 18) 会创建一个实例,并把传入的 name、age 放到这个实例上。
怎么做:保留笔记代码:
html
<body>
<script>
// 1. 构造函数实现封装,封装人的姓名、年龄和 sayHi方法
function Person(name, age) {
this.name = name
this.age = age
this.sayHi = function () {
console.log('hi~')
}
}
// 实例化
const zs = new Person('张三', 18)
const ls = new Person('李四', 19)
console.log(zs)
console.log(ls)
console.log(zs === ls) // false
// 2. 构造函数实现封装有个小问题
console.log(zs.sayHi === ls.sayHi) // false 两个函数不一样
</script>
</body>易错点:zs 和 ls 虽然都由 Person 创建,但它们是两个不同实例,所以 zs === ls 为 false。
实例对象彼此独立、互不影响
为什么需要:封装后,每个实例应保存自己的数据,不能因为创建了 ls 就改掉 zs 的信息。
怎么想:zs 的 name 是“张三”,ls 的 name 是“李四”,它们各自保存自己的属性值。
怎么做:通过 console.log(zs)、console.log(ls) 观察两个实例;再用 console.log(zs === ls) 验证它们不是同一个对象。
易错点:不要因为两个对象“长得像”就认为相等。对象比较看是否同一引用,本例输出为 false,说明它们彼此独立。
同名方法不等于同一个函数
为什么需要:理解 zs.sayHi === ls.sayHi 为什么是 false,才能发现构造函数写法里的内存问题。
怎么想:每次 new Person(...) 都会执行一次 this.sayHi = function () { ... },相当于为每个实例各创建一个函数对象。
怎么做:代码里用 console.log(zs.sayHi === ls.sayHi) 比较,注释已给出结果 false,即“两个函数不一样”。
易错点:方法名都叫 sayHi,不代表它们是同一个函数。本节总结也指出:构造函数实例创建的对象彼此独立、互不影响;但前面学过的构造函数方法很好用,却存在“浪费内存”的问题。本节先把这个现象暴露出来。
调试过程与转写稿取舍
为什么需要:调试时要把“实例独立”和“方法不共享”分开验证。
怎么想:先看对象是否相等,再看方法是否相等;两个比较都输出 false,分别说明实例不同、函数对象也不同。
怎么做:按代码顺序运行,观察 console.log(zs)、console.log(ls)、zs === ls、zs.sayHi === ls.sayHi 四处输出。转写稿中有“通过构造函数 new 出来的实例对象身上有的一些方法”这一表述,可借它理解 zs.sayHi、ls.sayHi 是挂在各自实例上的方法,因此比较结果为 false。
易错点:转写稿大量内容在讲数组 reduce、find、every、Array.from 等内置构造函数方法,和本节“自定义构造函数实现封装”不是同一主题,不能硬套。转写稿中的同音字应按语义纠正,如“勾造函数”应为“构造函数”,“实力对象”应为“实例对象”,“返馈”应为“返回”,“初始证”应为“初始值”,“缩引号”应为“索引号”。本节只采用与构造函数、实例对象相关的表述,不编造数组内容。
总结:构造函数体现了面向对象的封装特性;实例对象彼此独立、互不影响;同时要记住示例暴露的问题——每个实例各自创建方法,zs.sayHi === ls.sayHi 为 false,存在浪费内存的问题。
说明与范围
提供的转写稿讲的是数组 reduce/find/every/Array.from 等内容,与本节“原型”笔记不匹配;为避免编造,下面只依据本节笔记扩写原型知识,不把数组内容写入原型推导。转写稿中的同音字问题(如“勾造函数/返馈”等)因内容无关,未用于正文。
原型对象-prototype
为什么需要:构造函数封装方法时,若把函数写在构造函数内部,每次 new 都会创建一份函数,浪费内存。
怎么想:把公共方法挂到构造函数的 prototype 上,让所有实例共享。
怎么做:Person.prototype.sayHi = function () { ... },实例调用 zs.sayHi()、ls.sayHi()。
易错点:原型对象里的函数若要用 this,不要用箭头函数;箭头函数没有 this,也不能做构造函数。
html
<body>
<script>
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.sayHi = function () {
console.log('hi~')
}
console.log(Person.prototype)
const zs = new Person('张三', 18)
const ls = new Person('李四', 19)
zs.sayHi()
ls.sayHi()
console.log(zs.sayHi === ls.sayHi) // true
</script>
</body>constructor 属性
为什么需要:用对象形式重写 prototype 会覆盖原原型内容,constructor 可能不再指向原构造函数。
怎么想:constructor 在原型对象里,默认指向该原型对象的构造函数。
怎么做:重写原型时手动写 constructor: Person。
易错点:不修正 constructor 会丢失指向;不要把它和实例的 __proto__ 混淆。
html
<body>
<script>
function Person(name) {
this.name = name
}
console.log(Person.prototype.constructor === Person) // true
Person.prototype = {
constructor: Person,
sing() {
console.log('我会唱歌')
},
dance() {
console.log('我会跳舞')
}
}
console.log(Person.prototype)
</script>
</body>原型 proto
为什么需要:实例对象要能使用构造函数原型对象上的方法。
怎么想:对象有 __proto__,指向构造函数的 prototype 原型对象。
怎么做:zs.__proto__ === Person.prototype 为 true。
易错点:prototype 是原型对象,__proto__ 是原型;__proto__ 现代浏览器显示为 [[Prototype]],意义相同;尽量不要修改它,否则影响性能。
html
<body>
<script>
function Person(name) {
this.name = name
}
const zs = new Person('张三')
console.log(zs)
console.log(zs.__proto__ === Person.prototype) // true
</script>
</body>原型链
为什么需要:访问对象成员时需要明确的查找路线。
怎么想:先找对象自身,再找它的原型对象,再找原型对象的原型,直到 Object 为止(null)。
怎么做:下面示例中 zs.sayHi 会按实例、Person.prototype、Object.prototype 的顺序查找。
易错点:找不到成员返回 undefined;不要随意改 __proto__,会影响性能。

html
<body>
<script>
function Person(name) {
this.name = name
}
const zs = new Person('张三')
zs.sayHi = function () {
console.log('实例对象的方法')
}
Person.prototype.sayHi = function () {
console.log('Person原型对象的方法')
}
Object.prototype.sayHi = function () {
console.log('Object原型对象的方法')
}
zs.sayHi()
</script>
</body>instanceof 运算符
为什么需要:检测实例对象与构造函数之间的原型链关系。
怎么想:看构造函数 prototype 是否存在于实例对象的原型链上。
怎么做:zs instanceof Person、arr instanceof Array、arr instanceof Object。
易错点:instanceof 检测的是原型链,不是只看构造函数名字;数组既是 Array 实例,也在 Object 原型链上。

html
<body>
<script>
function Person(name) {
this.name = name
}
function Person1(name) {
this.name = name
}
const zs = new Person('张三')
console.log(zs instanceof Person) // true
console.log(zs instanceof Person1) // false
const arr = [1, 2, 3]
console.log(arr instanceof Array) // true
console.log(arr instanceof Object) // true
console.log(arr.__proto__ === Array.prototype) // true
console.log(Array.prototype.__proto__ === Object.prototype) // true
</script>
</body>原型继承
为什么需要:公共属性和方法可写到父级,子级通过继承复用。
怎么想:JavaScript 大多借助原型对象实现继承;子构造函数原型指向父构造函数实例,并修正 constructor。
怎么做:Man.prototype = new Person(),再 Man.prototype.constructor = Man;子级独有方法挂到自己的原型上。
易错点:继承后要修正 constructor;子级独有方法应挂在子级原型上,避免影响其他子级;共享父实例属性时要注意相互影响。
html
<body>
<script>
function Person() {
this.eyes = 2
}
Person.prototype.eat = function () {
console.log('我会吃饭')
}
function Man() {}
function Woman() {}
Man.prototype = new Person()
Man.prototype.constructor = Man
const zs = new Man()
Woman.prototype = new Person()
Woman.prototype.constructor = Woman
const xl = new Woman()
Woman.prototype.baby = function () {
console.log('我会生孩子')
}
console.log(xl)
console.log(zs)
</script>
</body>小结
原型解决构造函数方法重复创建的问题;constructor 维护原型与构造函数的指向;__proto__ 连接实例与原型;原型链规定成员查找路线;instanceof 检测原型链关系;原型继承实现公共属性和方法复用。
课件图示








代码示例
示例一:构造函数 + 原型(封装与原型链)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>构造函数与原型</title>
</head>
<body>
<script>
// 1. 构造函数实现封装:封装姓名、年龄、sayHi 方法
function Person(name, age) {
this.name = name
this.age = age
// this.sayHi = function () { console.log('hi~') }
// 上面写法每 new 一次就创建一个新函数,浪费内存
}
// 2. 把方法挂到原型对象上,所有实例共享同一个函数
Person.prototype.sayHi = function () {
console.log('hi~', this.name) // 原型对象里的 this 也指向实例对象
}
const zs = new Person('张三', 18)
const ls = new Person('李四', 19)
zs.sayHi()
ls.sayHi()
console.log(zs === ls) // false
console.log(zs.sayHi === ls.sayHi) // true(共享同一个函数)
console.log(Person.prototype.constructor === Person) // true
console.log(zs.__proto__ === Person.prototype) // true
// 3. 原型链查找:实例自身 -> Person.prototype -> Object.prototype -> null
Object.prototype.sayHi = function () {
console.log('Object原型对象的方法')
}
zs.sayHi() // 就近原则,仍走 Person.prototype 上的方法
delete Person.prototype.sayHi
zs.sayHi() // Person.prototype 上没有了,沿 __proto__ 找到 Object.prototype
// 4. 用对象形式重写原型对象时,要手动把 constructor 指回来
Person.prototype = {
constructor: Person, // 不写这句,constructor 就不再指向 Person
sing() { console.log('我会唱歌') },
dance() { console.log('我会跳舞') }
}
console.log(Person.prototype.constructor === Person) // true
</script>
</body>
</html>关键行与易错点
Person.prototype.sayHi = function () {}:方法写在原型上,zs.sayHi === ls.sayHi才是true;写在构造函数里每次实例化都会新建函数,浪费内存。- 原型对象里的方法不要用箭头函数,箭头函数没有自己的
this,会拿不到实例对象;构造函数同理,箭头函数不能做构造函数。 Person.prototype = { ... }是整体覆盖,会把原来的constructor一起覆盖掉,必须手动写constructor: Person指回构造函数。- 区分两个名字:
prototype是原型对象(构造函数身上的),__proto__是原型(实例对象身上的);__proto__非标准属性,现代浏览器显示为[[Prototype]],且不要手动修改,影响性能。 - 成员查找顺序:自身 →
Person.prototype→Object.prototype→null,这就是原型链。
示例二:原型继承
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>原型继承</title>
</head>
<body>
<script>
// 1. 父级:抽取公共的属性和方法
function Person() {
this.eyes = 2
}
Person.prototype.eat = function () {
console.log('我会吃饭')
}
// 2. 子级构造函数
function Man() {}
function Woman() {}
// 3. 借助原型对象实现继承
Man.prototype = new Person()
Man.prototype.constructor = Man // 手动指回构造函数
Woman.prototype = new Person()
Woman.prototype.constructor = Woman
// 4. 子级原型上新增方法,不影响兄弟类
Woman.prototype.baby = function () {
console.log('我会生孩子')
}
const zs = new Man()
const xl = new Woman()
zs.eat() // 继承自 Person.prototype
xl.eat() // 继承自 Person.prototype
xl.baby() // Woman 自己扩展的方法
console.log(zs.baby) // undefined,Man 没有这个方法
console.log(zs instanceof Man) // true
console.log(zs instanceof Person) // true,构造函数原型在原型链上
console.log(xl instanceof Man) // false
</script>
</body>
</html>关键行与易错点
Man.prototype = new Person():继承的本质是把子级原型指向一个父级实例,这样子级实例沿__proto__就能找到Person.prototype上的eat。- 赋值后必须补一句
Man.prototype.constructor = Man,否则constructor会指向Person。 - 顺序不能乱:
Woman.prototype.baby = ...必须写在Woman.prototype = new Person()之后,否则会被覆盖掉。 Woman上扩展的baby只挂在Woman.prototype上,Man的实例访问不到,这是各自原型独立的体现。instanceof检测的是“构造函数的prototype是否在实例的原型链上”,所以zs instanceof Person也是true。
示例三:reduce 计算薪资 + find / every / Array.from
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>数组常见方法</title>
</head>
<body>
<div>1</div>
<div>2</div>
<div>3</div>
<script>
const employees = [
{ name: '张三', salary: 10000 },
{ name: '李四', salary: 15000 },
{ name: '王五', salary: 20000 }
]
// 1. reduce 累加求薪资总和,初始值必须写 0
const total = employees.reduce((prev, curr) => prev + curr.salary, 0)
console.log(total) // 45000
// 2. find 找满足条件的第一个元素,findIndex 找它的索引
const target = employees.find(item => item.name === '李四')
const index = employees.findIndex(item => item.name === '李四')
console.log(target, index) // {name:'李四',salary:15000} 1
// 3. every 要求全部满足,some 只要有一个满足
const arr = [10, 20, 30]
console.log(arr.every(item => item >= 10)) // true
console.log(arr.some(item => item >= 30)) // true
console.log(arr.some(item => item >= 40)) // false
// 4. Array.from 把伪数组转成真数组
const divs = document.querySelectorAll('div') // 伪数组,没有 push
const newArr = Array.from(divs) // 返回真数组,不影响原伪数组
newArr.push('123')
console.log(newArr)
console.log(divs.length) // 3,原来的伪数组没被改变
</script>
</body>
</html>关键行与易错点
reduce回调里累加的是curr.salary,不能直接写prev + curr,否则就是对象相加得到字符串"[object Object]..."。- 这里的初始值
0不能省略:省略时第一次的prev是数组第一个对象,无法做数值累加。 - 没有初始值时,
reduce的遍历次数比数组长度少一次(首元素直接当prev);有初始值时,有几个元素就遍历几次,第一次的prev就是初始值。 - 回调必须
return上一次的结果,箭头函数一行时可省略{}和return。 find返回的是数组元素本身(对象),找不到返回undefined;filter返回的是数组,这是两者最容易混的点。findIndex找不到返回-1。every是“每个都满足才true”,some是“有一个满足就true”,返回的都是布尔值。Array.from是挂在构造函数上的静态方法,写法是Array.from(伪数组);它返回新数组,不修改原伪数组,转换后即可使用push、every、find等数组方法。
常见错误
1. 方法写在构造函数体内,实例之间方法不共享
- 现象:
zs.sayHi === ls.sayHi返回false,实例越多内存占用越大。 - 原因:
this.sayHi = function(){}在每次new时都重新创建一个函数对象,每个实例各持一份。 - 修法:把方法挂到
Person.prototype上,构造函数体内只保留实例独有的属性(name、age)。
2. 用对象字面量重写 prototype 后,constructor 丢失
- 现象:
Person.prototype.constructor === Person变成false,指向了Object。 - 原因:
Person.prototype = { sing(){}, dance(){} }是整体赋值,把原来带constructor的原型对象整个替换掉了。 - 修法:在字面量里手动补
constructor: Person;或者逐个Person.prototype.xxx = ...赋值。
3. 原型方法写成箭头函数,this 拿不到实例
- 现象:
Person.prototype.sayHi = () => console.log(this.name)输出undefined或指向 window。 - 原因:箭头函数没有自己的 this,取的是定义时外层作用域的 this,不会随调用者(实例)改变。
- 修法:原型方法一律用普通函数(或 ES6 方法简写
sayHi(){});箭头函数只用于不需要 this 的回调场景。
4. 原型继承先加方法、后赋值原型,方法被覆盖
- 现象:
Man.prototype.xxx明明写了,实例却调不到,或Man.prototype里的内容全没了。 - 原因:
Man.prototype = new Person()是整体替换,之前挂在Man.prototype上的成员全部被丢弃。 - 修法:先完成
Man.prototype = new Person()与Man.prototype.constructor = Man,再往上追加子类特有方法。
5. reduce 处理对象数组时省略初始值,或累加错了字段
- 现象:结果是
"[object Object][object Object]"字符串拼接或NaN。 - 原因:没有初始值时,
prev取数组第一个元素(一个对象),prev + curr变成对象运算;或写成prev + curr而不是prev + curr.salary。 - 修法:必须传第二个参数
0作为初始值,累加项取curr.salary这类具体数值字段。
6. find / findIndex 的返回值判断写错
- 现象:
if (arr.findIndex(...))在找到下标 0 的元素时被判为不存在;或把find的返回值当数组去.length。 - 原因:
findIndex找不到返回-1,找到可能是0;find找不到返回undefined,找到返回的是元素本身而非数组。 - 修法:
findIndex用!== -1判断;find用!== undefined判断;需要多个结果时改用filter。
7. Array.from 忘记接收返回值
- 现象:
Array.from(divs)写完后divs.push(...)依然报 "not a function"。 - 原因:
Array.from是静态方法,返回一个新数组,不修改原类数组对象。 - 修法:
const arr = Array.from(divs),后续操作arr;原类数组保持不变是特性而非 bug。
练习
练习 1(构造函数 + 原型) 写出 Person 构造函数(name、age),要求两个实例 zs、ls 调用 sayHi 输出 hi~,且满足 zs.sayHi === ls.sayHi。再用对象字面量方式重写 Person.prototype,同时提供 sing、dance 两个方法,保证 Person.prototype.constructor === Person 仍为 true。
练习 2(reduce 求薪资) 给定 [{name:'张三',salary:10000},{name:'李四',salary:15000},{name:'王五',salary:20000}],用 reduce 的箭头函数写法求当月总薪资。回答两个问题:① 第二个参数 0 能不能省?省略后会发生什么?② 如果把 prev + curr.salary 误写成 prev + curr,结果是什么?
练习 3(find / findIndex) 有一个商品对象数组,要求:① 找出第一个价格大于 100 的商品对象;② 找出该商品在数组中的下标;③ 写出一段代码,正确判断"价格大于 1000 的商品是否存在",并说明为什么不能直接写 if (arr.findIndex(fn))。
练习 4(原型继承) 定义父构造函数 Person(eyes = 2,原型上有 eat),子构造函数 Man、Woman,让二者继承 Person,再给 Woman 单独加 baby 方法。要求:zs instanceof Person 为 true,Man.prototype.constructor === Man,且 Woman 的 baby 不会出现在 Man 的实例上。写出完整代码并说明关键两步的顺序。
练习 5(every / some + Array.from) 用 document.querySelectorAll('div') 获取元素,先用 every 检测是否每个元素的 dataset.index 都存在,再用 some 检测是否存在某个特定 index。要求代码能正常调用 every/some,并说明为什么不能直接对 querySelectorAll 的返回值调用这两个方法。
原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | 数组reduce方法基本使用 | 08:50 |
| 2 | 数组reduce执行过程 | 08:46 |
| 3 | reduce计算薪资小案例 | 06:48 |
| 4 | 数组find和findIndex方法 | 12:09 |
| 5 | 数组every和some方法 | 05:56 |
| 6 | Array.from转换真数组 | 05:34 |
小结
- 面向对象在前端更多体现在"数据 + 行为"的组织上,构造函数负责封装实例独有的属性,原型负责承载共享的方法,二者分工不能混。
prototype(原型对象)、__proto__(原型,即[[Prototype]])、constructor(指回构造函数)三者是同一套机制的不同视角,任何一次整体替换prototype都要补回constructor。- 原型链决定了成员查找的路线:自身 → 原型对象 → Object 原型 →
null;instanceof判断的正是构造函数的prototype是否在这条链上。 - 原型继承的关键顺序是:先赋值
Child.prototype = new Parent(),紧接着修正constructor,最后再追加子类独有方法。 reduce的两个参数决定了它的行为差异:有无初始值会影响遍历次数和第一次的prev来源;处理对象数组时初始值0和"取哪个字段累加"是两个必错点。find返回元素、filter返回数组、findIndex返回下标(找不到为-1)、every/some返回布尔值,返回值类型不同决定了后续判断写法不能通用。Array.from是挂在构造函数上的静态方法,返回新真数组且不改变原类数组;类数组(arguments、querySelectorAll、children)拿到后先转换再使用数组方法。- 这些 API 不需要死记,遇到不熟悉的先查 MDN,确认三件事:作用、参数含义、返回值——这比背语法更可靠。