【若川视野 x源码共读】第33期 | arrify 转数组 Convert a value to an array

简介: mac: 在github代码仓库页面通过快捷键 command + k,再在文本框中输入 ">",即可查看到在线打开的命令行

image.png


  • 算是头一次参加若川的活动,唯一的期望就是打开自己学习源码的思路、以及打开自己的视野,后续进行不断的优化和学习


  • 真正的热爱学习,多参与、多查阅文档、多思考、形成自己的思维思想


  • 如果或许对于你有一点点启发,那么也算是另外的一份收获


  • 最后希望自己能坚持下去


  • 本文阅读思路提示


  • 1、本期要学习源码仓库地址
  • 2、在线查看源码或者本地克隆代码
  • 3、查看package.json文件
  • 4、从入口文件的入口函数开始
  • 5、查看typescript声明文件
  • 6、查看测试用例文件
  • 7、总结


1、本期要学习的源码仓库地址



2、在线查看源码或者本地克隆代码


  • 1、在线查看源码


  • mac:  在github代码仓库页面通过快捷键 command + k,再在文本框中输入 ">",即可查看到在线打开的命令行
  • window:同mac,只是快捷键改为ctrl + k
  • 通用方法: 【<>Code】 按钮点击即可查看到Codespaces对应的Tab分组


  • 2、本地克隆代码


  • 直接在本地文件夹,克隆代码


git clone git@github.com:sindresorhus/arrify.git


3、查看package.json文件


  • dependencies和devDependencies依赖查看


"ava": "^3.15.0",
"tsd": "^0.14.0",
"xo": "^0.39.1"


  • 突然发现,这三个依赖都没见过,这里推荐一个小工具npm.devtool.tech, 这是山月大佬的自制小工具,感觉非常棒,安利一波。跳转网页后,直接输入npm库名即可查看,包的相关信息。


  • ava: 19.8k star,通过查看readme.md可以发现,原来是一个单元测试的类库,还有中文翻译版本,具体翻译课查看 github.com/avajs/ava-d… ,它最大的优势应该就是可以并行的执行测试用例,这对于IO繁重的测试就特别的友好了。


  • tsd: 1.4k star,也是star比较高的库,看似其貌不扬,但对于我个人来说,貌似又发现了新用法(以前从来没见过,涨姿势了)。检查typescript类型声明定义,让你通过xxx.test-d.ts后缀名,去为.xxx.d.ts类型声明去做测试(当然前提是你要是typescript)。


  • xo: 6.6k star,xo基于ESLint。这个项目最初只是一个可共享的ESLint配置,但它很快就发展了.我想要更简单的东西.只是输入xo就完成.没有争论.没有配置.我也有一些令人兴奋的未来计划.但是,在使用ESLint时,您仍然可以获得大部分ESLint可共享配置,来自xo优势.


  • scripts 脚本


"test": "xo && ava && tsd"
// &&相当于串行(上一个执行完毕后才会继续执行下一个)、&相当于并行
- xo: 对代码进行检查
- ava: 执行根目录的test.js文件中的测试用例
- tsd: 通过index.test-d.ts文件测试index.d.ts定义    


4、从入口文件的入口函数开始


  • main : 定义了 npm 包的入口文件,browser 环境和 node 环境均可使用


  • module : 定义 npm 包的 ESM 规范的入口文件,browser 环境和 node 环境均可使用


  • exports: exports 的优先级比 mainmodule 高,也就是说,使用方只要匹配上exports的路径就不会使用 mainmodule 的路径。


终于找到入口文件和入口函数index.js


export default function arrify(value) {
    if (value === null || value === undefined) {
        return [];
    }
    if (Array.isArray(value)) {
        return value;
    }
    if (typeof value === 'string') {
        return [value];
    }
    if (typeof value[Symbol.iterator] === 'function') {
        return [...value];
    }
    return [value];
}


  • 1、判断 null 或者undefined,则返回空的数组


  • 2、判断传入的是一个数组的话,则返回它本身


  • 3、判断传入的类型为string字符串的话,则返回一个数组长度为1的数组,第一个元素就为传入的字符串值


  • 4、应该是本文要讲解的重点


