成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

探究underscore源碼(二)

jeyhan / 2668人閱讀

摘要:源碼如下通過來(lái)判斷到底通過來(lái)區(qū)分對(duì)象以及數(shù)組。傳入回調(diào)函數(shù)的參數(shù)分別為對(duì)象鍵值對(duì)中的值或者數(shù)組中的序號(hào)值對(duì)象鍵值對(duì)中的鍵或者數(shù)組中的相應(yīng)序號(hào)舉個(gè)例子,傳入回調(diào)的參數(shù)依次為如果是數(shù)組,則傳入?yún)?shù)依次為三這幾個(gè)方法都是利用一個(gè)核心函數(shù)。

一、_.each

一開始我并沒有以為_.each這個(gè)方法會(huì)有多大的用處,不就是一個(gè)遍歷嘛~

但當(dāng)我利用自己測(cè)試這個(gè)函數(shù)的時(shí)候,發(fā)現(xiàn)了一件“大事”

underscore的初始化時(shí)怎么做的?你是不是跟我一樣都以為underscore的初始化就是在_這個(gè)對(duì)象上面加上一堆屬性?

Naive!

underscore的真是做法是通過一系列的函數(shù)編程實(shí)現(xiàn)初始化的“自動(dòng)化”

看下面的代碼:

// Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError, isMap, isWeakMap, isSet, isWeakSet.
  _.each(["Arguments", "Function", "String", "Number", "Date", "RegExp", "Error", "Symbol", "Map", "WeakMap", "Set", "WeakSet"], function(name) {
    _["is" + name] = function(obj) {
      return toString.call(obj) === "[object " + name + "]";
    };
  });

underscore通過如上的代碼實(shí)現(xiàn)數(shù)據(jù)類型的判斷

類似的,作者通過調(diào)用_.each方法來(lái)實(shí)現(xiàn)

自己拓展的函數(shù)跟庫(kù)提供函數(shù)的合并(詳見_.mixin

Array方法的自動(dòng)初始化(line1588+line1599

二、_.map

_.map源碼的精華之處在于將Array以及Object合在一起處理。

源碼如下:

_.map = _.collect = function(obj, iteratee, context) {
    debugger
    iteratee = cb(iteratee, context);
    var keys = !isArrayLike(obj) && _.keys(obj),
        length = (keys || obj).length,
        results = Array(length);
    for (var index = 0; index < length; index++) {
      // 通過key來(lái)判斷到底
      var currentKey = keys ? keys[index] : index;
      results[index] = iteratee(obj[currentKey], currentKey, obj);
    }
    return results;
};

通過var keys = !isArrayLike(obj) && _.keys(obj)來(lái)區(qū)分對(duì)象以及數(shù)組。如果為對(duì)象,則keystrue,否則為false。傳入回調(diào)函數(shù)的參數(shù)分別為:

對(duì)象鍵值對(duì)中的值或者數(shù)組中的序號(hào)值

對(duì)象鍵值對(duì)中的鍵或者數(shù)組中的相應(yīng)序號(hào)

舉個(gè)例子,{one:1, two:2, three:3}:
傳入回調(diào)的參數(shù)依次為:

1, one

2, two

3, three

如果是數(shù)組[1, 2, 3],則傳入?yún)?shù)依次為:

1, 0

2, 1

3, 2

三、_.reduce _.foldl _.inject _.reduceRight _.foldr

這幾個(gè)方法都是利用一個(gè)核心函數(shù)createReduce。

createReduce利用了高階函數(shù)的方式進(jìn)行封裝, 通過傳入的參數(shù)來(lái)確定是正序遍歷還是倒敘遍歷。下面來(lái)看源碼:

  // 創(chuàng)建一個(gè)遍歷函數(shù),從左到右或者從右向左
  // param: dir  >0(從左向右遍歷),dir < 0(從右向左遍歷)
  var createReduce = function(dir) {
    // 包裝代碼,在一個(gè)多帶帶的函數(shù)中分配參數(shù)變量,而不是訪問`arguments.length",以避免發(fā)生issue #1991
    var reducer = function(obj, iteratee, memo, initial) {
      var keys = !isArrayLike(obj) && _.keys(obj),
          length = (keys || obj).length,
          index = dir > 0 ? 0 : length - 1;
      if (!initial) {
        // 未初始化memo參數(shù)的情況下,設(shè)置memo值為傳入?yún)?shù)的第一個(gè)值(有可能是倒序的第一個(gè)值也有可能是正序的第一個(gè)值)
        memo = obj[keys ? keys[index] : index];
        index += dir;
      }
      // 利用 for 循環(huán) 迭代用戶傳入的參數(shù)
      for (; index >= 0 && index < length; index += dir) {
        var currentKey = keys ? keys[index] : index;
        memo = iteratee(memo, obj[currentKey], currentKey, obj);
      }
      return memo;
    };

    return function(obj, iteratee, memo, context) {
      var initial = arguments.length >= 3;
      return reducer(obj, optimizeCb(iteratee, context, 4), memo, initial);
    };
  };

