【若川源码共读】把一个值转换成数组--arrify

85 阅读2分钟

arrify这个包为开发者提供了一种非常简单快捷的数据转数组方法。

使用演示

arrify('🦄');
//=> ['🦄']

arrify(['🦄']);
//=> ['🦄']

arrify(new Set(['🦄']));
//=> ['🦄']

arrify(null);
//=> []

arrify(undefined);
//=> []

源码

来看一下这个包是怎么样实现这个功能的.

前置知识: ES6中的iterator

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];
}

前三个if判断非常简单,它们的规则是:

  • Null 和 Undefined 返回空数组
  • 数组类型直接返回本身
  • String 类型直接用数组包起来

重点在于最后一个if判断.

函数的参数value传入的值可能是任何的数据类型,value[Symbol.iterator]这句话的意思就是拿到value里面的Symbol.iterator属性。

你可以这样理解,对于普通的对象,对象中的属性键名都是字符串,所以你在调用对象中的属性时,使用的方式是对象名.属性,而Symbol.iterator在数据中存储形式是变量,要想取到这个变量,不能直接通过"."获得,因为这不是一个字符串,你应该使用中括号获得这个属性.

关于迭代器

在JavaScript中一些内置类型拥有默认的迭代器行为(即内部包含Symbol.iterator属性),包括

  • array
  • typedArray
  • string
  • map
  • set

但object并没有这一特性.

当你访问Symbol.iterator这一属性时,你将会获得一个next()函数,如果你对这一知识点不熟悉,请先学习ES6中的iterator.

在这里展示了在对象中实现迭代器功能,你可以从中理解迭代器到底是什么结构.

const banji = {
      name: 'hahaha',
      stu: [
        'xiaoming',
        'xiaohong'
      ],
      // 在这里定义iterator属性,这个属性是一个函数
      [Symbol.iterator]() {
        let index = 0;
        let _this = this;
        //这个函数将会返回一个next函数
        return {
          next: function () {
            if (index < this.stu, length) {
              const res = { value: this.stu[index], done: false };
              index++;
              return res;
            } else {
              return { value: undefined, done: true };
            }
          }
        };
      }
    };

因此,在typeof value[Symbol.iterator]的语句中,如果值为function,就说明value是一个具有迭代器的数据类型.

于是转换数组的方法是使用扩展运算符,把数据中的属性放到数组中.

而对于其它类型的数据,直接用数组包裹起来即可.

我学到了: typeof value[Symbol.iterator] === 'function'可以用来判断一个数据是否具有迭代器