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

資訊專欄INFORMATION COLUMN

學(xué)習(xí) underscore 源碼整體架構(gòu),打造屬于自己的函數(shù)式編程類庫

junnplus / 2327人閱讀

摘要:譯立即執(zhí)行函數(shù)表達(dá)式處理支持瀏覽器環(huán)境微信小程序。學(xué)習(xí)整體架構(gòu),利于打造屬于自己的函數(shù)式編程類庫。下一篇文章可能是學(xué)習(xí)的源碼整體架構(gòu)。也可以加微信,注明來源,拉您進(jìn)前端視野交流群。

前言

上一篇文章寫了jQuery整體架構(gòu),學(xué)習(xí) jQuery 源碼整體架構(gòu),打造屬于自己的 js 類庫

雖然看過挺多underscore.js分析類的文章,但總感覺少點什么。這也許就是紙上得來終覺淺,絕知此事要躬行吧。于是決定自己寫一篇學(xué)習(xí)underscore.js整體架構(gòu)的文章。

本文章學(xué)習(xí)的版本是v1.9.1。
unpkg.com源碼地址:https://unpkg.com/underscore@...

雖然很多人都沒用過underscore.js,但看下官方文檔都應(yīng)該知道如何使用。

從一個官方文檔_.chain簡單例子看起:

_.chain([1, 2, 3]).reverse().value();
// => [3, 2, 1]

