国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Lodash 源碼分析(三)Array

ZoomQuiet / 2861人閱讀

摘要:前言這是源碼分析系列文章的第三篇,前面兩篇文章源碼分析一源碼分析二分別分析了中的一些重要函數(shù),也給出了簡化的實現(xiàn),為理解其內部機理和執(zhí)行方式提供了便利。官方也對其進行了說明。

前言

這是Lodash源碼分析系列文章的第三篇,前面兩篇文章(Lodash 源碼分析(一)“Function” Methods、Lodash 源碼分析(二)“Function” Methods)分別分析了Lodash "Function" 中的一些重要函數(shù),也給出了簡化的實現(xiàn),為理解其內部機理和執(zhí)行方式提供了便利。這篇文章將專注于Array,Array是Lodash中非常重要的內容,我們將分析其代碼實現(xiàn)以及同類似庫中的實現(xiàn)對比。

_.head

_.head函數(shù)其實很簡單,返回一個數(shù)組的第一個元素,完全可以在兩三行代碼中實現(xiàn)??梢钥吹絃odash中是這么實現(xiàn)的:

function head(array) {
   return (array && array.length) ? array[0] : undefined;
}

Lodash進行了簡單的判斷,然后返回了第一個元素。這么簡單的函數(shù)其實沒有什么好說的,但我拿出來說是想介紹另一個庫Ramda.js的實現(xiàn):

module.exports = nth(0);

它是用nth函數(shù)實現(xiàn)該功能的,那么這個函數(shù)式怎么樣的呢?

module.exports = _curry2(function nth(offset, list) {
  var idx = offset < 0 ? list.length + offset : offset;
  return _isString(list) ? list.charAt(idx) : list[idx];
});

這個函數(shù)就有點意思了,用了柯里化,是一個函數(shù)式的實現(xiàn),當head函數(shù)返回一個nth(0)時,其實返回的是一個柯里化之后的函數(shù),然后再接受一個數(shù)組,判斷數(shù)組類型之后返回list[offset]的值。

再看看Lodash的nth的實現(xiàn):

function nth(array, n) {
   return (array && array.length) ? baseNth(array, toInteger(n)) : undefined;
}

function baseNth(array, n) {
  var length = array.length;
  if (!length) {
    return;
  }
  n += n < 0 ? length : 0;
  return isIndex(n, length) ? array[n] : undefined;
}

仔細對比兩個庫的實現(xiàn),兩個庫都允許負下標的處理,但是對于Ramda而言,如果list是一個null或者undefined類型的數(shù)據(jù)的話,將會拋出TypeError,而Lodash則優(yōu)雅一些。

_.join

_.join函數(shù)是另一個簡單的函數(shù):

var arrayProto = Array.prototype;
var nativeJoin = arrayProto.join;

function join(array, separator) {
  return array == null ? "" : nativeJoin.call(array, separator);
}

重寫之后函數(shù)變?yōu)?

function join(array,separator) {
    return array == null ? "" : Array.prototype.join.call(array, separator);
}

我們再對比一下Ramda的實現(xiàn):

var invoker = require("./invoker");
module.exports = invoker(1, "join");

再看看invoker函數(shù):

module.exports = _curry2(function invoker(arity, method) {
  return curryN(arity + 1, function() {
    var target = arguments[arity];
    if (target != null && _isFunction(target[method])) {
      return target[method].apply(target, Array.prototype.slice.call(arguments, 0, arity));
    }
    throw new TypeError(toString(target) + " does not have a method named "" + method + """);
  });
});

invoker函數(shù)就是為了返回一個curry化的函數(shù),那么我們其實可以這么理解如果用Lodash實現(xiàn)一個函數(shù)化的join可以這么實現(xiàn):

function _join(array,separator){
        return Array.prototype.join.call(array,seprator);
}
var join = _.curry(_join);

那么我們可以和Ramda的使用方式一樣使用:

join(_,",")([1,2,3]);
// 1,2,3
_.remove

這個方法很有意思,我們可以看到不同的實現(xiàn)方式(通常實現(xiàn)/函數(shù)式實現(xiàn)),兩種實現(xiàn)差別很大,所以拿出來分析一下。

