- 本文参加了由公众号@若川视野 发起的每周源码共读活动, 点击了解详情一起参与。
- 这是源码共读的第33期,链接:arrify转数组
首先看代码
export default function arrify(value) {
//未传参数,或者传递null返回空数组
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];
}
不愧是【源码共读】活动号称第二简单的,除了[Symbol.iterator]之外都是常规操作,我们照常先走测试用例再看[Symbol.iterator]。
其次看依赖
"scripts": {
"test": "xo && ava && tsd"
},
"devDependencies": {
"ava": "^3.15.0",
"tsd": "^0.14.0",
"xo": "^0.39.1"
}
脚本只有一个单元测试脚本,但继发执行了xo ava tsd三个指令,我们分别看看这三个指令干了些什么
xo
一个基于eslint的代码检查工具,0配置
tsd
提升IDE对JavaScript智能感知的能力,可以理解为给IDE看的类型关系
ava
Create your test file
Create a file named test.js in the project root directory:
import test from 'ava';
test('foo', t => {
t.pass();
});
test('bar', async t => {
const bar = Promise.resolve('bar');
t.is(await bar, 'bar');
});
Running your tests
npm test
我们打印看下t有哪些api
ExecutionContext {
pass: [Function] { skip: [Function: skip] },//测试通过。
fail: [Function] { skip: [Function: skip] },//断言失败。
is: [Function] { skip: [Function: skip] },//断言 `value` 是否和 `expected` 相等。
not: [Function] { skip: [Function: skip] },//断言 `value` 是否和 `expected` 不等。
deepEqual: [Function] { skip: [Function: skip] },//断言 `value` 是否和 `expected` 深度相等。
notDeepEqual: [Function] { skip: [Function: skip] },//断言 `value` 是否和 `expected` 深度不等。
like: [Function] { skip: [Function: skip] },
throws: [Function] { skip: [Function: skip] },
throwsAsync: [Function] { skip: [Function: skip] },
notThrows: [Function] { skip: [Function: skip] },
notThrowsAsync: [Function] { skip: [Function: skip] },
snapshot: [Function] { skip: [Function] },
truthy: [Function] { skip: [Function: skip] },断言 `value` 是否是真值。
falsy: [Function] { skip: [Function: skip] },//断言 `value` 是否是假值。
true: [Function] { skip: [Function: skip] },//断言 `value` 是否是 `true`。
false: [Function] { skip: [Function: skip] },//断言 `value` 是否是 `false`。
regex: [Function] { skip: [Function: skip] },//断言 `contents` 匹配 `regex`
notRegex: [Function] { skip: [Function: skip] },//断言 `contents` 不匹配 `regex`。
assert: [Function] { skip: [Function: skip] },
log: [Function],
plan: [Function] { skip: [Function] },
timeout: [Function],
teardown: [Function],
try: [AsyncFunction]
}
所以可以理解测试用例代码意图
import test from 'ava';
import arrify from './index.js';
test('main', t => {
t.deepEqual(arrify('foo'), ['foo']);//判断生成的数组和 ['foo']是否深度相等
t.deepEqual(arrify(new Map([[1, 2], ['a', 'b']])), [[1, 2], ['a', 'b']]);
t.deepEqual(arrify(new Set([1, 2])), [1, 2]);
t.deepEqual(arrify(null), []);
t.deepEqual(arrify(undefined), []);
const fooArray = ['foo'];
t.is(arrify(fooArray), fooArray);//判断fooArray是否地址相同
});
[Symbol.iterator]
说到[Symbol.iterator],我想到一道经典的JS面试题 forin forof的区别,其中forof正是基于迭代器Iterator的,而迭代器最主要依赖的API就是[Symbol.iterator]。
对象无法forof就是因为没有[Symbol.iterator],如果我们手写一个就可以试下对象的forof。
今年年初的时候因为forin学习下对象的枚举属性,因为forof学习了迭代器,都是参照红宝书四。所以下面代码示例也参照红宝书
class Counter {
constructor(limit) {
this.limit = limit;
}
[Symbol.iterator]() {
let count = 1,
limit = this.limit;
return {
next() {
if (count <= limit) {
return { done: false, value: count++ };
} else {
return { done: true, value: undefined };
}
}
};
}
}
let counter = new Counter(3);
for (let i of counter) { console.log(i); }
// 1
// 2
// 3
for (let i of counter) { console.log(i); }
// 1
// 2
// 3
Array Set Map默认都是有迭代器了,如果我们给对象手写迭代器也可以,比如我们把上面示例写入测试用例
import test from 'ava';
import arrify from './index.js';
class Counter {
constructor(limit) {
this.limit = limit;
}
[Symbol.iterator]() {
let count = 1,
limit = this.limit;
return {
next() {
if (count <= limit) {
return { done: false, value: count++ };
} else {
return { done: true, value: undefined };
}
}
};
}
}
let foo=new Counter(3)
console.log(typeof foo)
test('main', t => {
t.deepEqual(arrify(foo ), [1,2,3]);
});
可以看到foo的类型是object,但是成功转成了
[1,2,3],就是因为生成的对象有[Symbol.iterator],而且手写了迭代规则。如果我们想玩玩,那就可以给Object添加原型
Object.prototype[Symbol.iterator] = function() {
let index = 0
let o=this
return {
next()
{
let keys = Object.keys(o)
if(index < keys.length) {
return {
done: false,
value: o[keys[index++]]
}
} else {
return {
done: true,
value: undefined
}
}
}
}
}
只有执行过上述代码,我们连window对象都能forof了
总结
- 开源项目没想象的那么难,但开源项目涉及的知识点没想象的那么少
- 开始学习迭代器的时候不知道有什么用,现在发现在判断参数类型时候还是很有用的
- xo其实挺麻烦的,我在写测试用例的时候都说删掉之后再执行npm run test的,可能我们开发规范不够,后面还需改善
- 红宝书有时间还是要多刷