-
Notifications
You must be signed in to change notification settings - Fork 52
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
读lodash源码之从slice看稀疏数组与密集数组 #1
Comments
start = start == null ? 0 : start
end = end === undefined ? length : end 为何end不判断为null的情况? |
不传参的情况下,我喜欢这种判断: let length = array && array.length || 0 请问有什么弊端吗? |
你不说我还没注意到。 在规范中规定, 而 所以这里不判断 |
这种判断也是可以的 |
@yeyuqiudeng 你读源码有没有什么顺序,或者说按什么逻辑来读源码的,我也想跟您一起读读源码,谢谢! |
我之前读 Zepto 时,按照模块,一个模块一个模块读,最先读的是核心模块。在读的时候会先了解模块的作用,api的用法和参数,看不明白的时候会调用api,打debug,看看是怎样运行的。 读 lodash 就简单点了,看lodash的文档,按api的文档顺序来读。 |
@yeyuqiudeng 好的,非常感谢,跟着你的顺序读。 |
看北岛就是从这两句诗开始的,高尚者已死,只剩卑鄙者在世间横行。
本文为读 lodash 源码的第一篇,后续文章会更新到这个仓库中,欢迎 star:pocket-lodash
gitbook也会同步仓库的更新,gitbook地址:pocket-lodash
引言
你可能会有点奇怪,原生的 slice 方法基本没有兼容性的问题,为什么 lodash 还要实现一个 slice 方法呢?而且 lodash 中的 slice 方法还要比原生的慢。
这个问题,lodash 的作者已经在 why not the 'baseslice' func use Array.slice(), loop faster than slice? 的 issue 中给出了答案:lodash 的 slice 会将数组当成密集数组对待,原生的 slice 会将数组当成稀疏数组对待。
密集数组VS稀疏数组
我们先来看看犀牛书是怎样定义稀疏数组的:
如果数组是稀疏的,那么这个数组中至少有一个以上的位置不存在元素(包括
undefined
)。例如:
其中
sparse
的length
为10,但是sparse
数组中没有元素,是稀疏数组;而dense
每个位置都是有元素的,虽然每个元素都为undefined
,为密集数组 。那稀疏数组和密集数组有什么区别呢?在 lodash 中最主要考虑的是两者在迭代器中的表现。
稀疏数组在迭代的时候会跳过不存在的元素。
sparse
根本不会调用console.log
打印任何东西,但是dense
会打印出10个undefined
。源码总览
当然,除了对待稀疏数组跟原生的 slice 不一致外,其他的规则还是一样的,下面是 lodash 实现 slice 的源码。
不传参的情况
不传参时,
length
默认为0,否则获取数组的长度。注意这里用的是array == null
,非array === null
,包含了undefined
的判断。所以在不传参调用 lodash 的 slice 时,返回的是空数组,而原生的 slice 没有这种调用方式。
处理start参数
start
参数用来指定截取的开始位置。先来看下 MDN 对该参数的描述:
因此这段是处理省略的情况,省略时,默认值为0。
这段是处理负数的情况。
如果负数取反后比数组的长度还要大,即超出了数组的范围,则取值为0,表示从开始的位置截取,否则用
length + start
,即向后倒数。最后,用在
>>>
来确保start
参数为整数或0。因为 lodash 的 slice 除了可以处理数组外,也可以处理类数组,因此第一个参数
array
可能为一个对象,length
属性不一定为数字。处理end参数
end
参数用来指定截取的结束位置。同样来看下 MDN 对些的描述:
这段是处理
end
被省略的情况,省略时,end
默认为为length
,即截取到数组的末尾。这是处理
end
比数组长度大的情况,如果被数组长度大,也会截取到数组的末尾。这段是处理负值的情况,如果为负值,则从数组末尾开始向前倒数。
这里没有像
start
一样控制end
的向前倒数完后是否为负数,因为后面还有一层控制。获取新数组的长度
新数组的长度计算方式很简单,就是用
edn - start
即可得出。上面说到,没有控制最终
end
是否为负数的情况。这里用的是start
和end
的比较,如果start
比end
大,则新数组长度为0,即返回一个空数组。否则用end - start
来计算。这里同样用了无符号右移位运算符来确保
length
为正数或0。截取并返回新数组
result
为新数组容器。用
while
循环,从start
位置开始,获取原数组的值,依次存入新的数组中。因为是通过索引取值,如果遇到稀疏数组,对应的索引值上没有元素时,通过数组索引取值返回的是
undefined
, 但这并不是说稀疏数组中该位置的值为undefined
。最后将
result
返回。参考
License
署名-非商业性使用-禁止演绎 4.0 国际 (CC BY-NC-ND 4.0)
最后,所有文章都会同步发送到微信公众号上,欢迎关注,欢迎提意见:
作者:对角另一面
The text was updated successfully, but these errors were encountered: