【若川源码共读】arrify转数组

93 阅读3分钟

** arrify是将任意类型的值转为数组的类型的库。**

源码

// arrify函数接收一个参数value
export default function arrify(value) {
  // value为 null 或 undefined 时, 返回一个空数组
	if (value === null || value === undefined) {
		return [];
	}

  // value 本身为数组类型, 返回 value 这个数组
	if (Array.isArray(value)) {
		return value;
	}
  // value 为字符串, 则返回一个包含该字符串的一维数组
	if (typeof value === 'string') {
		return [value];
	}

  // 当 传入的value可迭代对象, 拥有迭代器属性, 返回一个包含该对象所有元素的数组
	if (typeof value[Symbol.iterator] === 'function') {
		return [...value];
	}
  // 不是null/undifined, 也不是数组/字符串/可迭代对象
	return [value];
}

Symbol.iterator 迭代器 在JS中一些内置类型拥有默认的迭代器行为(即内部包含Symbol.iterator属性), 包括 array typedArray string map set 但是 object 并没有这一特性

let arr = [1,2,33,4]
let it = arr[Symbol.iterator]()
console.log(it.next())

image.png

上面的代码定义了一个数组arr,调用了Symbol.iterator方法,这个方法内部还存在next方法。 返回了一个含有value 和done属性的对象

  • value ,迭代器返回的当前值,done为true则为undefined
  • done,是不是已经到了结尾,是一个布尔值,如果为true则意味着迭代结束

image.png

对象中没有Symbol.iterator这个属性,所以对象无法用for...of...

var obj = {name: 'hello'}
var ele = obj[Symbol.iterator]()
console.log(ele)
// => Uncaught TypeError: obj[Symbol.iterator] is not a function

那如何在对象中实现迭代器功能,理解迭代器的结构。

const obj = {
    name: 'hello world',
    age: 18,
    child: ['xiaoqing', 'xiaowen'],
    // 在这里定义iterator属性,这个属性是一个函数
    [Symbol.iterator](){
        let index = 0;
        let _this = this;
        // 这个函数返回一个next函数
        return {
            next: function(){
                if(index < this.child.length){
                    const res = { value: this.child[index], done: false };
                    index++;
                    return res;
                } else {
                    return { value: undefined, done: true };
                }
            }
        }
    }
}

typeof obj[Symbol.iterator] 值为function,说明obj是一个具有迭代器的数据类型。

在对象原型链上添加[Symbol.iterator]属性,使所有对象拥有迭代器属性。

// 给所有对象添加迭代器
Object.prototype[Symbol.iterator] = function(){
    let index = 0;
    return {
        // 箭头函数的this指向当前对象
        next:() => {
        // 获取对象的所有键值
            let keys = Object.keys(this)
            return {
                value: this[keys[index++]],
                done: index > keys.length
            }
        }
    }
}

补充:

测试数据: let value = 'test'

if (typeof value === 'string') { return [value]; }

上面的代码结果加上返回的结果是 ['test'].

如果上面的代码不加的话,会对字符串 test 进行 value[Symbol.iterator] === 'function'判断,用for...of 先将字符串展开,再拼接成数组,结果为['t','e','s','t']

疑问:

为什么将数组类型拿出来判断?

我的思考是,数组类型是具有迭代属性的,如果用value[Symbol.iterator] === 'function'判断的话,再遍历一遍,会浪费性能。

总结

  1. 学习 arrify 源码,将任意类型的数据转为数组类型

  2. 学习 Symbol.iterator 迭代器属性

  3. 了解 xo、tsd、ava,真的只是了解,后期自己再补上笔记

  4. 看完源码后再写笔记,确实比收获要很多很多,理解也更透彻许多。之前自己也试着读源码,效率不高,也很难坚持。

  5. 源码包含的知识点很多,带着疑问学习,进步更大

  6. 笔记也是对自己每一次学习成果的一种验证