join
-
先从最简单的数组操作开始:
1
2var array = ['a','b','c']
array.join('-') // 结果是 'a-b-c' -
我们画一下内存图:
- array.join 实际上是 Array.prototype.join 对应的函数(array.join === Array.prototype.join === ADDR401)
- array.join(’-’) 等价与 array.join.call(array, ‘-’)
- join 函数通过 this 和 arguments[0] 可以得到 array 和 ‘-’ 两个值
-
猜测array.join 的源码:
1
2
3
4
5
6
7
8Array.prototype.join = function(char){
let result = this[0] || ''
let length = this.length
for(let i=1; i< length; i++){
result += char + this[i]
}
return result
}
slice
-
用法:
1
array.slice(beginIndex, endIndex)
-
猜测源码:
1
2
3
4
5
6
7
8
9Array.prototype.slice = function(begin,end){
let result = []
begin = begin || 0
end = end || this.length
for(let i= begin; i < end; i++){
result.push(this[i])
}
return result
} -
slice可以将伪数组转化为数组
1
2array = Array.prototype.slice.call(arrayLike)
array = [].slice.call(arrayLike) -
ES6 有新的方法解决上面问题
1
array = Array.from(arrayLike)
-
PS: 伪数组和真数组的区别就是原型链有没有
Array.prototype
,即操作数组的API
sort
- 猜测源码:
1
2
3
4
5
6
7
8
9
10
11
12Array.prototype.sort = function(fn){
fn = fn || (a,b) => a-b
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++){
if(fn.call(null, this[k],this[i]) < 0) {
[this[i], this[k]] = [this[k], this[i]]
}
}
}
}
splice
-
用法:
1
arr.splice(start, count, addElement1, addElement2, ...);
splice
的第一个参数是删除的起始位置(从0开始),第二个参数是被删除的元素个数。如果后面还有更多的参数,则表示这些就是要被插入数组的新元素。
-
猜测源码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17Array.prototype.splice = function(startIndex, deleteCount, ...params){
let removeArray = this.slice(startIndex,startIndex+deleteCount)
let endArray = this.slice(startIndex+deleteCount)
let originalLength = this.length
for(let i = 0;i < params.length; i++ ){
this[startIndex + i] = params[i]
}
let currentLength = startIndex + params.length
for(let i = 0;i < endArray.length; i++) {
this[currentLength + i] = endArray[i]
}
this.length = originalLength - deleteCount + params.length
return removeArray
}
forEach、 map、filter 和 reduce
forEach
-
猜测源码:
1
2
3
4
5
6
7Array.prototype.forEach = function(fn){
for(let i = 0; i < this.length; i++){
if(i in this) {
fn.call(undefined, this[i], i, this)
}
}
} -
forEach
和for
的区别有两个:forEach
不能break
forEach
用到了函数,所以每次迭代都会有一个新的函数作用域。
map
-
map
方法将数组的所有成员依次传入参数函数,然后把每一次的执行结果组成一个新数组返回。 -
猜测源码:
1
2
3
4
5
6
7
8
9Array.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
} -
PS:
map
和forEach
的区别就是map
有返回值,forEach
没有
filter
-
filter
方法用于过滤数组成员,满足条件的成员组成一个新数组返回。 -
猜测源码:
1
2
3
4
5
6
7
8
9
10
11
12Array.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)) {
result.push(this[i])
}
}
}
return result
} -
fn.call()返回真值就push到返回值,没有返回真值就不push
reduce
-
reduce
方法是依次处理数组的每个成员,最终累计为一个值 -
猜测源码:
1
2
3
4
5
6
7
8Array.prototype.reduce = function(fn, init){
let result = init
for(let i = 0; i < this.length; i++){
if(i in this){
result = fn.call(undefined, result, this[i], i, this)
}
}
} -
map、filter 和 reduce 的区别:
-
map、filter 和 reduce 的联系:
-
map 可以用 reduce 表示:
1
2
3
4
5
6array2 = array.map( (v) => v+1 )
可以写成
array2 = array.reduce( (result, v)=> {
result.push(v + 1)
return result
}, [] ) -
filter 可以用 reduce 表示:
1
2
3
4
5
6
7
8array2 = array.filter( (v) => v % 2 === 0 )
可以写成
array2 = array.reduce((result, v) => {
if(v % 2 === 0){
result.push(v + 1)
}
return result
})
-