这是我参与新手入门的第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
替换.
最后
这是新人活动最后一篇了。对于这三篇文章,自我感觉是真好,现实也是真残酷。不知道还能不能拿到新人优秀奖…如果觉得不错还请点个赞,帮个忙…十分感谢!