这是我参与新手入门的第3篇文章
上篇文章对数组api进行了梳理,在整体上对数组api有了清晰的了解。接下来这篇文章将手动实现数组的部分api,从底层上来认识认识它们是怎么实现的...
手写every
先上源码
Array.prototype._every = function(callBack, thisArg) {
if(this == null) {
throw new TypeError()
}
if(typeof callBack !== 'function') {
throw new TypeError()
}
//问题1
//if(thisArg == null) {
// thisArg = window
//}
const obj = Object(this)
const len = obj.length >>> 0 // 问题2
for(let i = 0; i < len; i++) {
if(i in obj && !callBack.call(thisArg, obj[i], i, obj)) {
return false
}
}
return true //若其是一个空数组,则直接返回true
}
基本思路如下:
- 安全性考虑,对于
this和callBack是否符合类型,进行判断。 - 将this抽象为对象(通用性), 同时获取转换后的
length。 - 核心操作,在遍历数组时跳过空项,若存在一个不满足
callBack的返回false。
对于代码中出现的问题解答:
问题1: 为什么可以不考虑thisArg为null或者undefined的情况?
因为代码中是使用了call来实现绑定this的。其本身内部就考虑了thisArg为null或者undefined的情况。不了解call的可以看手写call,apply
问题2:obj.length >>> 0有什么用?
>>> 0有3个作用:(1)、将非数字类型转换为number; (2)、将数字类型转换为32位 无符号 整数。 所以,它还有个副作用,对于负数x来说会将其转换为2^32 + x。
手写filter
源码如下:
Array.prototype._filter = function(callBack, thisArg) {
if(this == null) {
throw new TypeError()
}
if(typeof callBack !== 'function') {
throw new TypeError()
}
// if(thisArg == null) {
// thisArg = window //严格模式下为undefined
// }
const obj = Object(this)
let len = obj.length >>> 0
const result = new Array(len) // 预先分配长度
let index = 0
for(let i = 0; i < len; i++) {
if(i in this && callBack.call(thisArg, obj[i], i, obj)) {
result[index++] = obj[i]
}
}
result.length = index //调整长度为合适状态
return result
}
思路基本没变...
手写reduce
源码:
Array.prototype._reduce = function(callBack, initialValue) {
if(this == null) {
throw new TypeError()
}
if(typeof callBack !== 'function') {
throw new TypeError()
}
const obj = Object(this)
const len = obj.length >>> 0
//问题1
const hasInit = arguments.length > 1
if(len === 0 && !hasInit) {
throw new TypeError()
}
//问题2
let accumulator, i = 0
if(hasInit) {
accumulator = initialValue
} else {
while(i < len && !(i in obj)) {
i++
}
if(i >= len) {
throw new TypeError()
}
accumulator = obj[i++]
}
while(i < len) {
if(i in obj) {
// 问题3
accumulator = callBack(accumulator, obj[i], i, obj)
}
++i
}
return accumulator
}
对于代码中出现的问题解答:
问题1:怎么判断是否提供了initialValue呢?使用initialValue !== undefined判断行不行?
不能使用initialValue !== undefined来判断是否提供了initialValue。其无法判断initialValue赋值为undefined的情况。
问题2: reduce内部对于没有提供initialValue时,accmulator的初值什么?
注意:当没有提供initialValue时,对于accmulator的初值是第一个已赋值的项(空数组报错)。其不会考虑未赋值的。我在初次写代码的时候没有考虑到这,就离谱...
问题3:accumulator的值是怎么修改的?
注意:accumulator是每次迭代,callBack的返回值赋值给accumulator的。也就是说accumulator是由callBack的返回值确定的!
由于用的少.开始还以为
accumulator是通过某种响应式操作处理的呢...有点憨0.0
手写includes
源码:
Array.prototype._includes = function(value, fromIndex) {
if(this == null) {
throw new TypeError()
}
const obj = Object(this)
const len = obj.length >>> 0
if(len === 0) { //如果是空数组则直接返回false
return false
}
//问题1
fromIndex = fromIndex | 0
//问题2
let n = Math.max(fromIndex < 0 ? len - Math.abs(fromIndex) : fromIndex, 0)
const sameValueZero = (x, y) => x === y || (x !== x && y !== y)
while(n < len) {
if(sameValueZero(value, obj[n])) {
return true
}
n++
}
return false
}
对于代码中出现的问题解答:
问题1:fromIndex = fromIndex | 0有什么用?
fromIndex | 0有两个作用:(1)、将fromIndex转换为number类型. (2)、将fromIndex转换为32位 有符号 整数。注意是有符号。
问题2:如果fromIndex是负数,该怎么处理?
从代码可知,若fromIndex是负数,则使用length - Math.abs(fromIndex)替换。之后再判断,如果length - Math.abs(fromIndex)小于0,则再用0替换.
觉得有帮助就点个赞呗~