看來(lái)這個(gè)主要函數(shù),來(lái)看看這個(gè)函數(shù)的調(diào)用方式:

  _.reduce = _.foldl = _.inject = createReduce(1);

  _.reduceRight = _.foldr = createReduce(-1);

主要是在初始化時(shí),先運(yùn)行createReduce形成一個(gè)閉包,從而可以在運(yùn)行時(shí)可以很好的區(qū)分是從左向右遍歷還是從右向左遍歷。

四、_.findIndex|_.findKey|_.find|_.findLastIndex

_.find基于_.findIndex以及_.findKey,所以我們先來(lái)看后兩者。

先來(lái)看_.findIndex。查看findIndex源碼會(huì)發(fā)現(xiàn),它與findLastIndex原理一直,都是調(diào)用一個(gè)名為createPredicateIndexFinder

// 利用此函數(shù)來(lái)生成findIndex以及findLastIndex
  var createPredicateIndexFinder = function(dir) {
    // 判斷 dir 參數(shù)的正負(fù)來(lái)確認(rèn)是findIndex還是findLastIndex
    return function(array, predicate, context) {
      predicate = cb(predicate, context);
      var length = getLength(array);
      var index = dir > 0 ? 0 : length - 1;
      // 遍歷來(lái)尋找符合要求的index
      for (; index >= 0 && index < length; index += dir) {
        // 傳入?yún)?shù)給回調(diào)函數(shù)
        if (predicate(array[index], index, array)) return index;
      }
      // 如果未找到符合要求的index則返回 -1
      return -1;
    };
  };

看懂上面的函數(shù),則findIndex以及findLatIndex就淺顯了:

_.findIndex = createPredicateIndexFinder(1);
_.findLastIndex = createPredicateIndexFinder(-1);

看懂findIndex之后,來(lái)了解_.findKey,這個(gè)函數(shù)又牽扯到了另一個(gè)函數(shù)_.keys了(別那樣的表情,函數(shù)式編程就是這樣。。。)

  _.keys = function(obj) {
    // 容錯(cuò)處理,判斷是否為對(duì)象
    if (!_.isObject(obj)) return [];
    // 如果能調(diào)用 ES5 的方法,則調(diào)用 Object.keys 方法
    if (nativeKeys) return nativeKeys(obj);
    var keys = [];
    // 遍歷key值
    for (var key in obj) if (_.has(obj, key)) keys.push(key);
    // Ahem, IE < 9.(IE9以下的處理沒太看懂。。。)
    if (hasEnumBug) collectNonEnumProps(obj, keys);
    return keys;
  };

上面源碼的處理有幾個(gè)點(diǎn)值得我們注意以下:

  _.isObject = function(obj) {
    var type = typeof obj;
    // function 屬于 object,typeof null = "object",需要通過!!obj排除null這種情況
    return type === "function" || type === "object" && !!obj;
  };
  _.has = function(obj, key) {
    // 通過原生的hasOwnProperty進(jìn)行屬性的判斷
    return obj != null && hasOwnProperty.call(obj, key);
  };
  // IE < 9 ,有些key值不會(huì)被遍歷,導(dǎo)致key的遍歷缺失bug
  // hasEnumBug 用來(lái)辨識(shí)是否在IE < 9 的環(huán)境下
  var hasEnumBug = !{toString: null}.propertyIsEnumerable("toString");
  var nonEnumerableProps = ["valueOf", "isPrototypeOf", "toString",
                      "propertyIsEnumerable", "hasOwnProperty", "toLocaleString"];

  var collectNonEnumProps = function(obj, keys) {
    var nonEnumIdx = nonEnumerableProps.length;
    var constructor = obj.constructor;
    var proto = _.isFunction(constructor) && constructor.prototype || ObjProto;

    // Constructor is a special case.
    var prop = "constructor";
    if (_.has(obj, prop) && !_.contains(keys, prop)) keys.push(prop);

    while (nonEnumIdx--) {
      prop = nonEnumerableProps[nonEnumIdx];
      if (prop in obj && obj[prop] !== proto[prop] && !_.contains(keys, prop)) {
        keys.push(prop);
      }
    }
  };
五、_.filter

