类数组的定义是什么?

2,597 阅读2分钟

by @zhangbao(zhangbao) #0105

概览

什么是类数组呢?查看 Lodash _.isArrayLike(value) 函数的实现,得知参数 value 须符合下列条件:

  1. 不是函数
  2. length 属性
  3. length 属性值是不大于 Number.MAX_SAFE_INTEGER 的自然数

字符串就符合这个条件,说明它就是类数组。

carbon.png

Lodash 的实现

著名 JS 工具库 Lodash 提供了一个工具函数:_.isArrayLike(value),检查某个值是否为类数组的。下面是官方给的用例:

_.isArrayLike([1, 2, 3]);
// => true
 
_.isArrayLike(document.body.children);
// => true
 
_.isArrayLike('abc');
// => true
 
_.isArrayLike(_.noop);
// => false

我悄悄的看了看实现的源码(这里这里)。

_.isArrayLike 的实现细节

isArrayLike

下面是定义函数 isArrayLike 的地方:

import isLength from './isLength.js'

function isArrayLike(value) {
  return value != null && typeof value !== 'function' && isLength(value.length)
}

首先检查输入的值:

  1. value != null:这个判断使用的是 !=,而非 !==。令判断结果为 false 的值有两个:nullundefined。就是说,如果向 isArrayLike 函数传入的值是 nullundefined,那么第一个判断都未满足,直接返回 false

💡 提示: 这里引入了 JS 一个小怪癖的地方,就是规范中定义对于抽象相等算法( == 内部执行的就是此算法):null == undefined 比较结果返回 true,自然 null != undefined 的返回结果为 false

  1. typeof value !== 'function':传入值不能是函数,如果函数的话,也不算是类数组。
  2. isLength(value.length):最后检查传入值的 length 属性,isLength(value.length) 返回值即 isArrayLike 的返回值。

isLength

isLength 函数是用来干什么的?我们来看下:

const MAX_SAFE_INTEGER = 9007199254740991

/**
 * Checks if `value` is a valid array-like length.
 *
 * **Note:** This method is loosely based on
 * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength).
 *
 * ...
 *
 */
function isLength(value) {
  return typeof value === 'number' &&
    value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER
}

根据注释,我们知道 isLength 函数是用来检查传入值 value 是否为有效长度。这里的 MAX_SAFE_INTEGER 就等于 Number.MAX_SAFE_INTEGER 的值,这样写的目的估计是为了效率,能少一步计算。

根据判断逻辑,我们知道所谓“有效的长度值”,是指值不大于 Number.MAX_SAFE_INTEGER 的自然数。

比如以下这些值,就不能算是有效的长度值:

isLength(Number.MIN_VALUE)
// => false。负数不是

isLength(Infinity)
// => false。无穷大不是

isLength('3')
// => false。字符串也不是

额外说明

根据 isLength 方法的注释说明:此种验证方式是对规范中定义的抽象算法 ToLength 的宽松实现。下面是规范中的定义:

image.png

相较于规范中的定义,后者是包含类型转换和参数值纠正逻辑的:

  1. 类型转换:首先将参数转为整数(ToInteger(argument)
    2. 参数纠正:负值转为 +0;无穷大转为 253 - 1 等。

而这在 Lodash 中的实现中是没有的,这就是为什么称之为“宽松实现”的原因。

参考链接

(完)