先看看Lodash的實現(xiàn):

    /**
     * Removes all elements from `array` that `predicate` returns truthy for
     * and returns an array of the removed elements. The predicate is invoked
     * with three arguments: (value, index, array).
     *
     * **Note:** Unlike `_.filter`, this method mutates `array`. Use `_.pull`
     * to pull elements from an array by value.
     *
     * @static
     * @memberOf _
     * @since 2.0.0
     * @category Array
     * @param {Array} array The array to modify.
     * @param {Function} [predicate=_.identity] The function invoked per iteration.
     * @returns {Array} Returns the new array of removed elements.
     * @example
     *
     * var array = [1, 2, 3, 4];
     * var evens = _.remove(array, function(n) {
     *   return n % 2 == 0;
     * });
     *
     * console.log(array);
     * // => [1, 3]
     *
     * console.log(evens);
     * // => [2, 4]
     */
    function remove(array, predicate) {
      var result = [];
      if (!(array && array.length)) {
        return result;
      }
      var index = -1,
          indexes = [],
          length = array.length;

      predicate = getIteratee(predicate, 3);
      while (++index < length) {
        var value = array[index];
        if (predicate(value, index, array)) {
          result.push(value);
          indexes.push(index);
        }
      }
      basePullAt(array, indexes);
      return result;
    }

一定要注意的是,該方法會修改原數(shù)組。官方也對其進行了說明。該方法同_.fliter的區(qū)別也就在是否會修改原對象上。

我們分析一下Lodash是如何實現(xiàn)這個功能的,首先判斷數(shù)組是否合法,如果不合法就直接返回。在Lodash中的實現(xiàn)其實很簡單,首先得到一個predicate謂詞函數(shù),該謂詞函數(shù)用于判斷元素是否符合條件,如果符合條件就將其從原數(shù)組中移除。邏輯也比較簡單,但是該函數(shù)會修改原array,該功能是通過basePullAt()實現(xiàn)的:

    /**
     * The base implementation of `_.pullAt` without support for individual
     * indexes or capturing the removed elements.
     *
     * @private
     * @param {Array} array The array to modify.
     * @param {number[]} indexes The indexes of elements to remove.
     * @returns {Array} Returns `array`.
     */
    function basePullAt(array, indexes) {
      var length = array ? indexes.length : 0,
          lastIndex = length - 1;

      while (length--) {
        var index = indexes[length];
        if (length == lastIndex || index !== previous) {
          var previous = index;
          if (isIndex(index)) {
            splice.call(array, index, 1);
          } else {
            baseUnset(array, index);
          }
        }
      }
      return array;
    }

需要說明的是,這里的splice方法的原型是Array.prototype.splice,該方法同Array.prototype.slice的區(qū)別是,splice會修改原數(shù)組的內容,而slice不會修改原數(shù)組的內容,而僅僅做的是一次淺拷貝。

還需要說明一下的是baseUnset

/**
 * The base implementation of `unset`.
 *
 * @private
 * @param {Object} object The object to modify.
 * @param {Array|string} path The property path to unset.
 * @returns {boolean} Returns `true` if the property is deleted, else `false`.
 */
function baseUnset(object, path) {
  path = castPath(path, object)
  object = parent(object, path)
  return object == null || delete object[toKey(last(path))]
}

export default baseUnset

這個方法其實很簡單,就是刪除對象中的某一個屬性/鍵。

所以Lodash的整個_.remove的脈絡就捋清楚了,按照慣例,我們需要稍微簡化一下這個函數(shù),把核心邏輯抽取出來:

function remove(list,predicated){
  var indexes = [];
    for(var i=0;i < list.length;i++){
      if(predicated(list[i])){
        indexes.push(i);
      }
    }
    for(var idx = indexes.length -1; idx >=0;idx--){
      Array.prototype.splice.call(list,indexes[idx],1);
    }
    return list;
}

var a = [1,2,3,4];
remove(a,function(a){if (a == 3) return true; else return false;});
console.log(a); // [1,2,4]

恩,感覺好像也挺好用的。

但是我們不能止步于此,作為一個熱衷函數(shù)式編程的程序員,最終目標是代碼中沒有循環(huán)沒有分支。我們看看Ramda.js是怎么實現(xiàn)的:

/**
 * Removes the sub-list of `list` starting at index `start` and containing
 * `count` elements. _Note that this is not destructive_: it returns a copy of
 * the list with the changes.
 * No lists have been harmed in the application of this function.
 *
 * @func
 * @memberOf R
 * @since v0.2.2
 * @category List
 * @sig Number -> Number -> [a] -> [a]
 * @param {Number} start The position to start removing elements
 * @param {Number} count The number of elements to remove
 * @param {Array} list The list to remove from
 * @return {Array} A new Array with `count` elements from `start` removed.
 * @example
 *
 *      R.remove(2, 3, [1,2,3,4,5,6,7,8]); //=> [1,2,6,7,8]
 */
module.exports = _curry3(function remove(start, count, list) {
  var result = Array.prototype.slice.call(list, 0);
  result.splice(start, count);
  return result;
});

其實Ramda就是對splice進行了curry化,什么也沒有做,毫無參考價值。沒有達到我們的預期,所以只能自己動手了:

function remove2(list,predicated){
  return _remove(list,list.length-1,predicated);
}

