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

資訊專欄INFORMATION COLUMN

underscore源碼學習(一)

gclove / 1313人閱讀

摘要:所以,剛開始,我從源碼比較短的包含注釋只有行開始學習起。一般,在客戶端瀏覽器環境中,即為,暴露在全局中。學習以后判斷直接使用看起來也優雅一點滑稽臉。在的函數視線中,的作用執行一個傳入函數次,并返回由每次執行結果組成的數組。

前言

最近在社區瀏覽文章的時候,看到了一位大四學長在尋求前端工作中的面經,看完不得不佩服,掌握知識點真是全面,無論是前端后臺還是其他,都有涉獵。

在他寫的文章中,有這么一句話,大概意思是,沒有看過一個庫或者框架的源碼還敢出來混。然后自己心虛了一下,一直以來,都只是學習使用框架或庫,或者在過程中有學習框架的思想,但并不深入。例如,在學習Vue.js中,我曾經去探索過Vue中的雙向綁定是如何實現的,通過什么模式,什么API,作者的思想是什么,也曾經實現過簡單版的雙向綁定。

但是感覺自己在這方面并沒有什么提高,尤其在原生JavaScript的學習中,一些不常用的API經常忘,思維也不夠好。所以有了學習優秀的庫的源碼的想法,一方面能夠學習作者的思想,提高自己的分析能力,另一方面我覺得如果能好好分析一個庫的源碼,對自己的提升也是有的。

所以,剛開始,我從源碼比較短的underscore.js(包含注釋只有1.5k行)開始學習起。

什么是underscore

Underscore一個JavaScript實用庫,提供了一整套函數式編程的實用功能,但是沒有擴展任何JavaScript內置對象。它是這個問題的答案:“如果我在一個空白的HTML頁面前坐下, 并希望立即開始工作, 我需要什么?“...它彌補了部分jQuery沒有實現的功能,同時又是Backbone.js必不可少的部分?!訳nderscore中文文檔

我的學習之路是基于Underscore1.8.3版本開始的。

// Current version.
 _.VERSION = "1.8.3";
作用域包裹

與其他第三方庫一樣,underscore最外層是一個立即執行函數(IIFE),來包裹自己的業務邏輯。一般使用IIFE有如下好處,可以創建一個獨立的沙箱似的作用域,避免全局污染,還可以防止其他代碼對該函數內部造成影響。(但凡在立即執行函數中聲明的函數、變量等,除非是自己想暴露,否則絕無可能在外部獲得)

(function(){

    // ...執行邏輯
    
}.call(this))

學習的點,當我們要寫自己的庫或者封裝某個功能函數時,可以給自己的庫或函數在最外層包裹一個立即執行函數,這樣既不會受外部影響,也不會給外部添麻煩。

_對象

underscore有下劃線的意思,所以underscore通過一個下劃線變量_來標識自身,值得注意的是,_是一個函數對象或者說是一個構造函數,并且支持無new調用的構造的函數,所有API都會掛載在這個對象上,如_.each,_.map

var _ = function(obj) {
    if(obj instanceof _) return obj;
    if(!(this instanceof _)) return new _(obj) //實例化
    this._wrapped = obj
}
全局命名空間

underscore使用root變量保存了全局的this

var root = this;

為了防止其他庫對_的沖突或影響,underscore做了如下處理,

var previousUnderscore = root._
_.noConflict = function() {
    root._ = perviousUnderscore;
    return this;
}
執行環境判斷

underscore 既能夠服務于瀏覽器,又能夠服務于諸如 nodejs 所搭建的服務端。
一般,在客戶端(瀏覽器)環境中,_即為window._=_,暴露在全局中。若在node環境中,_將被作為模塊導出,并且向后兼容老的API,即require。

if (typeof exports !== "undefined") {
  if (typeof module !== "undefined" && module.exports) {
    exports = module.exports = _;  
  }
  exports._ = _ ;
} esle {
  root._ = _;
}
緩存局部變量及快速引用

underscore本身用到了不少ES5的原生方法,在瀏覽器支持的條件下,underscore率先使用原生的ES5方法。如下代碼所示,underscore通過局部變量來保存一些常用到的方法或者屬性。
這樣做有幾個好處:

便于壓縮代碼

提高代碼性能,減少在原型鏈中的查找次數

同時也可減少代碼量,避免在使用時冗長的書寫

var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
var
  push             = ArrayProto.push,
  slice            = ArrayProto.slice,
  toString         = ObjProto.toString,
  hasOwnProperty   = ObjProto.hasOwnProperty;
var
  nativeIsArray      = Array.isArray,
  nativeKeys         = Object.keys,
  nativeBind         = FuncProto.bind,
  nativeCreate       = Object.create;
undefined處理

在underscore中,有很多函數都會有一個context函數,也就是當前函數的執行上下文,underscore對其進行了處理,如果沒有傳入contextcontextundefined,則返回原函數。
這里判斷值為undefined用的是void 0,如下:

if (context === void 0) return func

作為一只涉獵尚淺的小白,查閱資料之后終于知道這里作者為什么要用void 0來做判斷了。

詳情可點鏈接了解,這樣做更加安全可靠。
在還沒看到這個代碼時, 如果我要判斷一個值是不是undefined,我會這樣寫

if (context === undefined) {}

但是,在發現作者的void 0之后,才發現這樣寫并不可靠,在JavaScript中,我們可以這樣寫:

args => {
  let undefined = 1
  console.log(undefined) // => 1
  if (args === undefined) {
    //...
  }
}

如果這樣寫,undefined就被輕易地修改為了1,所以對于我們之后定義的undefined的理解有歧義。所以,在JavaScript中,把undefined直接解釋為“未定義”是有風險的,因為它可能被修改。

學習:以后判斷undefined直接使用void 0, 看起來也優雅一點(滑稽臉)。

處理類數組
// getLength 函數
// 該函數傳入一個參數,返回參數的 length 屬性值
// 用來獲取 array 以及 arrayLike 元素的 length 屬性值
var getLength = property("length");

// 判斷是否是 ArrayLike Object
// 類數組,即擁有 length 屬性并且 length 屬性值為 Number 類型的元素
// 包括數組、arguments、HTML Collection 以及 NodeList 等等
// 包括類似 {length: 10} 這樣的對象
// 包括字符串、函數等
var isArrayLike = function(collection) {
  var length = getLength(collection);
  return typeof length == "number" && length >= 0 && length <= MAX_ARRAY_INDEX;
};
對象創建的特殊處理

為了處理Object.create的跨瀏覽器的兼容性,underscore進行了特殊的處理。我們知道,原型是無法直接實例化的,因此我們先創建一個空對象,然后將其原型指向這個我們想要實例化的原型,最后返回該對象其一個實例。其代碼如下:

var Ctor = function() {};  // 用于代理原型轉換的空函數

var baseCreate = function(prototype) {
  if (!(_.isObject(prototype))) return {}; // 如果參數不是對象,直接返回空對象
  if (nativeCreate) return nativeCreate(prototype); // 如果原生的對象創建可以使用,返回該方法根據原型創建的對象
    
  // 處理沒有原生對象創建的情況
  Ctor.prototype = prototype;  // 將空函數的原型指向要使用的原型
  var result = new Ctor();  // 創建一個實例
  Ctor.prototype = null;  // 恢復Ctor的原型供下次使用
  return result;  // 返回該實例
};
underscore中的迭代(iteratee)

在函數式編程中,使用更多的是迭代,而不是循環。
迭代:

var res = _.map([1,2], function(item){
  return item * 2
})

循環:

var arr = [1,2]
var res = []
for(var i = 0; i < arr.length; i++) {
  res.push(arr[i] * 2)
}

在underscore中迭代使用非常巧妙,源碼也寫的非常好,通過傳入的數據類型不同而選擇不同的迭代函數。
首先,在underscore中_.map的實現如下:

_.map = _.collect = function(obj, iteratee, context) {
  iteratee = cb(iteratee, context);
  var keys = !isArrayLike(obj) && _.keys(obj),
      length = (keys || obj).length,
      results = Array(length);
  for (var index = 0; index < length; index++) {
    var currentKey = keys ? keys[index] : index;
    results[index] = iteratee(obj[currentKey], currentKey, obj) //(value, index, obj)
  }
  return results;
}

