學習lodash源碼之Math部分

最近在看lodash的源碼, 因此順便記錄一些文檔看成學習筆記, 寫的很差還請見諒哈git

github倉庫 , 第一篇先從比較見到的Math部分開始!github

Math 部分

_.add

  • 使用
參數
  augend (number): 相加的第一個數。
  addend (number): 相加的第二個數。
  返回
  (number): 返回總和。
複製代碼
  • _.add 函數源碼以下, 能夠很清楚的看到這個函數是由 createMathOperation 這個函數生成的, 第一個參數是一個函數, 這個函數有兩個參數, 根據參數名稱, 能夠猜到是兩個傳入的要相加的數, 而後返回這兩個數的和, 第二個參數是一個寫死的 0
const add = createMathOperation((augend, addend) => augend + addend, 0)
複製代碼

createMathOperation

function createMathOperation(operator, defaultValue) {
    return (value, other) => {
      // 缺失參數的狀況
      if (value === undefined && other === undefined) {
        return defaultValue
      }
      if (value !== undefined && other === undefined) {
        return value
      }
      if (other !== undefined && value === undefined) {
        return other
      }
      if (typeof value === 'string' || typeof other === 'string') {
        value = baseToString(value)
        other = baseToString(other)
      }
      else {
        value = baseToNumber(value)
        other = baseToNumber(other)
      }
      return operator(value, other)
    }
  }

複製代碼
  • 他直接 return 了一個函數, 這個 return 的函數接收的兩個參數就是傳入的兩個 加數 , 首先他對參數的數量是否合法進行了分類, 若是兩個參數都沒有, 直接返回 deafaultValue 也就是 0 , 若是隻有一個參數, 那麼就直接返回了另外一個參數, 若是參數的數量是合法的, 他會先判斷參數是不是字符串, 只要有一個是, 會經過 baseToString 將其所有轉成字符串, 若是參數中並無字符串, 經過 baseToNumber 確保轉成數值, 最後執行第一個傳入的 operator , 最後返回的是傳入的 operator(value, other) , 這下咱們明白了, operator 是規定運算的種類, 由於當前是 _.add 因此傳入的 operator(augend, addend) => augend + addend , 一樣的對四則運算均可以進行擴展, 上述用到了兩個轉換類型的函數 baseToStringbaseToNumber , 咱們來看一下他們都作了什麼

baseToNumber

  • 這個函數很簡單, 先使用 typeof 判斷是否是一個 number 類型, 若是是, 不須要轉
  • 以後判斷是否是 symbol 類型, 若是是 symbol 類型不能轉成 number , 直接返回 NaN
  • 若是面兩個判斷都沒中, 直接返回 +value
const NAN = 0 / 0
  function baseToNumber(value) {
    if (typeof value === 'number') {
      return value
    }
    if (isSymbol(value)) {
      return NAN
    }
    return +value
  }
複製代碼

baseToString

  • 一樣的, 也是先使用 typeof 判斷是否是一個 string , 若是是不須要轉
  • 以後判斷是否是一個數組, 若是是數組, 就遞歸數組每一項執行
  • 若是是一個 symbol 類型, 經過 Symbol.prototype.toString.call(value) 轉成字符串
  • 若是以上都沒中, 直接使用模板字符串進行轉, 這種方法的好處是, 若是是一個基本類型好比, 一個數字 1 , 使用 ${1} 以後會轉成字符串, 而若是是一個對象類型, 和使用 Object.prototype.call({}) 同樣, 返回 [Object object] , 也就是說對於基本類型好比 數字 , 進行正常轉換, 不是基本類型轉成 [Object xxx] 這種形式
  • 最後的最後, 會對 是不是 -0 進行一個判斷, 不然不管 -0 仍是 +0 都會輸出 0 , 這裏先是對已經轉換了的結果 result 進行一個無關類型的 == 比較, 看他是否 0 , 若是是 0 再看看是否是 -0 , 具體細節源碼中展示的已經比較清楚了..
const symbolToString = Symbol.prototype.toString
  const INFINITY = 1 / 0
  function baseToString(value) {
    if (typeof value === 'string') {
      return value
    }
    if (Array.isArray(value)) {
      return `${value.map(baseToString)}` 
    }
    if (isSymbol(value)) {
      return symbolToString ? symbolToString.call(value) : ''
    }
    const result = `${value}` 
    return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result
  }
複製代碼

總結一下, 經過 createMathOperation(operator, defaultValue) 傳入一個運算函數, 返回一個新的 _.add 函數, 而且經過 baseToNumberbaseToString 進行了類型轉化, 一樣的另外的一些運算也能夠按照相似來實現ide

_.ceil

  • 使用: 向上取整, 而且能夠傳入想要保留的精度
參數
number (number): 要向上舍入的值。
[precision=0] (number): 向上舍入的的精度。
返回
(number): 返回向上舍入的值。
複製代碼
  • 這個方法一樣也是一個函數生成的,
const ceil = createRound('ceil')
複製代碼

createRound

  • 先經過傳入相應的字符串, 獲取原生 Math 對象的向上取整函數, 並保留到 func 變量
  • return 一個新的函數, 第一個參數是將要取整的數, 第二個參數是精度
    • 判斷是否有 precision 參數, 若是沒有初始化成 0 , 即不保留精度
    • 若是有傳入, 進行一個位數的安全限制, 即精度的範圍
    • 下面的操做其實是利用 科學記數法 先轉成一個大數, 而後對其使用 func 取整, 再用用 科學記數法 轉成該精度的小數
      • 舉個例子 例如咱們如今要轉化的數是 6.004 , 要求精度是 2
      • 先轉成 6.004 e 2 => 600.4
      • 對其取整 func(601)
      • 轉回小數 601 e -2 => 6.01
function createRound(methodName) {
    const func = Math[methodName]
    return (number, precision) => {
      precision = precision == null ? 0 : (precision >= 0 ? Math.min(precision, 292) : Math.max(precision, -292))
      if (precision) {
        let pair = `${number}e` .split('e')
        const value = func( `${pair[0]}e${+pair[1] + precision}` )
        pair = `${value}e` .split('e')
        return + `${pair[0]}e${+pair[1] - precision}` 
      }
      return func(number)
    }
  }
複製代碼

類似的, 能夠實現 向下取整 , 四捨五入取整 , 只要在最開始獲取 Math 的原生方法不同便可函數

_.meanBy

  • 使用: 接受 iteratee 來調用 array中的每個元素,來生成其值排序的標準。 iteratee 會調用1個參數: (value)
參數
array (Array): 要迭代的數組。
[iteratee=_.identity] (Function): 調用每一個元素的迭代函數。
返回
(number): 返回平均值。
複製代碼
  • 先判斷數組是否有元素, 若是有調用 baseSum 算出總和再除一下
const NAN = 0 / 0
function meanBy(array, iteratee) {
  const length = array == null ? 0 : array.length
  return length ? (baseSum(array, iteratee) / length) : NAN
}
複製代碼

baseSum

  • for of 遍歷數組, 對每一項執行傳入的 iteratee , 而後累加
function baseSum(array, iteratee) {
  let result

  for (const value of array) {
    const current = iteratee(value)
    if (current !== undefined) {
      result = result === undefined ? current : (result + current)
    }
  }
  return result
}
複製代碼
相關文章
相關標籤/搜索