function _remove(list,idx,predicated){
  if(predicated(list[idx])){
    list.splice(idx,1);
  }
  if (idx == 0){return list;}else{
      _remove(list,idx-1,predicated);
  }
}
//調用
var a = [1,2,3,4];
remove2(a,function(a){if (a == 3) return true; else return false;});
console.log(a); //[1,2,4]

感覺舒服多了,對于JavaScript而言沒有分支語句是不可能的,但是可以把所有的循環(huán)用遞歸取代,感覺代碼也簡潔了許多,函數(shù)式能夠讓人以另一個角度思考問題,真的是一個很好的編程范式。

結語

最近工作非常忙,也沒有時間寫第三篇連載,忙里抽空用午休時間將本文寫完了。成文比較匆忙難免有一些謬誤望各位看官海涵,也希望能夠直接指出我文章中的錯誤,感激不盡!

敬請期待

本系列文章還有后續(xù)內容,包括數(shù)組和集合的操作,以及對象的操作,具體還沒有想好涉及哪方面內容,總之敬請期待!

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

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/91970.html

相關文章

  • lodash源碼分析之數(shù)組的差集

    摘要:依賴源碼分析之緩存使用方式的進一步封裝源碼分析之源碼分析之源碼分析之的實現(xiàn)源碼分析之源碼分析的調用如果有傳遞,則先調用,使用生成要比較數(shù)組的映射數(shù)組。循環(huán)完畢,沒有在第二個數(shù)組中發(fā)現(xiàn)相同的項時,將該項存入數(shù)組中。 外部世界那些破舊與貧困的樣子,可以使我內心世界得到平衡?!柧S諾《煙云》 本文為讀 lodash 源碼的第十七篇,后續(xù)文章會更新到這個倉庫中,歡迎 star:pocke...

    Noodles 評論0 收藏0
  • Lodash 源碼分析(二)“Function” Methods

    摘要:眾所周知,函數(shù)能夠將一個集合進行折疊。我們看到源代碼是這樣的在官方的注釋中說,對于對象,遍歷順序是無法保證的。我在閱讀源代碼的過程中也會遇到很多不理解的地方。待續(xù)下周將繼續(xù)更新源碼分析系列,接下來將會分析集合方法。 前言 這是Lodash源碼分析的第二篇文章,我們在第一篇Lodash 源碼分析(一)Function Methods中介紹了基本的_.after,_.map,以及復雜的_....

    cheukyin 評論0 收藏0
  • lodash源碼分析之chunk的尺與刀

    摘要:萬條數(shù)據(jù)依賴讀源碼之從看稀疏數(shù)組與密集數(shù)組原理的原理歸結起來就是切割和放置。尺在切割之前,需要用尺確定切割的數(shù)量。容器的長度剛好與塊的數(shù)量一致。當與塊的數(shù)量相等時,表示已經切割完畢,停止切割,最后將結果返回。 以不正義開始的事情,必須用罪惡使它鞏固。——莎士比亞《麥克白》 最近很多事似乎印證了這句話,一句謊言最后要用一百句謊言來圓謊。 本文為讀 lodash 源碼的第二篇,后續(xù)文章會...

    ZweiZhao 評論0 收藏0
  • lodash源碼分析之baseFindIndex中的運算符優(yōu)先級

    摘要:從表中可以看到,比較運算符的優(yōu)先級為,而三元表達式條件運算符的優(yōu)化級為,因此可以確定比較運算符的優(yōu)先級要比三元表達式的要高,循環(huán)條件其實等價于第二種寫法。從上表中也可以看出前綴自增比比較運算符的優(yōu)化級要高。 我悟出權力本來就是不講理的——蟑螂就是海米;也悟出要造反,內心必須強大到足以承受任何后果才行?!睄u《城門開》 本文為讀 lodash 源碼的第十篇,后續(xù)文章會更新到這個倉庫中...

    Meathill 評論0 收藏0
  • lodash源碼分析之compact中的遍歷

    摘要:到這里,源碼分析完了。但是,有兩個致命的特性的遍歷不能保證順序會遍歷所有可枚舉屬性,包括繼承的屬性。的遍歷順序依賴于執(zhí)行環(huán)境,不同執(zhí)行環(huán)境的實現(xiàn)方式可能會不一樣。 小時候,鄉(xiāng)愁是一枚小小的郵票, 我在這頭, 母親在那頭。 長大后,鄉(xiāng)愁是一張窄窄的船票, 我在這頭, 新娘在那頭。 后來啊, 鄉(xiāng)愁是一方矮矮的墳墓, 我在外頭, 母親在里頭。 而現(xiàn)在, 鄉(xiāng)愁是一灣淺淺的海峽, 我在這頭, 大...

    dmlllll 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<