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

資訊專欄INFORMATION COLUMN

underscore源碼學(xué)習(xí)(一)

gclove / 1322人閱讀

摘要:所以,剛開始,我從源碼比較短的包含注釋只有行開始學(xué)習(xí)起。一般,在客戶端瀏覽器環(huán)境中,即為,暴露在全局中。學(xué)習(xí)以后判斷直接使用看起來(lái)也優(yōu)雅一點(diǎn)滑稽臉。在的函數(shù)視線中,的作用執(zhí)行一個(gè)傳入函數(shù)次,并返回由每次執(zhí)行結(jié)果組成的數(shù)組。

前言

最近在社區(qū)瀏覽文章的時(shí)候,看到了一位大四學(xué)長(zhǎng)在尋求前端工作中的面經(jīng),看完不得不佩服,掌握知識(shí)點(diǎn)真是全面,無(wú)論是前端后臺(tái)還是其他,都有涉獵。

在他寫的文章中,有這么一句話,大概意思是,沒有看過一個(gè)庫(kù)或者框架的源碼還敢出來(lái)混。然后自己心虛了一下,一直以來(lái),都只是學(xué)習(xí)使用框架或庫(kù),或者在過程中有學(xué)習(xí)框架的思想,但并不深入。例如,在學(xué)習(xí)Vue.js中,我曾經(jīng)去探索過Vue中的雙向綁定是如何實(shí)現(xiàn)的,通過什么模式,什么API,作者的思想是什么,也曾經(jīng)實(shí)現(xiàn)過簡(jiǎn)單版的雙向綁定。

但是感覺自己在這方面并沒有什么提高,尤其在原生JavaScript的學(xué)習(xí)中,一些不常用的API經(jīng)常忘,思維也不夠好。所以有了學(xué)習(xí)優(yōu)秀的庫(kù)的源碼的想法,一方面能夠?qū)W習(xí)作者的思想,提高自己的分析能力,另一方面我覺得如果能好好分析一個(gè)庫(kù)的源碼,對(duì)自己的提升也是有的。

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

什么是underscore

Underscore一個(gè)JavaScript實(shí)用庫(kù),提供了一整套函數(shù)式編程的實(shí)用功能,但是沒有擴(kuò)展任何JavaScript內(nèi)置對(duì)象。它是這個(gè)問題的答案:“如果我在一個(gè)空白的HTML頁(yè)面前坐下, 并希望立即開始工作, 我需要什么?“...它彌補(bǔ)了部分jQuery沒有實(shí)現(xiàn)的功能,同時(shí)又是Backbone.js必不可少的部分。——摘自Underscore中文文檔

我的學(xué)習(xí)之路是基于Underscore1.8.3版本開始的。

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

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

