Lodash 源码阅读-size
概述
size函数用于获取集合(collection)的大小,适用于多种数据类型,包括数组、字符串、对象、Map 和 Set。它通过不同的方法准确计算各种集合类型的元素数量,为开发者提供统一的 API 接口确定集合大小,而无需关心具体集合类型。
前置学习
依赖函数
isArrayLike: 检查值是否类数组,判断对象是否有 length 属性且是有效的数组长度isString: 检查值是否为字符串类型stringSize: 计算字符串的实际字符数量getTag: 获取值的内部[[Class]]标签,用于精确判断数据类型baseKeys: 获取对象的所有自身可枚举属性键名数组
技术知识
- JavaScript 数据类型判断方法
- 集合类型(Array、String、Object、Map、Set)的大小计算方式
Object.prototype.toString.call()方法和[object Type]标记- Symbol.toStringTag 及 ES6 引入的内置对象类型识别机制
源码实现
function size(collection) {
if (collection == null) {
return 0;
}
if (isArrayLike(collection)) {
return isString(collection) ? stringSize(collection) : collection.length;
}
var tag = getTag(collection);
if (tag == mapTag || tag == setTag) {
return collection.size;
}
return baseKeys(collection).length;
}
实现思路
- 首先处理空值情况,若集合为 null 或 undefined 则直接返回 0
- 判断集合是否为类数组,若是则进一步区分字符串和普通数组,采用不同方式获取长度
- 对于非类数组集合,通过 getTag 获取其内部标签类型
- 针对 Map 和 Set 类型,直接使用其内置的 size 属性
- 对于普通对象,则通过 baseKeys 获取所有键名然后计算数组长度
源码解析
1. 空值处理
if (collection == null) {
return 0;
}
这段代码使用宽松相等操作符==检查collection是否为null或undefined。在 JavaScript 中,只有这两个值与null进行宽松比较时会返回true。当集合为空时,函数直接返回 0 作为集合大小,避免后续操作出现错误。
例如:
_.size(null); // => 0
_.size(undefined); // => 0
2. 类数组和字符串处理
if (isArrayLike(collection)) {
return isString(collection) ? stringSize(collection) : collection.length;
}
当集合是类数组对象时,函数进一步判断是否为字符串类型:
- 若为字符串,则调用
stringSize函数计算字符串的实际字符数 - 若为其他类数组对象(如数组、arguments 等),则直接返回其
length属性值
对字符串使用特殊处理是因为 JavaScript 中字符串的length属性只计算代码单元(code units)数量,而不是实际 Unicode 字符数量,尤其对于包含 emoji 或 surrogate pairs 的字符串,stringSize能更准确地计算。
例如:
_.size([1, 2, 3]); // => 3
_.size("abc"); // => 3
3. Map、Set 和普通对象处理
var tag = getTag(collection);
if (tag == mapTag || tag == setTag) {
return collection.size;
}
return baseKeys(collection).length;
对于非类数组的集合,函数首先通过getTag获取其内部[[Class]]标签:
- 若集合是 Map 或 Set 类型,则直接返回其内置的
size属性值 - 对于其他对象类型(如普通对象),则调用
baseKeys获取所有自身可枚举属性的键名数组,然后返回该数组的长度
getTag函数通过Object.prototype.toString.call()实现更准确的类型检测,能识别 ES6 引入的新集合类型。
例如:
_.size(
new Map([
["a", 1],
["b", 2],
])
); // => 2
_.size(new Set([1, 2, 3, 3])); // => 3 (Set自动去重)
_.size({ a: 1, b: 2 }); // => 2
总结
- 使用宽松相等(
==)检查 null/undefined,减少代码冗余 - 采用类型检测分派策略,对不同集合类型使用最优的大小计算方法
- 特殊处理字符串,确保 Unicode 字符计数准确性
- 利用 ES6 集合类型的内置属性,避免不必要的转换操作
- 结合
Object.keys和length实现对普通对象大小的优雅计算