对于两个单词分别的理解可以查看ES6文档,阮一峰老大讲解的应该非常清楚了,这里我就不进行意义赘述





那么我在这里要强调一下什么呢:


  • ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性,或者说,一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”(iterable)。Symbol.iterator属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。执行这个函数,就会返回一个遍历器。至于属性名Symbol.iterator,它是一个表达式,返回Symbol对象的iterator属性,这是一个预定义好的、类型为 Symbol 的特殊值。(引用自ES6)


  • 原生具备 Iterator 接口的数据结构如下。


  • Array


  • Map


  • Set


  • String


  • TypedArray


  • 函数的 arguments 对象


  • NodeList 对象


通过上面的描述你或许还没能理解,我们通过代码简单的来学习一下


let iteratorArray = new Set()
iteratorArray.add(1)
iteratorArray.add('2')
iteratorArray.add(['11', 22])
console.log(iteratorArray) /// Set(3) {1, '2', Array(2)}                
console.log(typeof iteratorArray )  /// object


我们对下面的obj对象封装一个简单的遍历器


let obj = {
   a: 'aa',
   b: 11,
   c: 'hellowrold'
 }
 obj = {
   ...obj,
   *[Symbol.iterator]() {
     console.log(this, 'this-item')
     yield { a: 'aa' }
     yield { b: 11 }
     yield { c: 'hellowrold' }
   }
 }
 for (const item of obj) {
   console.log(item, 'item')
 }
 // {a: 'aa'} 'item'
 // {b: 11} 'item'
 // {c: 'hellowrold'} 'item'
 // 那么obj 如果调用 arrify,对于其他的Object不生效
 // 可以统一在Object.prototype[Symbol.iterator] 上实现
 console.log(arrify(obj))
 //(3) [{…}, {…}, {…}]0: {a: 'aa'}1: {b: 11}2: {c: 'hellowrold'}length: 3[[Prototype]]: Array(0)


再回到源码


if (typeof value[Symbol.iterator] === 'function') {
    return [...value];
}


现在再来看这个判断条件,其实就是判断value本身是否具有 Symbol.iterator属性,并且类型为function;也可以说成value是否是可遍历的,是否拥有接口 Iterator;还可以说成是否属于哪几种数据类型: Array、Map、Set、String、TypedArray、函数的arguments对象、NodeList对象,其中Array和String已经单独判断就可以不考虑了。


  • 5、其他类型默认返回跟上述第三条一致


5、查看typescript声明文件


typescript类型声明,源代码连接 github.com/sindresorhu…


export default function arrify<ValueType>(
        value: ValueType
    ): ValueType extends (null | undefined)
        ? [] // eslint-disable-line  @typescript-eslint/ban-types
        : ValueType extends string
            ? [string]
            : ValueType extends readonly unknown[]
                ? ValueType
                : ValueType extends Iterable<infer T>
                    ? T[]
                    : [ValueType];
  • 条件类型判断: 关于这个类型声明中主要的一个关键字为extends,然后使用了(?:三目运算)。这里extends主要是作为条件判断的一个用法: ValueType extends (null | undefined) 前者ValueType 能够赋值给后面的类型,则返回 true,否则返回false。


  • 继承: extends在 class中普通的用法就是继承


class Animal {
        name: string,
        constructor(name: string) {
            this.name = name
        }
        do(): void {
            console.log('动作')
        }
    }
    class Dog extends Animal {
        constructor(name:string){
            super(name)
        }
        do(): void {
            console.log('汪汪汪')
        }  
    }
    class Cat extends Animal {
        constructor(name:string){
            super(name)
        }
        do(): void {
            console.log('喵喵喵')
        }  
    }


6、查看测试用例文件