上源碼:

  // 返回所有通過測(cè)試的元素
  // 別名為`select`
  _.filter = _.select = function(obj, predicate, context) {
    var results = [];
    // 改變predicate的函數(shù)指向
    predicate = cb(predicate, context);
    // 遍歷每個(gè)值,如果通過測(cè)試則放入要返回的數(shù)組中
    _.each(obj, function(value, index, list) {
      if (predicate(value, index, list)) results.push(value);
    });
    return results;
  };

主要采用了內(nèi)部函數(shù)_.each,這個(gè)函數(shù)沒有什么好說的~

六、_.reject | _.negate

_.reject這個(gè)函數(shù)內(nèi)部使用了_.negate,所以,我們先從_.negate看起.

  _.negate = function(predicate) {
    return function() {
      return !predicate.apply(this, arguments);
    };
  };

這個(gè)函數(shù)的意思就是運(yùn)行傳入進(jìn)來(lái)的cb函數(shù)之后,對(duì)其運(yùn)行的結(jié)果取反。

下面來(lái)看_.reject

  _.reject = function(obj, predicate, context) {
    return _.filter(obj, _.negate(cb(predicate)), context);
  };

不難理解,_.reject就是通過_.filter來(lái)實(shí)現(xiàn)的。

七、_.every | _.some

如果你有興趣查看代碼的話,你會(huì)發(fā)現(xiàn)這兩個(gè)函數(shù)除了判斷部分,其它地方基本一模一樣。也不難理解,兩個(gè)函數(shù)只是邏輯稍微不同而已。

  // 判斷是否所有元素都符合條件
  // 別名為 `all`.
  _.every = _.all = function(obj, predicate, context) {
    predicate = cb(predicate, context);
    var keys = !isArrayLike(obj) && _.keys(obj),
        length = (keys || obj).length;
    for (var index = 0; index < length; index++) {
      var currentKey = keys ? keys[index] : index;
      if (!predicate(obj[currentKey], currentKey, obj)) return false;
    }
    return true;
  };

  // 判斷至少又一個(gè)元素符合條件
  // 別名為 `any`.
  _.some = _.any = function(obj, predicate, context) {
    predicate = cb(predicate, context);
    var keys = !isArrayLike(obj) && _.keys(obj),
        length = (keys || obj).length;
    for (var index = 0; index < length; index++) {
      var currentKey = keys ? keys[index] : index;
      if (predicate(obj[currentKey], currentKey, obj)) return true;
    }
    return false;
  };
八、_.indexOf | _.lastIndexOf

這兩個(gè)函數(shù)與findIndex以及findLastIndex在寫法上面有異曲同工之處。

_.indexOf以及_.lastIndexOf都引用的一個(gè)公共函數(shù),參數(shù)中都傳入標(biāo)識(shí)符,用來(lái)表示是從頭到尾尋找還是從尾到頭尋找。

我們先來(lái)看這個(gè)公共函數(shù)createIndexFinder

  /**
   * dir           Number    1表示正序查找,-1表示倒敘查找
   * predicateFind Function  _.findIndex 或者 _.findLastIndex
   * sortedIndex   Function  _.sortedIndex
   */
  var createIndexFinder = function(dir, predicateFind, sortedIndex) {
    return function(array, item, idx) {
      var i = 0, length = getLength(array);
      if (typeof idx == "number") {
        // 正序查找,重置i
        if (dir > 0) {
          i = idx >= 0 ? idx : Math.max(idx + length, i);
        } else {
        // 倒敘查找,重置length
          length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1;
        }
      } else if (sortedIndex && idx && length) {
      // 二分法查找
        idx = sortedIndex(array, item);
        return array[idx] === item ? idx : -1;
      }
      // item為NaN的情況處理
      if (item !== item) {
        idx = predicateFind(slice.call(array, i, length), _.isNaN);
        return idx >= 0 ? idx + i : -1;
      }
      // NaN處理之后,可以放心的使用for循環(huán)迭代了
      for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) {
        if (array[idx] === item) return idx;
      }
      // 如果上面的都不存在,則返回 -1
      return -1;
    };
  };

基本上看懂上面的那個(gè)函數(shù),_.findIndex_.findLastIndex就可以理解啦

_.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex);
_.lastIndexOf = createIndexFinder(-1, _.findLastIndex);
九、_.sortedIndex

這個(gè)方法咋眼一看對(duì)我而言還是有點(diǎn)難理解的。所以我先看它的api描述。

使用二分查找確定value在list中的位置序號(hào),value按此序號(hào)插入能保持list原有的排序。如果提供iterator函數(shù),iterator將作為list排序的依據(jù),包括你傳遞的value 。iterator也可以是字符串的屬性名用來(lái)排序(比如length)

看源碼如下:

