发到网络上的很可能是这篇,
编程是很抽象,很细致的一件事。
正如difference
方法,有一层baseDifference
的抽象。drop方法,也有一个baseXXX
的方法(baseSlice)。
以base开头的函数,是一个基础的实现。一系列更具体的实现,都再这之上。
在这些基础或是具体的实现了,都不会缺少一些帮助函数,在drop中一系列的实现中,就有如下的函数。
- toInteger
- toFinite
- toNumber
其实在drop
的实现源码里只有toInteger
。我将他们两个列出来是因为toInteger
基于toFinite,toNumber
, toNumber
中也有两个帮助函数
- isObject
- isSymbol
先看toNumber
这个函数的源码
function toNumber(value) { if (typeof value == 'number') { return value; } if (isSymbol(value)) { return NAN; } if (isObject(value)) { var other = typeof value.valueOf == 'function' ? value.valueOf() : value; value = isObject(other) ? (other + '') : other; } if (typeof value != 'string') { return value === 0 ? value : +value; } value = value.replace(reTrim, ''); var isBinary = reIsBinary.test(value); return (isBinary || reIsOctal.test(value)) ? freeParseInt(value.slice(2), isBinary ? 2 : 8) : (reIsBadHex.test(value) ? NAN : +value);}
我第一个疑惑的地方是这里
var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
对valueOf
的理解只是知道它是Object原型上的一个方法,实际调用,返回的是它的实际值。
valueOf
返回指定对象的原始值。(primitive value )
JavaScript的许多内置对象都重写了该函数,以实现更适合自身的功能需要。因此,不同类型对象的valueOf()方法的返回值和返回值类型均可能不同
做些尝试
var arr = []arr.valueOf() == arr // => truevar n = 1n.valueOf() === n// => truevar f = function(){}f.valueOf() === f// => truevar d = new Date()d.valueOf() === d //falsevar b = trueb.valueOf() === b// truevar s = "123"s.valueOf() === s// truevar o = {a:1}o.valueOf() === o// true
只有Date类型有点奇怪,Date类型的valueOf返回的是存储的时间是从 1970 年 1 月 1 日午夜开始计的毫秒数 UTC。
之后是这里
value = isObject(other) ? (other + '') : other;
isObject
function isObject(value) { var type = typeof value; return value != null && (type == 'object' || type == 'function');}
需要排除null
的影响。
value在这在做一个类型转换,转换撑字符串。
value = value.replace(reTrim, ''); //这里删除了 字符串的前后的空白符var isBinary = reIsBinary.test(value); // 二进制return (isBinary || reIsOctal.test(value)) //二进制或者八进制? freeParseInt(value.slice(2), isBinary ? 2 : 8): (reIsBadHex.test(value) ? NAN : +value);
要看懂上边的代码,要读懂下边的几个正则,
// 十六进制var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;// 二进制var reIsBinary = /^0b[01]+$/i;// 八进制var reIsOctal = /^0o[0-7]+$/i;
0x
是十六进制的前缀,在计算机语言中,在数字前加进制前缀,更容易理解,必定同样的数字,同样是8进制或者二进制表示的真实数字天壤之别。
0b
是二进制的前缀。
0o
是八进制的前缀。
0d
是十进制的前缀。
再回到之前代码的逻辑
- 处理掉空白符
- 保存二进制判断,因为有两处会用到
- 二进制或者八进制判断
- true => 截掉前缀并调用对应的parseInt返回结果
- 如果是16进制,就返回NAN,否则返回value
+value
貌似没见过这种写法,查阅相关资料,(链接如下)
一元加号运算符,位于它要操作的数的前方,计算其操作数的数值,如果操作的value不是数字,也会尝试将它转换成数字。
一元正号是转换其他对象到数值的最快方法,也是最推荐的做法,因为它不会对数值执行任何多余操作。它可以将字符串转换成整数和浮点数形式,也可以转换非字符串值 true,false 和 null。小数和十六进制格式字符串也可以转换成数值。负数形式字符串也可以转换成数值(对于十六进制不适用)。如果它不能解析一个值,则计算结果为 NaN.
+3 // 3+"3" // 3+true // 1+false // 0+null // 0+function(val){ return val;} //NaN
详细查看parseInt
方法,在没有指定基数
(传入的第二个参数,没有指定就是没传第二个参数),
- 如果字符串 string 以"0x"或者"0X"开头, 则基数是16 (16进制).
- 如果字符串 string 以"0"开头,基数是8(八进制)或者10(十进制),那么具体是哪个基数由实现环境决定。ECMAScript 5 规定使用10,但是并不是所有的浏览器都遵循这个规定。因此,永远都要明确给出radix参数的值。
- 如果字符串 string 以其它任何值开头,则基数是10 (十进制)。
freeParseInt(value.slice(2), isBinary ? 2 : 8)
这里其实就是根据输入的字符串是否是某进制开头做的判断,如果是8进制或2进制,就移除"字符串开头的0x",然后设置相应的进制。。。
如此麻烦的去做也是为了做兼容。
实际上,我们使用parseInt的时候,应该明确的传入第二个参数。实际上,还是应该尽量使用_.toNumber
来规避这个问题。