可以看到,在_.map函數中的第二個參數iteratee,這個參數的格式可以是函數,對象,字符串。underscore會將其處理成一個函數,這將由回調函數cb來完成,我們來看一下cb的實現:

var cb = function(value, context, argCount) {
 // 是否用默認的迭代器 如果沒有傳入value 則返回當前迭代元素自身
 if (value == null) return _.identity;
 // 如果value是一個回調函數, 則需要優化回調 優化函數為optimizeCb
 if (_.isFunction(value)) return optimizeCb(value, context, argCount);
 // 如果value是個對象, 則返回一個matcher進行對象匹配
 if (_.isObject(value)) return _.matcher(value)
 // 否則, 如果value只是一個字面量, 則把value看做是屬性名稱, 返回一個對應的屬性獲得函數
 return _.property(value);
}

前面兩個比較容易理解,看看當傳入的數據格式為對象的情況,如果 value 傳入的是一個對象,那么返回iteratee(_.matcher)的目的是想要知道當前被迭代元素是否匹配給定的這個對象:

var results = _.map([{name:"water"},{name: "lzb",age:13}], {name: "lzb"});
// => results: [false,true]

如果傳入的是字面量,如數字,字符串等, 他會返回對應的key值,如下:

var results = _.map([{name:"water"},{name:"lzb"}],"name");
// => results: ["water", "lzb"];
回調處理

在上面的cb函數中,我們可以看到,當傳入的數據格式是函數,則需要通過optimizeCb函數進行統一處理,返回對應的回調函數,下面是underscore中optimizeCb函數的實現:

// 回調處理
// underscore 內部方法
// 根據 this 指向(context 參數)
// 以及 argCount 參數
// 二次操作返回一些回調、迭代方法
var optimizeCb = function(func, context, argCount) {
  // // void 0 會返回純正的undefined,這樣做避免undefined已經被污染帶來的判定失效
  if (context === void 0) return func;
  switch (argCount == null ? 3 : argCount) {
    // 回調參數為1時, 即迭代過程中,我們只需要值
    // _.times
    case 1: return function(value) {
      return func.call(context, value);
    };
    case 2: return function(value, other) {
      return func.call(context, value, other);
    };
    // 3個參數(值,索引,被迭代集合對象)
    // _.each、_.map  (value, key, obj)
    case 3: return function(value, index, collection) {
      return func.call(context, value, index, collection);
    };
    // 4個參數(累加器(比如reducer需要的), 值, 索引, 被迭代集合對象)
    // _.reduce、_.reduceRight
    case 4: return function(accumulator, value, index, collection) {
      return func.call(context, accumulator, value, index, collection);
    };
  }

  // 如果都不符合上述的任一條件,直接使用apply調用相關函數
  return function() {
    return func.apply(context, arguments);
  };
}

optimizeCb 的總體思路就是:傳入待優化的回調函數 func,以及迭代回調需要的參數個數argCount,根據參數個數分情況進行優化。

在underscore的_.times函數視線中,_times的作用執行一個傳入iteratee函數n次,并返回由每次執行結果組成的數組。它的迭代過程iteratee只需要1個參數(當前迭代的索引)
_.times函數在underscore中的實現:

_.times = function(n, iteratee, context) {
  vat accum = Array(Math.max(0, n));
  iteratee = optimizeCb(iteratee, context, 1);
  for (var i = 0; i < n; i++) accum[i] = iteratee(i);
  return accum;
}

_.times的使用

function getIndex(index) {
  return index;
}
var results = _.times(3, getIndex); // => [0,1,2]

optimizeCb函數中當argCount的個數為2的情況并不常見,在_.each,_.map等函數中,argCount的值為3(value, key, obj),當argCount需要四個參數時,這四個參數的格式為:

accumulator:累加器

value:迭代元素

index:迭代索引

collection:當前迭代集合

underscore中reduce的實現如下:

/**
 * reduce函數的工廠函數, 用于生成一個reducer, 通過參數決定reduce的方向
 * @param dir 方向 left or right
 * @returns {function}
 */
function createReduce(dir) {
  function iterator(obj, iteratee, memo, keys, index, length) {
    for(; index >= 0 && index < length; index += dir) {
      var currentKey = keys ? keys[index] : index;
      // memo 用來記錄最新的 reduce 結果
      // 執行 reduce 回調, 刷新當前值
      memo = iteratee(memo, obj[currentKey], currentKey, obj);
    }
    return memo;
  }
  return function(obj, iteratee, memo, context) {
    // 優化回調
    iteratee = optimizeCb(iteratee, context, 4);
    var keys = !isArrayLike(obj) && _.keys(obj),
        length = (keys || obj).length,
        index = dir > 0 ? 0 : length - 1;
    if (arguments.length < 3) {
      // 如果沒有傳入memo初始值 則從左第一個為初始值 從右則最后一個為初始值
      memo = obj[keys ? keys[index] : index];
      index += dir;
    }
    // return func
    return iterator(obj, iteratee, memo, keys, index, length);
  }
}

例如在_.reduce、_.reduceRight中,argCount的值為4??纯磚nderscore中_.reduce的使用例子

var sum = _.reduce([1,2,3,4], function(accumulator, value, index, collection){
  return accumulator + value;
}, 0) // => 10

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

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

相關文章

  • underscore源碼該如何閱讀?

    摘要:所以它與其他系列的文章并不沖突,完全可以在閱讀完這個系列后,再跟著其他系列的文章接著學習。如何閱讀我在寫系列的時候,被問的最多的問題就是該怎么閱讀源碼我想簡單聊一下自己的思路。感謝大家的閱讀和支持,我是冴羽,下個系列再見啦 前言 別名:《underscore 系列 8 篇正式完結!》 介紹 underscore 系列是我寫的第三個系列,前兩個系列分別是 JavaScript 深入系列、...

    weknow619 評論0 收藏0
  • Underscore源碼解析(

    摘要:本文同步自我得博客最近準備折騰一下,在事先了解了之后,我知道了對這個庫有著強依賴,正好之前也沒使用過,于是我就想先把徹底了解一下,這樣之后折騰的時候也少一點阻礙。 本文同步自我得博客:http://www.joeray61.com 最近準備折騰一下backbone.js,在事先了解了backbone之后,我知道了backbone對underscore這個庫有著強依賴,正好undersc...

    neu 評論0 收藏0
  • 學習 underscore 源碼整體架構,打造屬于自己的函數式編程類庫

    摘要:譯立即執行函數表達式處理支持瀏覽器環境微信小程序。學習整體架構,利于打造屬于自己的函數式編程類庫。下一篇文章可能是學習的源碼整體架構。也可以加微信,注明來源,拉您進前端視野交流群。 前言 上一篇文章寫了jQuery整體架構,學習 jQuery 源碼整體架構,打造屬于自己的 js 類庫 雖然看過挺多underscore.js分析類的文章,但總感覺少點什么。這也許就是紙上得來終覺淺,絕知此...

    junnplus 評論0 收藏0
  • underscore.js 源碼學習 讀書筆記(

    摘要:所以經常會在一個源碼中看到寫法吧立即執行函數創建變量,保存全局根變量。 // ================立即執行函數================ // 使用(function(){}())立即執行函數,減少全局變量 // ----????----函數聲明 function (){} 與函數表達式 var funName = function(){}----????---- /...

    lx1036 評論0 收藏0
  • 源碼解讀這半年

    摘要:作者韓子遲不知不覺間,源碼解讀系列進入了真正的尾聲,也請允許我最后一次下項目的原始地址這半年以來,花費了大量的業余時間,共計寫了篇隨筆包括此文,也給的源碼加了差不多行注釋,對于當初說的要做史上最詳細的源碼剖析,至此我也覺得問心無愧。 作者:韓子遲 What? 不知不覺間,「Underscore 源碼解讀系列」進入了真正的尾聲,也請允許我最后一次 po 下項目的原始地址 https://...

    zzzmh 評論0 收藏0

發表評論

0條評論

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