_.sortedIndex = function(array, obj, iteratee, context) {
    // 如果是一個(gè)數(shù)組元素為數(shù)字,則直接以數(shù)組元素大小為查找條件
    // 如果是數(shù)組元素為對(duì)象,則以 iteratee 為查找條件
    // 通過 cb 來(lái)查找出 數(shù)組元素中的 iterate 屬性值 (利用 _.property ) 
    iteratee = cb(iteratee, context, 1);
    var value = iteratee(obj);
    var low = 0, high = getLength(array);
    while (low < high) {
      var mid = Math.floor((low + high) / 2);
      if (iteratee(array[mid]) < value) low = mid + 1; else high = mid;
    }
    return low;
  };

總體而言,理解二分法以及其對(duì)數(shù)組元素為對(duì)象這種情況的處理方式即可以很好的理解這個(gè)方法了。

十、_.range
/**
 * start: 返回?cái)?shù)組的起始值
 * stop: 返回?cái)?shù)組的終止值
 * step: 返回?cái)?shù)組中間的間隔
 */
_.range = function(start, stop, step) {
    // 如果不存在stop,則默認(rèn)設(shè)置stop為0,start也為0
    if (stop == null) {
      stop = start || 0;
      start = 0;
    }
    // 如果不存在 step 值,則默認(rèn)設(shè)置為 1 或者 -1
    if (!step) {
      step = stop < start ? -1 : 1;
    }
    
    var length = Math.max(Math.ceil((stop - start) / step), 0);
    var range = Array(length);
    
    // 遍歷生成數(shù)組
    for (var idx = 0; idx < length; idx++, start += step) {
      range[idx] = start;
    }

    return range;
};

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/80931.html

相關(guān)文章

  • 探究underscore源碼(一)

    摘要:什么鬼結(jié)合上面的函數(shù),貌似可以看到每次調(diào)用函數(shù)時(shí)都會(huì)判斷一次是否等于。主要原理是利用回調(diào)函數(shù)來(lái)處理調(diào)用方法傳入的參數(shù)。 本文基于underscore v1.8.3版本 源頭 一直想學(xué)習(xí)一下類庫(kù)的源碼,jQuery剛剛看到選擇器那塊,直接被那一大塊正則搞懵逼了。經(jīng)過同事的推薦,選擇了underscore來(lái)作為類庫(kù)研究的起點(diǎn)。 閉包 所有函數(shù)都在一個(gè)閉包內(nèi),避免污染全局變量,這沒什么特殊的...

    CloudwiseAPM 評(píng)論0 收藏0
  • JavaScript專題系列20篇正式完結(jié)!

    摘要:寫在前面專題系列是我寫的第二個(gè)系列,第一個(gè)系列是深入系列。專題系列自月日發(fā)布第一篇文章,到月日發(fā)布最后一篇,感謝各位朋友的收藏點(diǎn)贊,鼓勵(lì)指正。 寫在前面 JavaScript 專題系列是我寫的第二個(gè)系列,第一個(gè)系列是 JavaScript 深入系列。 JavaScript 專題系列共計(jì) 20 篇,主要研究日常開發(fā)中一些功能點(diǎn)的實(shí)現(xiàn),比如防抖、節(jié)流、去重、類型判斷、拷貝、最值、扁平、柯里...

    sixleaves 評(píng)論0 收藏0
  • javascript知識(shí)點(diǎn)

    摘要:模塊化是隨著前端技術(shù)的發(fā)展,前端代碼爆炸式增長(zhǎng)后,工程化所采取的必然措施。目前模塊化的思想分為和。特別指出,事件不等同于異步,回調(diào)也不等同于異步。將會(huì)討論安全的類型檢測(cè)惰性載入函數(shù)凍結(jié)對(duì)象定時(shí)器等話題。 Vue.js 前后端同構(gòu)方案之準(zhǔn)備篇——代碼優(yōu)化 目前 Vue.js 的火爆不亞于當(dāng)初的 React,本人對(duì)寫代碼有潔癖,代碼也是藝術(shù)。此篇是準(zhǔn)備篇,工欲善其事,必先利其器。我們先在代...

    Karrdy 評(píng)論0 收藏0
  • 你要看看這些有趣的函數(shù)方法嗎?

    前言 這是underscore.js源碼分析的第六篇,如果你對(duì)這個(gè)系列感興趣,歡迎點(diǎn)擊 underscore-analysis/ watch一下,隨時(shí)可以看到動(dòng)態(tài)更新。 下劃線中有非常多很有趣的方法,可以用比較巧妙的方式解決我們?nèi)粘I钪杏龅降膯栴},比如_.after,_.before,_.defer...等,也許你已經(jīng)用過他們了,今天我們來(lái)深入源碼,一探究竟,他們到底是怎么實(shí)現(xiàn)的。 showIm...

    melody_lql 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

jeyhan

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<