arrify 源码,19行核心代码分析以及代码之外的收获【玩转源码】
前言
我之所以执着于源码源码,一方面是因为去年借着日更,读完了好几本大部头的JavaScript相关书籍,以及对 MDN 中的许多知识点做了梳理和过滤。另一方面,源码共读第一期活动中,开始关注若川大佬,若川大佬提供了大量的源码阅读的方向。
正所谓,背有大树好乘凉。计划集中1~2月的精力,进行一波源码阅读。
本篇是对 arrify源码的解读,以及在解读过程中,关于实现代码的新想法和 tsd 类型校验工具库的使用。
核心代码
arrify的核心代码很短,共19行。这19行,共包括5类转换方式。
5类转换方式
❶ 值不存在时,转换成空数组
if (value === null || value === undefined) {
return [];
}
❷ 值为 Array 类型时,值不需要额外的转换
if (Array.isArray(value)) {
return value;
}
❸ 值为字符串类型时,返回转成数组类型的最终值
if (typeof value === 'string') {
return [value];
}
❹ 值为拥有迭代器的类型时,使用扩展运算符取出可遍历的值并放到数组中
if (typeof value[Symbol.iterator] === 'function') {
return [...value];
}
❺ 不属于以上四种中的任何一种情况,返回转成数组类型的最终值
return [value];
为什么要加 Symbol.iterator 相关的校验?
一些内置类型拥有默认的迭代器行为,而 Symbol.iterator 则可以为它们定义默认的迭代器。
使用 typeof 进行 Symbol.iterator 方法的类型校验是否是 function,可以区分这类的类型值。
都有哪些内置类型
MDN 中一个罗列了五种:
- Array.prototype[@@iterator]()
- TypedArray.prototype[@@iterator]()
- String.prototype[@@iterator]()
- Map.prototype[@@iterator]()
- Set.prototype[@@iterator]()
下面用 Set 类型举个小例子
const set1 = new Set();
let res = typeof set1[Symbol.iterator] === 'function';
console.log(res);
运行结果
// => true
讨论:转换方式 ❹ 是否可以代替 ❷
既然拥有迭代器行为的内置类型包括 Array类型,那么转换方式 ❹ 不就包含了方式 ❷?
const list1 = [1, 2, 3];
let res = typeof list1[Symbol.iterator] === 'function';
console.log(res);
运行结果
// => true
结论
我将修改之后的代码,用测试用例跑了一下,结果和修改前的一致。所以我个人感觉是可以只保留方式 ❹ 的(需要提前到方式 ❷ 的位置)。
掘友们怎么看?
测试用例
来跑一遍所有的测试用例,看看结果是否和想的一致。
console.log(arrify(null));
console.log(arrify(undefined));
console.log(arrify('1'));
console.log(arrify(['1']));
console.log(arrify(true));
console.log(arrify(1));
console.log(arrify({}));
console.log(arrify([1, 'foo']));
console.log(arrify(new Set(['1', true])));
console.log(arrify(new Set([1, 2])));
console.log(arrify(['1']));
console.log(arrify(['1']).push(''));
console.log(arrify(false ? [1, 2] : null));
console.log(arrify(false ? [1, 2] : undefined));
console.log(arrify(false ? [1, 2] : '1'));
console.log(arrify(false ? [1, 2] : ['1']));
console.log(arrify(false ? [1, 2] : true));
console.log(arrify(false ? [1, 2] : 3));
console.log(arrify(false ? [1, 2] : {}));
console.log(arrify(false ? [1, 2] : [1, 'foo']));
console.log(arrify(false ? [1, 2] : new Set(['1', true])));
console.log(arrify(false ? [1, 2] : false ? true : '1'));
运行结果
[]
[]
[ '1' ]
[ '1' ]
[ true ]
[ 1 ]
[ {} ]
[ 1, 'foo' ]
[ '1', true ]
[ 1, 2 ]
[ '1' ]
2
[]
[]
[ '1' ]
[ '1' ]
[ true ]
[ 3 ]
[ {} ]
[ 1, 'foo' ]
[ '1', true ]
[ '1' ]
tsd | 类型检查工具库
使用 tsd 提供的 expectType 方法,可以帮助检查预期类型与表达式或变量的类型是否一致,语法也比较简单:
expectType<预期类型>(表达式或变量)
所以可以看到源码中的如下代码:
expectType<[]>(arrify(null));
前面的转换方式中 arrify(null) 的结果是 [],所以上面的用例是通过的。
代码中还有一个 expectAssignable 方法,可以帮助检查表达式或变量的类型是否能够赋值给预期类型。
expectAssignable<number[] | [boolean]>(arrify(false ? [1, 2] : true));
expectAssignable<number[]>(arrify(false ? [1, 2] : true));
上面两行代码运行结果是,第一行通过 ,第二行不通过。因为布尔值结果不可以分配给数值类型。
总结
最近源码读的多了,一个学习设计模式之外的收获就是自己尝试的独立思考,比如上面讨论里的另一种实现方式。
「书读百遍其义自见」,发散思维,校验技术的熟练性,也可以帮助突破一些瓶颈。
在这之前,我一直纠结的总是走别人已经走过的路,没有自己的创意。也在这一步一个印记中,消散了许多。
如果对源码感兴趣,但是没什么学习目标和方向的,此处手动@若川,可以跟川哥一起学习源码。
作者:非职业「传道授业解惑」的开发者叶一一
简介:「趣学前端」、「CSS畅想」系列作者,华夏美食、国漫、古风重度爱好者,刑侦、无限流小说初级玩家。
如果看完文章有所收获,欢迎点赞👍 | 收藏⭐️ | 留言📝。
- 点赞
- 收藏
- 关注作者
评论(0)