lodash源码乱读-drop后的故事(toNumber)

发到网络上的极可能是这篇,javascript

编程是很抽象,很细致的一件事。java

正如difference方法,有一层baseDifference的抽象。drop方法,也有一个baseXXX的方法(baseSlice)。编程

以base开头的函数,是一个基础的实现。一系列更具体的实现,都再这之上。浏览器

在这些基础或是具体的实现了,都不会缺乏一些帮助函数,在drop中一系列的实现中,就有以下的函数。网络

  • toInteger
  • toFinite
  • toNumber

其实在drop的实现源码里只有toInteger。我将他们两个列出来是由于toInteger基于toFinite,toNumber, toNumber中也有两个帮助函数函数

  • isObject
  • isSymbol

先看toNumber这个函数的源码code

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原型上的一个方法,实际调用,返回的是它的实际值。ip

valueOf返回指定对象的原始值。(primitive value )字符串

link查阅更多

JavaScript的许多内置对象都重写了该函数,以实现更适合自身的功能须要。所以,不一样类型对象的valueOf()方法的返回值和返回值类型都可能不一样

作些尝试

var arr = []
arr.valueOf() == arr 
// => true

var n = 1
n.valueOf() === n
// => true

var f = function(){}
f.valueOf() === f
// => true

var d = new Date()
d.valueOf() === d  
//false

var b = true
b.valueOf() === b
// true

var s = "123"
s.valueOf() === s
// true

var 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来规避这个问题。

相关文章
相关标签/搜索