- 开发工具
【免费下载链接】collect.js
💎 Convenient and dependency free wrapper for working with arrays and objects
导读
本文围绕 collect.js 的**严格比较(Strictness and comparisons)**设计展开:collect.js 中所有涉及比较的方法默认一律使用 JavaScript 严格相等(===),因此不提供containsStrict、uniqueStrict、whereStrict等 Laravel 风格的“Strict 变体方法”。文章将结合仓库源码与测试用例,逐一剖析contains、unique、where、whereIn、duplicates、diff等方法的底层比较实现,并给出与 Laravel Collections 的差异对照与实战注意事项,帮助你写出行为可预期、不踩类型隐式转换陷阱的集合操作代码。
一、核心设计:所有比较默认使用严格相等
collect.js 官方文档(bundler/strictness_and_comparisons.md)开宗明义地声明:
All comparisons in
collect.jsare done using strict equality. Using loose equality comparisons are generally frowned upon in JavaScript.
翻译过来即:collect.js 中的所有比较均使用严格相等;宽松相等(loose equality,即==)在 JavaScript 中普遍不推荐使用。这条约定是 collect.js 整个 API 的行为基石,直接影响contains、unique、where等一大批方法的判定结果。
从实现角度看,这一约定贯穿于 src/methods 目录下的各个方法文件,主要通过两种手段落实:
===/!==运算符:直接比较两个值是否“同类型且同值”;Array.prototype.indexOf:indexOf内部使用严格相等(SameValueZero 语义,与===等价),因此天然满足严格比较要求。
下面的小节会逐个方法给出源码证据与测试佐证。
二、严格比较的落地:核心方法源码剖析
2.1contains():查找即===
contains.js 是理解 collect.js 严格比较的最佳入口。该方法支持三种调用形态:
module.exports = function contains(key, value) { if (value !== undefined) { if (Array.isArray(this.items)) { return this.items .filter(items => items[key] !== undefined && items[key] === value) .length > 0; } return this.items[key] !== undefined && this.items[key] === value; } // ...省略函数回调、indexOf 分支 };关键点在于:
- key/value 形态:比较时使用
items[key] === value,例如collect({ number: 8 }).contains('number', '8')返回false,因为8 === '8'不成立; - 单值形态(数组):使用
this.items.indexOf(key) !== -1,同样不进行类型转换; - 单值形态(对象):把对象的值与键都展开后走
indexOf查找,保证既可按值查、也可按键查。
对应测试 contains_test.js 明确断言了类型敏感性:
const contains2 = collection.contains('number', '8'); expect(contains2).to.eql(false); // 8 与 '8' 类型不同,严格比较不通过 const contains3 = collection.contains('number', 28); expect(contains3).to.eql(false); // 值不同,同样不通过2.2unique():3与'3'是两个不同的值
unique.js 在无参数时通过indexOf去重,有参数(键名或回调)时维护一个usedKeys数组并用indexOf判断是否重复:
collection = this.items .filter((element, index, self) => self.indexOf(element) === index);测试 unique_test.js 给出了最直观的严格比较证据:
const collection2 = collect([1, 1, 1, 2, 3, 3, '3']); expect(collection2.unique().all()).to.eql([1, 2, 3, '3']);3与'3'被保留为两个独立元素——这正是严格相等去重的结果;若使用宽松相等,'3'会被当作与3重复而丢弃。
2.3duplicates():对象值先序列化再严格比对
duplicates.js 查找重复值时,对数组/对象类型的元素先JSON.stringify序列化,再用indexOf判断是否出现过:
const stringifiedValue = (value) => { if (Array.isArray(value) || typeof value === 'object') { return JSON.stringify(value); } return value; };普通标量(数字、字符串等)直接以原始值参与indexOf严格比较;由于duplicates()本身就严格,Laravel 中的duplicatesStrict变体在这里没有存在意义。
2.4where():默认===,宽松运算符需显式声明
where.js 是少数允许使用宽松运算符的方法,但它默认就是严格模式:当只传两个参数(省略运算符)时,自动把运算符补成'===':
if (value === undefined) { comparisonValue = operator; comparisonOperator = '==='; }只有显式传入==、!=、<>时才会进入宽松比较分支,例如nestedValue(item, key) === Number(comparisonValue) || nestedValue(item, key) === comparisonValue.toString()这种“尽力转换后再比对”的宽松逻辑。
测试 where_test.js 用商品数据验证了两种模式的差异——数据中既有price: 100也有price: '100':
// 宽松相等 '==':数字 100 与字符串 '100' 都命中 const loosly100 = collection.where('price', '==', 100); // => Chair(100) + Door('100') // 严格不等 '!==':排除 price === 100 的项 const not100 = collection.where('price', '!==', 100); // => Desk(200) + Bookcase(150) + Door('100')可见where的行为基调是严格,宽松只是显式请求时的选项。
2.5whereIn()/whereNotIn():indexOf严格判定
whereIn.js 与 whereNotIn.js 的实现几乎对称:
// whereIn:值出现在候选列表中 const collection = this.items .filter(item => items.indexOf(nestedValue(item, key)) !== -1); // whereNotIn:值不在候选列表中 const collection = this.items .filter(item => items.indexOf(nestedValue(item, key)) === -1);两者都依赖indexOf的严格语义,因此whereIn('price', [100, 200])不会匹配price: '100'的项。测试 whereIn_test.js 验证了这一点:返回结果只包含price严格等于100或200的两条记录,'100'字符串被排除。
2.6diff():集合差集同样严格
diff.js 使用valuesToDiff.indexOf(item) === -1过滤出“原集合中有、给定集合中没有”的值,indexOf再次保证了严格语义。
三、为什么没有*Strict系列方法?
这是本主题最容易让人困惑的一点:Laravel Collections 默认使用“宽松”比较,因此额外提供containsStrict、uniqueStrict、whereStrict、whereInStrict、whereNotInStrict等一组“严格”变体方法,供开发者按需切换。
collect.js 则反其道而行:既然所有方法默认就是严格的,那么“Strict 变体”就完全多余了。文档 bundler/not_implemented.md 用删除线明确列出了一批未实现的方法及替代方案:
| Laravel 方法 | collect.js 替代写法 |
|---|---|
containsStrict | 直接使用contains() |
duplicatesStrict | 直接使用duplicates() |
uniqueStrict | 直接使用unique() |
whereStrict | 直接使用where() |
whereInStrict | 直接使用whereIn() |
whereNotInStrict | 直接使用whereNotIn() |
从 src/index.js 的方法注册表可以看到,Collection.prototype上挂载的是contains、unique、where、whereIn、whereNotIn、duplicates等普通方法,并不存在任何*Strict后缀的方法。也就是说,如果你是从 Laravel 迁移过来的开发者,不需要做任何额外动作:collect.js 的contains()就是 Laravel 的containsStrict(),collect.js 的where()就是 Laravel 的whereStrict(),两者行为对齐。
四、Laravel 对照:从“默认宽松”到“默认严格”
| 维度 | Laravel Collections | collect.js |
|---|---|---|
| 默认比较方式 | 宽松(loose) | 严格(strict) |
| 需要严格比较时 | 调用containsStrict等方法 | 无需额外方法,默认即严格 |
| 需要宽松比较时 | 默认方法即宽松 | where()可显式传==/!=/<>运算符 |
| API 体量 | 方法 + Strict 变体 | 方法本身,无冗余变体 |
这一设计取舍让 collect.js 在保持与 Laravel “几乎一致的 API”的同时,规避了 JavaScript 中==的隐式类型转换陷阱(如0 == ''、1 == true等),使结果更可预期。正如 README 所述,collect.js 提供与 Laravel Collections 几乎相同的 API,strictness_and_comparisons.md 正是两者差异的官方说明。
五、实战注意事项与迁移建议
结合以上源码分析,在实际开发中应特别注意以下几点:
- 数值与字符串不会互相匹配:
collect([1, 2, 3]).contains('2')为false;如需匹配,请先对数据做显式类型转换,而不是依赖宽松相等; where()是唯一“可切换”比较模式的方法:想要宽松匹配时,必须显式写成where('price', '==', 100)或where('price', '!=', 100),省略运算符则自动使用===;- 去重与差集都是严格的:
unique()会把3和'3'视为不同值,diff()也不会把100与'100'当作相同,涉及混合类型数据时需自行清洗; - 从 Laravel 迁移无需改写调用:原来调用
containsStrict、uniqueStrict、whereStrict等方法的代码,直接改用contains()、unique()、where()即可,语义完全一致; - 对象/数组值的特殊处理:
duplicates()对对象和数组元素会先做JSON.stringify再比较,因此两个结构相同但引用不同的对象会被判定为重复,这是序列化比较的预期行为。
六、相关资源
- 官方说明:bundler/strictness_and_comparisons.md、bundler/not_implemented.md
- README 组装机制:bundler/bundle.js
- 源码实现:src/methods/contains.js、src/methods/unique.js、src/methods/where.js、src/methods/whereIn.js、src/methods/whereNotIn.js、src/methods/duplicates.js、src/methods/diff.js
- 测试佐证:test/methods/contains_test.js、test/methods/unique_test.js、test/methods/where_test.js、test/methods/whereIn_test.js
- 开发工具
【免费下载链接】collect.js
💎 Convenient and dependency free wrapper for working with arrays and objects
相关推荐
collect.js 严格比较设计解析:未实现的 `*Strict` 方法与替代方案
collect.js 严格比较设计解析:未实现的 Strict 方法与替代方案 collect.js 是一个无依赖、专注于数组与对象操作便利性的 JavaScr
开发工具嫌订阅费肉疼?5 款免费矢量设计工具够你用到下班
嫌订阅费肉疼?5 款免费矢量设计工具够你用到下班 上周接到一个改 Logo 的活儿,打开软件才发现订阅已过期,画布直接灰掉了。每月几十块的订阅费,对刚接活的自由
文档知识库nginxconfig.io配置差异比较:is_changed.js实现原理
nginxconfig.io配置差异比较:is_changed.js实现原理 引言:配置变更检测的核心挑战 在使用nginxconfig.io(NGINX配置生
开发工具前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考