arrify 源码,19行核心代码分析以及代码之外的收获【玩转源码】

举报
叶一一 发表于 2023/07/26 12:34:24 2023/07/26
【摘要】 前言 我之所以执着于源码源码,一方面是因为去年借着日更,读完了好几本大部头的JavaScript相关书籍,以及对 MDN 中的许多知识点做了梳理和过滤。另一方面,源码共读第一期活动中,开始关注若川大佬,若川大佬提供了大量的源码阅读的方向。正所谓,背有大树好乘凉。计划集中1~2月的精力,进行一波源码阅读。本篇是对 arrify源码的解读,以及在解读过程中,关于实现代码的新想法和 tsd 类型校...

前言

我之所以执着于源码源码,一方面是因为去年借着日更,读完了好几本大部头的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 中一个罗列了五种:

下面用 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畅想」系列作者,华夏美食、国漫、古风重度爱好者,刑侦、无限流小说初级玩家。
如果看完文章有所收获,欢迎点赞👍 | 收藏⭐️ | 留言📝。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。