本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。
本期要阅读的源码:arrify。
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];
}
虽然代码很很少,但是涉及了四个知识点,首先是全等运算符,然后是typeof,再则数组判断,最后是可迭代对象。
一、全等
提起全等,马上就能想到相等。相等(==)和全等(===)这两个运算符都用来判断两个操作数是否相等,但它们之间有一个最大的区别,就是“==”允许在比较中进行类型转换,而“===”禁止类型转换。各种类型之间进行相等比较时,执行的类型转换是不同的,在ECMAScript 5中已经定义了具体的转换规则。下表将规则以表格的形式展示,第一列表示左边操作数“X”的类型,第一行表示右边操作数“Y”的类型。在表格中,Number()函数用简写N()表示,ToPrimitive()函数用简写TP()表示。
当判断对象和非对象是否相等时,会先让对象执行ToPrimitive抽象操作,再进行相等判断。ToPrimitive抽象操作就是先检查是否有valueOf()方法,如果有并且返回基本类型的值,就用它的返回值;如果没有就改用toString()方法,再用它的返回值。
| X == Y | 数字 | 字符串 | 布尔值 | null | undefined | 对象 |
|---|---|---|---|---|---|---|
| 数字 | N(Y) | N(Y) | 不等 | 不等 | TP(Y) | |
| 字符串 | N(X) | N(Y) | 不等 | 不等 | TP(Y) | |
| 布尔值 | N(X) | N(X) | N(X) | N(X) | N(X) | |
| null | 不等 | 不等 | N(Y) | 相等 | 相等 | TP(Y) |
| undefined | 不等 | 不等 | N(Y) | 相等 | 相等 | TP(Y) |
| 对象 | TP(X) | TP(X) | N(Y) | TP(X) | TP(X) |
二、typeof
typeof运算符能检测出6种内置类型和函数,执行完后会返回一个小写字母的类型字符串,检测规则如下所列,在每条规则旁还会给出相应的示例代码。
- 当检测基本类型中的数字、字符串或布尔值时,会分别返回“number”、“string”、“boolean”和“symbol”,下面会检测3种类型的值,包括一些特殊的值。
//返回"number"
typeof 1; //整数
typeof 1.5; //浮点数
typeof Infinity; //无穷大
typeof NaN; //不是数字的数值
typeof Number("1"); //类型转换
//返回"string"
typeof "1"; //字符串字面量
typeof ""; //空字符串
typeof String(1); //类型转换
//返回"boolean"
typeof true; //真值
typeof false; //假值
typeof Boolean(1); //类型转换
//返回“symbol”
typeof Symbol();
- 当检测基本类型中的undefined时,会返回“undefined”。
typeof undefined; //"undefined"
- 当检测基本类型null时,不是返回“null”,而是返回“object”。
typeof null; //"object"
- 当检测函数时,会返回“function”。
function func() {}
typeof func; //"function"
- 当检测除了函数之外的对象时,都会返回“object”,下面对常用的对象执行typeof运算符。
typeof {name:"strick"}; //对象字面量
typeof [1, 2]; //数组字面量
typeof /\s/; //正则表达式字面量
typeof new Date(); //日期对象
typeof new Number("1"); //数字对象
typeof new String(1); //字符串对象
typeof new Boolean(1); //布尔值对象
三、toString()
Array.isArray()用于判断参数是否是数组,此方法在ES6中正式引入,在此之前,可以通过toString()来判断参数是否是数组类型。
typeof运算符是检测数字、字符串、布尔值、函数和undefined的最佳工具,但如果要区分对象中的类型,例如数组、日期等,就无能为力了,得换一种检测工具。在基础对象Object的原型上有个toString()方法,它能返回格式为“[object Type]”的字符串,其中Type是对象的类型,此方法能检测出的对象有Number、Array、Date、RegExp等。有一点需要注意,就是虽然每个对象都继承了toString()方法,但不能直接使用,因为对象有可能重写了此方法,必须使用函数的方法call()或apply()间接调用toString(),下面是toString()的用法。
var toString = Object.prototype.toString;
toString.call({}); //"[object Object]"
传入不同类型的值,其检测规则也会有所不同,具体如下所列,在每条规则旁还会给出相应的示例代码。
- 如果传入的是原始值,那么会先转换为对应的包装对象,再进行检测。
toString.call(1); //"[object Number]"
toString.call("1"); //"[object String]"
toString.call(true); //"[object Boolean]"
- 如果传入null,那么Type对应的值为“Null”。
toString.call(null); //"[object Null]"
- 如果传入undefined,那么Type对应的值为“Undefined”。
toString.call(undefined); //"[object Undefined]"
- 如果传入其它对象,那么Type对应的值为“Number”、“Array”等对象的类型,下面将toString()方法作用于常用的对象。
function func() {}
toString.call(func); //"[object Function]"
toString.call({ name: "strick" }); //"[object Object]"
toString.call([1, 2]); //"[object Array]"
toString.call(/\s/); //"[object RegExp]"
toString.call(new Date()); //"[object Date]"
toString.call(new TypeError()); //"[object Error]"
四、可迭代对象
包含Symbol.iterator属性的对象被称为可迭代对象(Iterable),Symbol.iterator是一个特殊的内置符号,它的值是一个返回迭代器的方法。ES6内置了多种可迭代对象,例如集合(Array、TypedArray、Set和Map)、类数组对象(Arguments和NodeList)、字符串等,它们都含有各自默认的迭代器。下面的示例使用了数组的默认迭代器,通过next()方法依次遍历了它所包含的元素,返回结果与上一个示例类似。
var arr = ["a", "b"],
iterator = arr[Symbol.iterator]();
iterator.next(); //{value: "a", done: false}
iterator.next(); //{value: "b", done: false}
iterator.next(); //{value: undefined, done: true}
可迭代对象的应用场景包括扩展运算符、解构赋值和Array.from()方法等。
参考资料: