从slice看稀疏密集数组,有何高见?

摘要:卑鄙是卑鄙者的通行证,高尚是高尚者的墓志铭。 ——北岛《回答》 看北岛就是从这两句诗开始的,高尚者已死,只剩卑鄙者在世间横行。 本文为读 lodash 源码的第一篇,后续文章会更新到这个仓库中,欢迎 star: "
卑鄙是卑鄙者的通行证,高尚是高尚者的墓志铭。 ——北岛《回答》 看北岛就是从这两句诗开始的,高尚者已死,只剩卑鄙者在世间横行。 本文为读 lodash 源码的第一篇,后续文章会更新到这个仓库中,欢迎 star:pocket-lodash gitbook也会同步仓库的更新,gitbook地址:pocket-lodash 引言 你可能会有点奇怪,原生的 slice 方法基本没有兼容性的问题,为什么 lodash 还要实现一个 slice 方法呢? 这个问题,lodash 的作者已经在 why not the 'baseslice' func use Array.slice(), loop faster than slice? 的 issue 中给出了答案:lodash 的 slice 会将数组当成密集数组对待,原生的 slice 会将数组当成稀疏数组对待。 密集数组VS稀疏数组 我们先来看看犀牛书是怎样定义稀疏数组的: 稀疏数组就是包含从0开始的不连续索引的数组。通常,数组的length属性值代表数组中元素的个数。如果数组是稀疏的,length属性值大于元素的个数。 如果数组是稀疏的,那么这个数组中至少有一个以上的位置不存在元素(包括 undefined )。 例如: var sparse = new Array(10) var dense = new Array(10).fill(undefined) 其中 sparse 的 length 为10,但是 sparse 数组中没有元素,是稀疏数组;而 dense 每个位置都是有元素的,虽然每个元素都为undefined,为密集数组 。 那稀疏数组和密集数组有什么区别呢?在 lodash 中最主要考虑的是两者在迭代器中的表现。 稀疏数组在迭代的时候会跳过不存在的元素。 sparse.forEach(function(item){ console.log(item) }) dense.forEach(function(item){ console.log(item) }) sparse 根本不会调用 console.log 打印任何东西,但是 dense 会打印出10个 undefined 。 源码总览 当然,除了对待稀疏数组跟原生的 slice 不一致外,其他的规则还是一样的,下面是 lodash 实现 slice 的源码。 function slice(array, start, end) { let length = array == null ? 0 : array.length if (!length) { return [] } start = start == null ? 0 : start end = end === undefined ? length : end if (start < 0) { start = -start > length ? 0 : (length + start) } end = end > length ? length : end if (end < 0) { end += length } length = start > end ? 0 : ((end - start) >>> 0) start >>>= 0 let index = -1 const result = new Array(length) while (++index < length) { result[index] = array[index + start] } return result } 不传参的情况 let length = array == null ? 0 : array.length if (!length) { return [] } 不传参时,length 默认为0,否则获取数组的长度。注意这里用的是 array == null ,非 array === null ,包含了 undefined 的判断。 所以在不传参调用 lodash 的 slice 时,返回的是空数组,而原生的 slice 没有这种调用方式。 处理start参数 start 参数用来指定截取的开始位置。 先来看下 MDN 对该参数的描述: 如果该参数为负数,则表示从原数组中的倒数第几个元素开始提取。
阅读全文