import test from 'ava';
import arrify from './index.js';
test('main', t => {
  t.deepEqual(arrify('foo'), ['foo']);  // 判断字符串
  t.deepEqual(arrify(new Map([[1, 2], ['a', 'b']])), [[1, 2], ['a', 'b']]);  //Map对象
  t.deepEqual(arrify(new Set([1, 2])), [1, 2]);  //Set对象
  t.deepEqual(arrify(null), []);   // null
  t.deepEqual(arrify(undefined), []);  // undefined
  const fooArray = ['foo'];
  t.is(arrify(fooArray), fooArray);  //正常的数组
});


测试用例本身并没有什么花期呼哨的边界值


7、总结


别看真正封装的代码只有不到20行,但从整个npm库来说,这个小项目包括了非常多的知识点,当然这其中还有很多我没有发现的,或者是我忽略掉的知识点。但从我的知识盲区来说已经学到了非常多的知识。

目录
相关文章
|
1月前
|
人工智能 前端开发 JavaScript
拿下奇怪的前端报错(一):报错信息是一个看不懂的数字数组Buffer(475) [Uint8Array],让AI大模型帮忙解析
本文介绍了前端开发中遇到的奇怪报错问题,特别是当错误信息不明确时的处理方法。作者分享了自己通过还原代码、试错等方式解决问题的经验,并以一个Vue3+TypeScript项目的构建失败为例,详细解析了如何从错误信息中定位问题,最终通过解读错误信息中的ASCII码找到了具体的错误文件。文章强调了基础知识的重要性,并鼓励读者遇到类似问题时不要慌张,耐心分析。
|
1月前
|
存储 Java
Java“(array) <X> Not Initialized” (数组未初始化)错误解决
在Java中,遇到“(array) &lt;X&gt; Not Initialized”(数组未初始化)错误时,表示数组变量已被声明但尚未初始化。解决方法是在使用数组之前,通过指定数组的大小和类型来初始化数组,例如:`int[] arr = new int[5];` 或 `String[] strArr = new String[10];`。
|
1月前
|
存储 JavaScript 前端开发
JavaScript Array(数组) 对象
JavaScript Array(数组) 对象
27 3
|
1月前
|
数据采集 JavaScript 前端开发
JavaScript中通过array.filter()实现数组的数据筛选、数据清洗和链式调用,JS中数组过滤器的使用详解(附实际应用代码)
JavaScript中通过array.filter()实现数组的数据筛选、数据清洗和链式调用,JS中数组过滤器的使用详解(附实际应用代码)
|
2月前
|
Go
Golang语言之数组(array)快速入门篇
这篇文章是关于Go语言中数组的详细教程,包括数组的定义、遍历、注意事项、多维数组的使用以及相关练习题。
33 5
|
3月前
|
Python
PyCharm View as Array 查看数组
PyCharm View as Array 查看数组
85 1
|
4月前
|
索引
|
4月前
|
JavaScript API 索引
JS【详解】Set 集合 (含 Set 集合和 Array 数组的区别,Set 的 API,Set 与 Array 的性能对比,Set 的应用场景)
JS【详解】Set 集合 (含 Set 集合和 Array 数组的区别,Set 的 API,Set 与 Array 的性能对比,Set 的应用场景)
66 0
|
4月前
|
前端开发
let array = [{id:‘001‘,name:‘小新‘,age:5},{ id:‘002‘,name:‘小葵‘]这样数据如何遍历,拿到其中一个值,数组中装对象如何获取其中一个固定的值
let array = [{id:‘001‘,name:‘小新‘,age:5},{ id:‘002‘,name:‘小葵‘]这样数据如何遍历,拿到其中一个值,数组中装对象如何获取其中一个固定的值
|
4月前
|
存储
数据存储之数组的特点,长度固定,适应变化需求,集合类特点是空间可变,ArrayList泛型,ArrayList<String> array = new ArrayList<String>()
数据存储之数组的特点,长度固定,适应变化需求,集合类特点是空间可变,ArrayList泛型,ArrayList<String> array = new ArrayList<String>()