数组的七个 API 的实现
join()
将一个数组(或一个类数组对象)的所有元素连接成一个字符串并返回这个字符串。
var array = ['a','b','c']
array.join('-') // 结果是 'a-b-c'
画一下内存图:
jzdg9gaxm4hh.png
- array.join 实际上是 Array.prototype.join 对应的函数(array.join === Array.prototype.join === ADDR401)
- array.join('-') 等价与 array.join.call(array, '-')
- join 函数通过 this 和 arguments[0] 可以得到 array 和 '-' 两个值
所以我们可以大胆猜测 array.join 的源代码大概是这样的
//只接受一个参数; char 是 '-' 的意思
Array.prototype.join = function(char){
// 用 this 来接收数组
// 首先 取第一个值 this[0] ,如果没有就是空字符
let result = this[0] || ''
let length = this.length
// 遍历 this
for (let i =1; i < length; i++){
//再从 i=1 (第二个值)开始取
//遍历 this ,让 result 每次都加上 '-' 再加上 第 i 个值
result +=char + this[i]
}
return result
}
this 就是 array,因为你使用 array.join('-') 来调用 join 的(隐式指定this)
slice()
返回一个新的数组对象,这一对象是一个由 begin和 end(不包括end)决定的原数组的浅拷贝。原始数组不会被改变。
array.slice(beginIndex, endIndex)
显而易猜,源码大概大概大概是这样的
//Array 原型 有个slice 属相 ,接受两个参数(开始,结束)
Array.prototype.slice = function(begin, end){
// 声明一个空数组
let result = []
//如果 begin 不存在,让begin是 0
begin = begin || 0
// 如果 end 不存在,让 end 是 数组的长度
end = end || this.length
// 遍历数组
// i 从 0 到 end , 每一个都 push 到 result数组 里面
for(let i = begin; i< end; i++){
result.push(this[i])
}
return result
}
于是很多前端用 slice 来将伪数组,转化成数组
array = Array.prototye.slice.call(arrayLike)
或者
array = [].slice.call(arrayLike)
ES 6 看不下去这种蹩脚的转化方法,出了一个新的 API
array = Array.from(arrayLike)
专门用来将伪数组转化成真数组。
注意:伪数组与真数组的区别就是:伪数组的原型链中没有 Array.prototype,而真数组的原型链中有 Array.prototype。因此伪数组没有 pop、join 等属性。
sort()
- 听说大部分的语言内置的 sort 方法都是快速排序算法。我们就简化成选择排序吧
//给数组排序
// sort 接受一个函数 fn
Array.prototype.sort = function(fn){
//fn 默认就是一个 a-b 这样的函数
fn = fn || ((a,b)=> a-b)
// 两个数 ,谁小就将谁放在第一位
// 所以两个数只有排一轮,三个数排两轮 ;this.length - 1
let roundCount = this.length - 1 // 比较的轮数
for (let i = 0; i< roundCount; i++){
// 假定 第一轮的第一个就是最小的
let minIndex = this[i]
//遍历
for(let k = i+1; k < this.length; K++){
// 调用这个函数,这个函数用第一个参数 减去 第二个参数(a - b)
// this 的 看k 项 减去 this 的 i 项 ,若值 <0 ,第 k 项比第 i 项 小
if(fn.call(null,this[k],this[i]) < 0){
// 小的要放在前面,所以讲 第 k 项的值与 第 i 项的值互换
[this[i],this[k] = [this[k],this[i]]]
}
}
}
}
es6 语法 ,a 与b 的值互换
a = 'a'
//"a"
b ='b'
//"b"
[a,b]=[b,a]
//(2) ["b", "a"]
a
//"b"
b
//"a"
- fn.call(null, this[k], this[i]) 决定了第 k 项和第 i 项的前后(大小)关系。
- fn 为 (a,b) => a-b 表示啥?fn 为 (a,b) => b-a 又表示啥?
不重要,因为如果前者不符合你的需求,那么后者一定符合你的需求,你只需要试两边就知道用哪一个了。
forEach()
对数组的每个元素执行一次提供的函数。
forEach源代码:
Array.prototype.forEach = function(fn){
//遍历 this
for (let i=0; i < this.length;i++){
// 如果 i 是 this 的 key
if( i in this){
fn.call(undefined, this[i], i ,this)
}
}
}
a = [1,2,3]
(3) [1, 2, 3]
a.forEach ( (item)=>{
console.log(item)
})
VM132:2 1
VM132:2 2
VM132:2 3
a.forEach((item,index,array)=>{
console.log(item ,index,array )
// fn.call(undefined, this[i], i ,this)
})
1 0 (3) [1, 2, 3]
2 1 (3) [1, 2, 3]
3 2 (3) [1, 2, 3]
forEach 把给我的函数在遍历的时候 call 一下
//fn 是 (item)=>{console.log(item) 这个函数
// a 是 this
//a[i]] 是 this[i]
//用item 来命名第一个参数 this[i] 元素
//用 index 来命名 i 下标
//array 来表示 this ,因为已经知道了this 是a 这个数组 整个数组
forEach 和 for 的区别主要有两个:
- forEach 没法 break
break: 此语句导致程序终止包含它的循环,并进行程序的下一阶段(整个循环后面的语句),即,不是跳到下一个循环周期而是退出循环。 - forEach 用到了函数,所以每次迭代都会有一个新的函数作用域;而 for 循环只有一个作用域(著名前端面试题就是考察了这个)
map():映射
创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。
Array.prototype.map = function(fn){
let result = []// 空数组
for(let i=0;i<this.length; i++){
if(i in this) {
result[i] = fn.call(undefined, this[i], i, this)
}
}
return result //将返回的值放到数组里面
}
- 由于 map 和 forEach 功能差不多,区别只有返回值而已,所以我推荐忘掉 forEach,只用 map 即可。
- 想用 map 的返回值就用,不用想就放在一边。
filter():过滤
创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。就是 过滤操作。
Array.prototype.filter = function(fn){
let result = []
let temp
for(let i=0;i<this.length;i++){
if(i in this){
if(temp = fn.call(undefined,this[i],i,this)){
// 判读temp = fn.call()的值是否为真值
//值为真 就放入数组里面
result.push(temp)
// 有选择的将值加入数组(满足条件)
}
}
}
return result
}
<!-- 6个假值
false 0 '' NAN undefimed null
-->
a = [1,2,3,4,5,6]
(6) [1, 2, 3, 4, 5, 6]
a.filter((value)=>{
return value % 2 ===0
})
(3) [2, 4, 6]
- fn.call() 返回真值就 push 到返回值,没返回真值就不 push。
reduce :求和
对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值。
Array.prototype.reduce = function(fn,init){
let result = init//初始值
// 遍历每一个元素;fn.call()
// 除了 this[i],i,this 这三个参数,还要传一个每一次调用之后(上一次)的结果
//reduce :每一次把上次的结果和当前的数字进行操作;fn进行操作
for(let i=0;i<this.length;i++){
if(i in this){
result = fn.call(undefined,result,this[i],i,this)
}
}
return result
}
a= [1,2,3,4,5]
(5) [1, 2, 3, 4, 5]
a.reduce((result,item,index,arrray)=>{
return result + item
},0)
15
map、filter 和 reduce 的区别:
- map 是等量的一对一的关系
- filter 是从多到少的过程 (过滤)
- reduce 进行一个累加
map、filter 和 reduce 的联系:
map 可以用 reduce 表示
array2 = array.map( (v) => v+1 )
可以写成
array2 = array.reduce( (result, v)=> {
result.push(v + 1)//push 当前值加 1
return result //返回 result作为下一次的result
}, [ ] )//result 最开始是空数组
filter 可以用 reduce 表示
array2 = array.filter( (v) => v % 2 === 0 )
可以写成
array2 = array.reduce( (result, v)=> {
if(v % 2 === 0){ result.push(v) }
return result
}, [])
网友评论