看例子中可以看出,這是支持鏈?zhǔn)秸{(diào)用。

讀者也可以順著文章思路,自行打開下載源碼進(jìn)行調(diào)試,這樣印象更加深刻。

鏈?zhǔn)秸{(diào)用

_.chain 函數(shù)源碼:

_.chain = function(obj) {
    var instance = _(obj);
    instance._chain = true;
    return instance;
};

這個函數(shù)比較簡單,就是傳遞obj調(diào)用_()。但返回值變量竟然是instance實例對象。添加屬性_chain賦值為true,并返回intance對象。但再看例子,實例對象竟然可以調(diào)用reverse方法,再調(diào)用value方法。猜測支持OOP(面向?qū)ο螅┱{(diào)用。

帶著問題,筆者看了下定義 _ 函數(shù)對象的代碼。

_ 函數(shù)對象 支持OOP
var _ = function(obj) {
    if (obj instanceof _) return obj;
    if (!(this instanceof _)) return new _(obj);
    this._wrapped = obj;
};

如果參數(shù)obj已經(jīng)是_的實例了,則返回obj。
如果this不是_的實例,則手動 new _(obj);
再次new調(diào)用時,把obj對象賦值給_wrapped這個屬性。
也就是說最后得到的實例對象是這樣的結(jié)構(gòu)
`{

_wrapped: "參數(shù)obj",

}`
它的原型_(obj).__proto___.prototype;

如果對這塊不熟悉的讀者,可以看下以下這張圖(之前寫面試官問:JS的繼承畫的圖)。

繼續(xù)分析官方的_.chain例子。這個例子拆開,寫成三步。

var part1 = _.chain([1, 2, 3]);
var part2 = part1.reverse();
var part3 = part2.value();

// 沒有后續(xù)part1.reverse()操作的情況下
console.log(part1); // {__wrapped: [1, 2, 3], _chain: true}

console.log(part2); // {__wrapped: [3, 2, 1], _chain: true}

console.log(part3); // [3, 2, 1]

思考問題:reverse本是Array.prototype上的方法呀。為啥支持鏈?zhǔn)秸{(diào)用呢。
搜索reverse,可以看到如下這段代碼:

并將例子代入這段代碼可得(怎么有種高中做數(shù)學(xué)題的既視感^_^):

_.chain([1,2,3]).reverse().value()
var ArrayProto = Array.prototype;
// 遍歷 數(shù)組 Array.prototype 的這些方法,賦值到 _.prototype 上
_.each(["pop", "push", "reverse", "shift", "sort", "splice", "unshift"], function(name) {
    // 這里的`method`是 reverse 函數(shù)
    var method = ArrayProto[name];
    _.prototype[name] = function() {
    // 這里的obj 就是數(shù)組 [1, 2, 3]
    var obj = this._wrapped;
    // arguments  是參數(shù)集合,指定reverse 的this指向為obj,參數(shù)為arguments, 并執(zhí)行這個函數(shù)函數(shù)。執(zhí)行后 obj 則是 [3, 2, 1]
    method.apply(obj, arguments);
    if ((name === "shift" || name === "splice") && obj.length === 0) delete obj[0];
    // 重點在于這里 chainResult 函數(shù)。
    return chainResult(this, obj);
    };
});
// Helper function to continue chaining intermediate results.
var chainResult = function(instance, obj) {
    // 如果實例中有_chain 為 true 這個屬性,則返回實例 支持鏈?zhǔn)秸{(diào)用的實例對象  { _chain: true, this._wrapped: [3, 2, 1] },否則直接返回這個對象[3, 2, 1]。
    return instance._chain ? _(obj).chain() : obj;
};

if ((name === "shift" || name === "splice") && obj.length === 0) delete obj[0];
提一下上面源碼中的這一句,看到這句是百思不得其解。于是趕緊在github中搜索這句加上""雙引號。表示全部搜索。

搜索到兩個在官方庫中的ISSUE,大概意思就是兼容IE低版本的寫法。有興趣的可以點擊去看看。

I don"t understand the meaning of this sentence.

[why delete obj[0]](https://github.com/jashkenas/...

基于流的編程

至此就算是分析完了鏈?zhǔn)秸{(diào)用_.chain()_ 函數(shù)對象。這種把數(shù)據(jù)存儲在實例對象{_wrapped: "", _chain: true} 中,_chain判斷是否支持鏈?zhǔn)秸{(diào)用,來傳遞給下一個函數(shù)處理。這種做法叫做 基于流的編程。

最后數(shù)據(jù)處理完,要返回這個數(shù)據(jù)怎么辦呢。underscore提供了一個value的方法。

_.prototype.value = function(){
    return this._wrapped;
}

順便提供了幾個別名。toJSONvalueOf。
_.prototype.valueOf = _.prototype.toJSON = _.prototype.value;

還提供了 toString的方法。

_.prototype.toString = function() {
    return String(this._wrapped);
};

這里的String()new String() 效果是一樣的。
可以猜測內(nèi)部實現(xiàn)和 _函數(shù)對象類似。

var String = function(){
    if(!(this instanceOf String)) return new String(obj);
}
var chainResult = function(instance, obj) {
    return instance._chain ? _(obj).chain() : obj;
};

細(xì)心的讀者會發(fā)現(xiàn)chainResult函數(shù)中的_(obj).chain(),是怎么實現(xiàn)實現(xiàn)鏈?zhǔn)秸{(diào)用的呢。

_(obj) 是返回的實例對象{_wrapped: obj}呀。怎么會有chain()方法,肯定有地方掛載了這個方法到_.prototype上或者其他操作,這就是_.mixin()。

_.mixin 掛載所有的靜態(tài)方法到 _.prototype, 也可以掛載自定義的方法

_.mixin 混入。但侵入性太強,經(jīng)常容易出現(xiàn)覆蓋之類的問題。記得之前Reactmixin功能,Vue也有mixin功能。但版本迭代更新后基本都是慢慢的都不推薦或者不支持mixin。

_.mixin = function(obj) {
    // 遍歷對象上的所有方法
    _.each(_.functions(obj), function(name) {
        // 比如 chain, obj["chain"] 函數(shù),自定義的,則賦值到_[name] 上,func 就是該函數(shù)。也就是說自定義的方法,不僅_函數(shù)對象上有,而且`_.prototype`上也有
    var func = _[name] = obj[name];
    _.prototype[name] = function() {
        // 處理的數(shù)據(jù)對象
        var args = [this._wrapped];
        // 處理的數(shù)據(jù)對象 和 arguments 結(jié)合
        push.apply(args, arguments);
        // 鏈?zhǔn)秸{(diào)用  chain.apply(_, args) 參數(shù)又被加上了 _chain屬性,支持鏈?zhǔn)秸{(diào)用。
        // _.chain = function(obj) {
        //    var instance = _(obj);
        //    instance._chain = true;
        //    return instance;
        };
        return chainResult(this, func.apply(_, args));
    };
    });
    // 最終返回 _ 函數(shù)對象。
    return _;
};

_.mixin(_);

_mixin(_) 把靜態(tài)方法掛載到了_.prototype上,也就是_.prototype.chain方法 也就是 _.chain方法。

所以_.chain(obj)_(obj).chain()效果一樣,都能實現(xiàn)鏈?zhǔn)秸{(diào)用。

關(guān)于上述的鏈?zhǔn)秸{(diào)用,筆者畫了一張圖,所謂一圖勝千言。

_.mixin 掛載自定義方法

掛載自定義方法:
舉個例子:

_.mixin({
    log: function(){
        console.log("哎呀,我被調(diào)用了");
    }
})
_.log() // 哎呀,我被調(diào)用了
_().log() // 哎呀,我被調(diào)用了
_.functions(obj)
_.functions = _.methods = function(obj) {
    var names = [];
    for (var key in obj) {
    if (_.isFunction(obj[key])) names.push(key);
    }
    return names.sort();
};

_.functions_.methods 兩個方法,遍歷對象上的方法,放入一個數(shù)組,并且排序。返回排序后的數(shù)組。

underscore.js 究竟在__.prototype掛載了多少方法和屬性

再來看下underscore.js究竟掛載在_函數(shù)對象上有多少靜態(tài)方法和屬性,和掛載_.prototype上有多少方法和屬性。

使用for in循環(huán)一試遍知??慈缦麓a:

var staticMethods = [];
var staticProperty = [];
for(var name in _){
    if(typeof _[name] === "function"){
        staticMethods.push(name);
    }
    else{
        staticProperty.push(name);
    }
}
console.log(staticProperty); // ["VERSION", "templateSettings"] 兩個
console.log(staticMethods); // ["after", "all", "allKeys", "any", "assign", ...] 138個
var prototypeMethods = [];
var prototypeProperty = [];
for(var name in _.prototype){
    if(typeof _.prototype[name] === "function"){
        prototypeMethods.push(name);
    }
    else{
        prototypeProperty.push(name);
    }
}
console.log(prototypeProperty); // []
console.log(prototypeMethods); // ["after", "all", "allKeys", "any", "assign", ...] 152個

根據(jù)這些,筆者又畫了一張圖underscore.js 原型關(guān)系圖,畢竟一圖勝千言。

整體架構(gòu)概覽 匿名函數(shù)自執(zhí)行
(function(){

}());

這樣保證不污染外界環(huán)境,同時隔離外界環(huán)境,不是外界影響內(nèi)部環(huán)境。

外界訪問不到里面的變量和函數(shù),里面可以訪問到外界的變量,但里面定義了自己的變量,則不會訪問外界的變量。
匿名函數(shù)將代碼包裹在里面,防止與其他代碼沖突和污染全局環(huán)境。
關(guān)于自執(zhí)行函數(shù)不是很了解的讀者可以參看這篇文章。
[[譯] JavaScript:立即執(zhí)行函數(shù)表達(dá)式(IIFE)](https://segmentfault.com/a/11...

root 處理
var root = typeof self == "object" && self.self === self && self ||
    typeof global == "object" && global.global === global && global ||
    this ||
    {};

支持瀏覽器環(huán)境、nodeWeb Worker、node vm、微信小程序。

導(dǎo)出
if (typeof exports != "undefined" && !exports.nodeType) {
    if (typeof module != "undefined" && !module.nodeType && module.exports) {
    exports = module.exports = _;
    }
    exports._ = _;
} else {
    root._ = _;
}

關(guān)于root處理導(dǎo)出的這兩段代碼的解釋,推薦看這篇文章冴羽:underscore 系列之如何寫自己的 underscore,講得真的太好了。筆者在此就不贅述了。
總之,underscore.js作者對這些處理也不是一蹴而就的,也是慢慢積累,和其他人提ISSUE之后不斷改進(jìn)的。

支持 amd 模塊化規(guī)范
if (typeof define == "function" && define.amd) {
    define("underscore", [], function() {
        return _;
    });
}
_.noConflict 防沖突函數(shù)

源碼:

// 暫存在 root 上, 執(zhí)行noConflict時再賦值回來
var previousUnderscore = root._;
_.noConflict = function() {
    root._ = previousUnderscore;
    return this;
};

使用:



總結(jié)

全文根據(jù)官網(wǎng)提供的鏈?zhǔn)秸{(diào)用的例子, _.chain([1, 2, 3]).reverse().value();較為深入的調(diào)試和追蹤代碼,分析鏈?zhǔn)秸{(diào)用(_.chain()_(obj).chain())、OOP、基于流式編程、和_.mixin(_)_.prototype掛載方法,最后整體架構(gòu)分析。學(xué)習(xí)Underscore.js整體架構(gòu),利于打造屬于自己的函數(shù)式編程類庫。

文章分析的源碼整體結(jié)構(gòu)。

(function() {
    var root = typeof self == "object" && self.self === self && self ||
        typeof global == "object" && global.global === global && global ||
        this ||
        {};
    var previousUnderscore = root._;

    var _ = function(obj) {
      if (obj instanceof _) return obj;
      if (!(this instanceof _)) return new _(obj);
      this._wrapped = obj;
    };

    if (typeof exports != "undefined" && !exports.nodeType) {
      if (typeof module != "undefined" && !module.nodeType && module.exports) {
        exports = module.exports = _;
      }
      exports._ = _;
    } else {
      root._ = _;
    }
    _.VERSION = "1.9.1";

    _.chain = function(obj) {
      var instance = _(obj);
      instance._chain = true;
      return instance;
    };

    var chainResult = function(instance, obj) {
      return instance._chain ? _(obj).chain() : obj;
    };

    _.mixin = function(obj) {
      _.each(_.functions(obj), function(name) {
        var func = _[name] = obj[name];
        _.prototype[name] = function() {
          var args = [this._wrapped];
          push.apply(args, arguments);
          return chainResult(this, func.apply(_, args));
        };
      });
      return _;
    };

    _.mixin(_);

    _.each(["pop", "push", "reverse", "shift", "sort", "splice", "unshift"], function(name) {
      var method = ArrayProto[name];
      _.prototype[name] = function() {
        var obj = this._wrapped;
        method.apply(obj, arguments);
        if ((name === "shift" || name === "splice") && obj.length === 0) delete obj[0];
        return chainResult(this, obj);
      };
    });

    _.each(["concat", "join", "slice"], function(name) {
      var method = ArrayProto[name];
      _.prototype[name] = function() {
        return chainResult(this, method.apply(this._wrapped, arguments));
      };
    });

    _.prototype.value = function() {
      return this._wrapped;
    };

    _.prototype.valueOf = _.prototype.toJSON = _.prototype.value;

    _.prototype.toString = function() {
      return String(this._wrapped);
    };

    if (typeof define == "function" && define.amd) {
      define("underscore", [], function() {
        return _;
      });
    }
}());

下一篇文章可能是學(xué)習(xí)lodash的源碼整體架構(gòu)。

讀者發(fā)現(xiàn)有不妥或可改善之處,歡迎評論指出。另外覺得寫得不錯,可以點贊、評論、轉(zhuǎn)發(fā),也是對筆者的一種支持。

推薦閱讀

underscorejs.org 官網(wǎng)
undersercore-analysis
underscore 系列之如何寫自己的 underscore

筆者往期文章

學(xué)習(xí) jQuery 源碼整體架構(gòu),打造屬于自己的 js 類庫
面試官問:JS的繼承
面試官問:JS的this指向
面試官問:能否模擬實現(xiàn)JS的call和apply方法
面試官問:能否模擬實現(xiàn)JS的bind方法
面試官問:能否模擬實現(xiàn)JS的new操作符
前端使用puppeteer 爬蟲生成《React.js 小書》PDF并合并

關(guān)于

作者:常以若川為名混跡于江湖。前端路上 | PPT愛好者 | 所知甚少,唯善學(xué)。
個人博客 https://lxchuan12.github.io
github blog,相關(guān)源碼和資源都放在這里,求個star^_^~

微信公眾號 若川視野

可能比較有趣的微信公眾號,長按掃碼關(guān)注。也可以加微信 lxchuan12,注明來源,拉您進(jìn)【前端視野交流群】。

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

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

相關(guān)文章

  • 學(xué)習(xí) jQuery 源碼整體架構(gòu)打造屬于自己 js 類庫

    摘要:匿名函數(shù)將代碼包裹在里面,防止與其他代碼沖突和污染全局環(huán)境。學(xué)習(xí)整體架構(gòu),打造屬于自己的函數(shù)式編程類庫讀者發(fā)現(xiàn)有不妥或可改善之處,歡迎評論指出。 雖然現(xiàn)在基本不怎么使用jQuery了,但jQuery流行10多年的JS庫,還是有必要學(xué)習(xí)它的源碼的。也可以學(xué)著打造屬于自己的js類庫,求職面試時可以增色不少。 本文章學(xué)習(xí)的是v3.4.1 版本。unpkg.com源碼地址:https://un...

    Gilbertat 評論0 收藏0
  • 打造屬于自己underscore系列 ( 一 )

    摘要:目前通行的模塊規(guī)范主要集中在和,因此為了讓定義的庫能夠適用于各種規(guī)范。在框架的定義時需檢測使用環(huán)境并兼容各種規(guī)范。服務(wù)端規(guī)范,檢測是否存在,滿足時通過將暴露出來,不滿足則通過對象暴露出來。前者回調(diào)函數(shù)處理的是值和下標(biāo),后者處理的是值和屬性。 本文為博主原創(chuàng)文章,轉(zhuǎn)載請注明出處 https://www.cnblogs.com/kidfl... underscore作為開發(fā)中比較常用的一個...

    nifhlheimr 評論0 收藏0
  • Android架構(gòu)

    摘要:目前它還未正式發(fā)布。理解系列一是谷歌在發(fā)布一套幫助開發(fā)者解決架構(gòu)設(shè)計的方案。但最近還是推出了一份關(guān)于應(yīng)用架構(gòu)的實踐指南,并給出了相當(dāng)詳盡的步驟和一些指導(dǎo)建議。 MVP+Retrofit+Rxjava在項目中實戰(zhàn)解析 文章目標(biāo) MVP在android中的原理解析 MVP+Retrofit+Rxjava在項目中實戰(zhàn)解析 架構(gòu)經(jīng)驗分享 MVP簡單介紹 先說說MVC分層: View:對應(yīng)于布局...

    bergwhite 評論0 收藏0
  • 窺探Underscore源碼系列-開篇

    摘要:他指示了一個對象的屬性,返回的將用來獲得該屬性對應(yīng)的值在上面的分析中,我們知道,當(dāng)傳入的是一個函數(shù)時,還要經(jīng)過一個叫的內(nèi)置函數(shù)才能獲得最終的所以此處的必然是優(yōu)化回調(diào)的作用了。 開篇說明 對的,讓你所見,又開始造輪子了。哈哈,造輪子我們是認(rèn)真的~ 源碼閱讀是必須的,Underscore是因為剛剛學(xué)習(xí)整理了一波函數(shù)式編程,加上自己曾經(jīng)沒有太多閱讀源碼的經(jīng)驗,先拿Underscore練練手,...

    zorpan 評論0 收藏0
  • JavaScript - 收藏集 - 掘金

    摘要:插件開發(fā)前端掘金作者原文地址譯者插件是為應(yīng)用添加全局功能的一種強大而且簡單的方式。提供了與使用掌控異步前端掘金教你使用在行代碼內(nèi)優(yōu)雅的實現(xiàn)文件分片斷點續(xù)傳。 Vue.js 插件開發(fā) - 前端 - 掘金作者:Joshua Bemenderfer原文地址: creating-custom-plugins譯者:jeneser Vue.js插件是為應(yīng)用添加全局功能的一種強大而且簡單的方式。插....

    izhuhaodev 評論0 收藏0

發(fā)表評論

0條評論

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