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來規避這個問題。

相關文章
相關標籤/搜索