(function(){

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

學(xué)習(xí)的點(diǎn),當(dāng)我們要寫自己的庫(kù)或者封裝某個(gè)功能函數(shù)時(shí),可以給自己的庫(kù)或函數(shù)在最外層包裹一個(gè)立即執(zhí)行函數(shù),這樣既不會(huì)受外部影響,也不會(huì)給外部添麻煩。

_對(duì)象

underscore有下劃線的意思,所以u(píng)nderscore通過一個(gè)下劃線變量_來(lái)標(biāo)識(shí)自身,值得注意的是,_是一個(gè)函數(shù)對(duì)象或者說是一個(gè)構(gòu)造函數(shù),并且支持無(wú)new調(diào)用的構(gòu)造的函數(shù),所有API都會(huì)掛載在這個(gè)對(duì)象上,如_.each,_.map

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

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

var root = this;

為了防止其他庫(kù)對(duì)_的沖突或影響,underscore做了如下處理,

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

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

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

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

便于壓縮代碼

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

同時(shí)也可減少代碼量,避免在使用時(shí)冗長(zhǎng)的書寫

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中,有很多函數(shù)都會(huì)有一個(gè)context函數(shù),也就是當(dāng)前函數(shù)的執(zhí)行上下文,underscore對(duì)其進(jìn)行了處理,如果沒有傳入contextcontextundefined,則返回原函數(shù)。
這里判斷值為undefined用的是void 0,如下:

if (context === void 0) return func

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

詳情可點(diǎn)鏈接了解,這樣做更加安全可靠。
在還沒看到這個(gè)代碼時(shí), 如果我要判斷一個(gè)值是不是undefined,我會(huì)這樣寫

if (context === undefined) {}

但是,在發(fā)現(xiàn)作者的void 0之后,才發(fā)現(xiàn)這樣寫并不可靠,在JavaScript中,我們可以這樣寫:

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

如果這樣寫,undefined就被輕易地修改為了1,所以對(duì)于我們之后定義的undefined的理解有歧義。所以,在JavaScript中,把undefined直接解釋為“未定義”是有風(fēng)險(xiǎn)的,因?yàn)樗赡鼙恍薷摹?/p>

學(xué)習(xí):以后判斷undefined直接使用void 0, 看起來(lái)也優(yōu)雅一點(diǎn)(滑稽臉)。

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

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

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

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

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

在函數(shù)式編程中,使用更多的是迭代,而不是循環(huán)。
迭代:

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

循環(huán):

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

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

_.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函數(shù)中的第二個(gè)參數(shù)iteratee,這個(gè)參數(shù)的格式可以是函數(shù),對(duì)象,字符串。underscore會(huì)將其處理成一個(gè)函數(shù),這將由回調(diào)函數(shù)cb來(lái)完成,我們來(lái)看一下cb的實(shí)現(xiàn):

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

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

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

如果傳入的是字面量,如數(shù)字,字符串等, 他會(huì)返回對(duì)應(yīng)的key值,如下:

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

在上面的cb函數(shù)中,我們可以看到,當(dāng)傳入的數(shù)據(jù)格式是函數(shù),則需要通過optimizeCb函數(shù)進(jìn)行統(tǒng)一處理,返回對(duì)應(yīng)的回調(diào)函數(shù),下面是underscore中optimizeCb函數(shù)的實(shí)現(xiàn):

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

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

optimizeCb 的總體思路就是:傳入待優(yōu)化的回調(diào)函數(shù) func,以及迭代回調(diào)需要的參數(shù)個(gè)數(shù)argCount,根據(jù)參數(shù)個(gè)數(shù)分情況進(jìn)行優(yōu)化。

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

_.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函數(shù)中當(dāng)argCount的個(gè)數(shù)為2的情況并不常見,在_.each,_.map等函數(shù)中,argCount的值為3(value, key, obj),當(dāng)argCount需要四個(gè)參數(shù)時(shí),這四個(gè)參數(shù)的格式為:

accumulator:累加器

value:迭代元素

index:迭代索引

collection:當(dāng)前迭代集合

underscore中reduce的實(shí)現(xiàn)如下:

/**
 * reduce函數(shù)的工廠函數(shù), 用于生成一個(gè)reducer, 通過參數(shù)決定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 用來(lái)記錄最新的 reduce 結(jié)果
      // 執(zhí)行 reduce 回調(diào), 刷新當(dāng)前值
      memo = iteratee(memo, obj[currentKey], currentKey, obj);
    }
    return memo;
  }
  return function(obj, iteratee, memo, context) {
    // 優(yōu)化回調(diào)
    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初始值 則從左第一個(gè)為初始值 從右則最后一個(gè)為初始值
      memo = obj[keys ? keys[index] : index];
      index += dir;
    }
    // return func
    return iterator(obj, iteratee, memo, keys, index, length);
  }
}

例如在_.reduce、_.reduceRight中,argCount的值為4。看看underscore中_.reduce的使用例子

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

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/91906.html

相關(guān)文章

  • underscore源碼該如何閱讀?

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

    weknow619 評(píng)論0 收藏0
  • Underscore源碼解析(

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

    neu 評(píng)論0 收藏0
  • 學(xué)習(xí) underscore 源碼整體架構(gòu),打造屬于自己的函數(shù)式編程類庫(kù)

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

    junnplus 評(píng)論0 收藏0
  • underscore.js 源碼學(xué)習(xí) 讀書筆記(

    摘要:所以經(jīng)常會(huì)在一個(gè)源碼中看到寫法吧立即執(zhí)行函數(shù)創(chuàng)建變量,保存全局根變量。 // ================立即執(zhí)行函數(shù)================ // 使用(function(){}())立即執(zhí)行函數(shù),減少全局變量 // ----????----函數(shù)聲明 function (){} 與函數(shù)表達(dá)式 var funName = function(){}----????---- /...

    lx1036 評(píng)論0 收藏0
  • 源碼解讀這半年

    摘要:作者韓子遲不知不覺間,源碼解讀系列進(jìn)入了真正的尾聲,也請(qǐng)?jiān)试S我最后一次下項(xiàng)目的原始地址這半年以來(lái),花費(fèi)了大量的業(yè)余時(shí)間,共計(jì)寫了篇隨筆包括此文,也給的源碼加了差不多行注釋,對(duì)于當(dāng)初說的要做史上最詳細(xì)的源碼剖析,至此我也覺得問心無(wú)愧。 作者:韓子遲 What? 不知不覺間,「Underscore 源碼解讀系列」進(jìn)入了真正的尾聲,也請(qǐng)?jiān)试S我最后一次 po 下項(xiàng)目的原始地址 https://...

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

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

0條評(píng)論

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