摘要:我們都知道可以鏈式調用,比如我們寫個簡單的模擬鏈式調用之所以能實現鏈式調用,關鍵就在于通過,返回調用對象。系列預計寫八篇左右,重點介紹中的代碼架構鏈式調用內部函數模板引擎等內容,旨在幫助大家閱讀源碼,以及寫出自己的。
前言
本文接著上篇《underscore 系列之如何寫自己的 underscore》,閱讀本篇前,希望你已經閱讀了上一篇。
jQuery我們都知道 jQuery 可以鏈式調用,比如:
$("div").eq(0).css("width", "200px").show();
我們寫個簡單的 demo 模擬鏈式調用:
function JQuery(selector) { this.elements = []; var nodeLists = document.getElementsByTagName(selector); for (var i = 0; i < nodeLists.length; i++) { this.elements.push(nodeLists[i]); } return this; } JQuery.prototype = { eq: function(num){ this.elements = [this.elements[0]]; return this; }, css: function(prop, val) { this.elements.forEach(function(el){ el.style[prop] = val; }) return this; }, show: function() { this.css("display", "block"); return this; } } window.$ = function(selector){ return new JQuery(selector) } $("div").eq(0).css("width", "200px").show();
jQuery 之所以能實現鏈式調用,關鍵就在于通過 return this,返回調用對象。再精簡下 demo 就是:
var jQuery = { eq: function(){ console.log("調用 eq 方法"); return this; }, show: function(){ console.log("調用 show 方法"); return this; } } jQuery.eq().show();_.chain
在 underscore 中,默認不使用鏈式調用,但是如果你想使用鏈式調用,你可以通過 _.chain 函數實現:
_.chain([1, 2, 3, 4]) .filter(function(num) { return num % 2 == 0; }) .map(function(num) { return num * num }) .value(); // [4, 16]
我們看看 _.chain 這個方法都做了什么:
_.chain = function (obj) { var instance = _(obj); instance._chain = true; return instance; };
我們以 [1, 2, 3] 為例,_.chain([1, 2, 3]) 會返回一個對象:
{ _chain: true, _wrapped: [1, 2, 3] }
該對象的原型上有著 underscore 的各種方法,我們可以直接調用這些方法。
但是問題在于原型上的這些方法并沒有像 jQuery 一樣,返回 this ,所以如果你調用了一次方法,就無法接著調用其他方法了……
但是試想下,我們將函數的返回值作為參數再傳入 _.chain 函數中,不就可以接著調用其他方法了?
寫一個精簡的 Demo:
var _ = function(obj) { if (!(this instanceof _)) return new _(obj); this._wrapped = obj; }; _.chain = function (obj) { var instance = _(obj); instance._chain = true; return instance; }; _.prototype.push = function(num) { this._wrapped.push(num); return this._wrapped } _.prototype.shift = function(num) { this._wrapped.shift() return this._wrapped } var res = _.chain([1, 2, 3]).push(4); // 將上一個函數的返回值,傳入 _.chain,然后再繼續調用其他函數 var res2 = _.chain(res).shift(); console.log(res2); // [2, 3, 4]
然而這也太復雜了吧,難道 chain 這個過程不能是自動化的嗎?如果我是開發者,我肯定希望直接寫成:
_.chain([1, 2, 3]).push(4).shift()
所以我們再優化一下實現方式:
var _ = function(obj) { if (!(this instanceof _)) return new _(obj); this._wrapped = obj; }; var chainResult = function (instance, obj) { return instance._chain ? _.chain(obj) : obj; }; _.chain = function (obj) { var instance = _(obj); instance._chain = true; return instance; }; _.prototype.push = function(num) { this._wrapped.push(num); return chainResult(this, this._wrapped) } _.prototype.shift = function() { this._wrapped.shift(); return chainResult(this, this._wrapped) } var res = _.chain([1, 2, 3]).push(4).shift(); console.log(res._wrapped);
我們在每個函數中,都用 chainResult 將函數的返回值包裹一遍,再生成一個類似以下這種形式的對象:
{ _wrapped: some value, _chain: true }
該對象的原型上有各種函數,而這些函數的返回值作為參數傳入了 chainResult,該函數又會返回這樣一個對象,函數的返回值就保存在 _wrapped 中,這樣就實現了鏈式調用。
_.chain鏈式調用原理就是這樣,可是這樣的話,我們需要對每個函數都進行修改呀……
幸運的是,在 underscore 中,所有的函數是掛載到 _ 函數對象中的,_.prototype 上的函數是通過 _.mixin 函數將 _ 函數對象中的所有函數復制到 _.prototype 中的。
所以為了實現鏈式調用,我們還需要對上一篇《underscore 系列之如何寫自己的 underscore》 中的 _.mixin 方法進行一定修改:
// 修改前 var ArrayProto = Array.prototype; var push = ArrayProto.push; _.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 func.apply(_, args); }; }); return _; }; _.mixin(_);
// 修改后 var ArrayProto = Array.prototype; var push = ArrayProto.push; 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(_);_.value
根據上面的分析過程,我們知道如果我們打印:
console.log(_.chain([1, 2, 3]).push(4).shift());
其實會打印一個對象 {_chain: true, _wrapped: [2, 3, 4] }
可是我希望獲得值是 [2, 3, 4] 呀!
所以,我們還需要提供一個 value 方法,當執行 value 方法的時候,就返回當前 _wrapped 的值。
_.prototype.value = function() { return this._wrapped; };
此時調用方式為:
var arr = _.chain([1, 2, 3]).push(4).shift().value(); console.log(arr) // [2, 3, 4]最終代碼
結合上一篇文章,最終的 underscore 代碼組織結構如下:
(function() { var root = (typeof self == "object" && self.self == self && self) || (typeof global == "object" && global.global == global && global) || this || {}; var ArrayProto = Array.prototype; var push = ArrayProto.push; 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 = "0.2"; var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1; var isArrayLike = function(collection) { var length = collection.length; return typeof length == "number" && length >= 0 && length <= MAX_ARRAY_INDEX; }; _.each = function(obj, callback) { var length, i = 0; if (isArrayLike(obj)) { length = obj.length; for (; i < length; i++) { if (callback.call(obj[i], obj[i], i) === false) { break; } } } else { for (i in obj) { if (callback.call(obj[i], obj[i], i) === false) { break; } } } return obj; } _.isFunction = function(obj) { return typeof obj == "function" || false; }; _.functions = function(obj) { var names = []; for (var key in obj) { if (_.isFunction(obj[key])) names.push(key); } return names.sort(); }; /** * 在 _.mixin(_) 前添加自己定義的方法 */ _.reverse = function(string){ return string.split("").reverse().join(""); } _.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(_); _.prototype.value = function () { return this._wrapped; }; })()underscore 系列
underscore 系列目錄地址:https://github.com/mqyqingfeng/Blog。
underscore 系列預計寫八篇左右,重點介紹 underscore 中的代碼架構、鏈式調用、內部函數、模板引擎等內容,旨在幫助大家閱讀源碼,以及寫出自己的 undercore。
如果有錯誤或者不嚴謹的地方,請務必給予指正,十分感謝。如果喜歡或者有所啟發,歡迎star,對作者也是一種鼓勵。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/89915.html
摘要:所以它與其他系列的文章并不沖突,完全可以在閱讀完這個系列后,再跟著其他系列的文章接著學習。如何閱讀我在寫系列的時候,被問的最多的問題就是該怎么閱讀源碼我想簡單聊一下自己的思路。感謝大家的閱讀和支持,我是冴羽,下個系列再見啦 前言 別名:《underscore 系列 8 篇正式完結!》 介紹 underscore 系列是我寫的第三個系列,前兩個系列分別是 JavaScript 深入系列、...
摘要:在上篇文章整體架構分析中,我們講過上面的方法有兩種掛載方式,一個是掛載到構造函數上以的形式直接調用在后文上統稱構造函數調用,另一種則是掛到上以的形式被實例調用在后文上統稱原型調用。 underscore源碼分析之基礎方法 本文是underscore源碼剖析系列的第二篇,主要介紹underscore中一些基礎方法的實現。 mixin 在上篇文章underscore整體架構分析中,我們講...
摘要:支持形式的調用這其實是非常經典的無構造,其實就是一個構造函數,的結果就是一個對象實例,該實例有個屬性,屬性值是。 前言 終于,樓主的「Underscore 源碼解讀系列」underscore-analysis 即將進入尾聲,關注下 timeline 會發現樓主最近加快了解讀速度。十一月,多事之秋,最近好多事情搞的樓主心力憔悴,身心俱疲,也想盡快把這個系列完結掉,也好了卻一件心事。 本文...
摘要:譯立即執行函數表達式處理支持瀏覽器環境微信小程序。學習整體架構,利于打造屬于自己的函數式編程類庫。下一篇文章可能是學習的源碼整體架構。也可以加微信,注明來源,拉您進前端視野交流群。 前言 上一篇文章寫了jQuery整體架構,學習 jQuery 源碼整體架構,打造屬于自己的 js 類庫 雖然看過挺多underscore.js分析類的文章,但總感覺少點什么。這也許就是紙上得來終覺淺,絕知此...
摘要:目前通行的模塊規范主要集中在和,因此為了讓定義的庫能夠適用于各種規范。在框架的定義時需檢測使用環境并兼容各種規范。服務端規范,檢測是否存在,滿足時通過將暴露出來,不滿足則通過對象暴露出來。前者回調函數處理的是值和下標,后者處理的是值和屬性。 本文為博主原創文章,轉載請注明出處 https://www.cnblogs.com/kidfl... underscore作為開發中比較常用的一個...
閱讀 4620·2021-10-25 09:48
閱讀 3212·2021-09-07 09:59
閱讀 2167·2021-09-06 15:01
閱讀 2693·2021-09-02 15:21
閱讀 2732·2019-08-30 14:14
閱讀 2184·2019-08-29 13:59
閱讀 2514·2019-08-29 11:02
閱讀 2533·2019-08-26 13:33