摘要:目前通行的模塊規(guī)范主要集中在和,因此為了讓定義的庫能夠適用于各種規(guī)范。在框架的定義時(shí)需檢測使用環(huán)境并兼容各種規(guī)范。服務(wù)端規(guī)范,檢測是否存在,滿足時(shí)通過將暴露出來,不滿足則通過對象暴露出來。前者回調(diào)函數(shù)處理的是值和下標(biāo),后者處理的是值和屬性。
本文為博主原創(chuàng)文章,轉(zhuǎn)載請注明出處 https://www.cnblogs.com/kidfl...
underscore作為開發(fā)中比較常用的一個(gè)javascript工具庫,提供了一套豐富的函數(shù)式編程功能,該庫并沒有拓展原有的javascript原生對象,而是在自定義的_對象上,提供了100多個(gè)方法函數(shù)。在這個(gè)系列中,將從uderscore源碼角度, 打造一個(gè)自己的underscore框架一,框架設(shè)計(jì) 1.1 自執(zhí)行函數(shù)
現(xiàn)代js 庫的框架設(shè)計(jì),一般都是以自執(zhí)行函數(shù)的形式,自執(zhí)行函數(shù)一般有兩種形式
(function(){ // 形式一 }())
(function(){ // 形式二 })()
我們知道,函數(shù)聲明的形式會(huì)掛載到window對象作為方法存在,而函數(shù)表達(dá)式的形式則會(huì)掛載在window對象作為屬性存在,這都會(huì)造成變量污染,而自執(zhí)行函數(shù)的好處在于可以防止變量的污染,函數(shù)執(zhí)行完后便立刻銷毀掉。
1.2 使用風(fēng)格underscore有兩種風(fēng)格形式可以使用,一種是面向?qū)ο箢愋停硪环N是函數(shù)類型。
// 例子 _.map([1, 2, 3], function(n){ return n * 2; }); _([1, 2, 3]).map(function(n){ return n * 2; });
因此,在定義underscore類的時(shí)候需要考慮對象和函數(shù)兩種場景。當(dāng)以函數(shù)的形式調(diào)用時(shí)需要把 _ 當(dāng)作一個(gè)構(gòu)造函數(shù)并返回他的實(shí)例化。代碼如下
(function(root){ var _ = function (obj) { if (!(this instanceof _)) { return new _(obj) } } root._ = _ }(this))1.3 使用環(huán)境
現(xiàn)在前端開發(fā)重視模塊化,以node服務(wù)端而論, 我們有commonjs規(guī)范,以客戶端而論,我們有AMD 和 CMD規(guī)范,對應(yīng)的模塊加載器為 requirejs 和 seajs。目前通行的javascript模塊規(guī)范主要集中在commonjs 和 AMD,因此為了讓定義的underscore庫能夠適用于各種規(guī)范。在框架的定義時(shí)需檢測使用環(huán)境并兼容各種規(guī)范。
服務(wù)端:commonjs規(guī)范,檢測module.exports 是否存在,滿足時(shí)通過 module.exports = {} 將 underscore暴露出來,不滿足則 通過window對象暴露出來。
客戶端: AMD 規(guī)范, 檢測 define.amd 是否存在,滿足時(shí)通過 define("**", [], function(){ return "***" }) 暴露模塊
(function (root) { var _ = function (obj) { if (!(this instanceof _)) { return new _(obj) } } // commonjs 規(guī)范 檢測 module.exports 是否存在 if ((typeof module !== "undefined" && module.exports)) { module.exports = { _: _ } } else { root._ = _;// window 對象暴露方法 } // amd 規(guī)范,檢測 define.amd 是否存在 if (typeof define == "function" && define.amd) { define("underscore", [], function () { return _; }); } }(this))
// commonjs const _ = require("./underscore.js") console.log(_)
// AMD require(["underscore"], function (underscore) { console.log(underscore) })1.4 方法定義
underscore的調(diào)用,既可以通過 _.unique(),也可以通過 _().unique(),兩種方法效果相同卻需要在框架設(shè)計(jì)時(shí)定義兩套方法,一套是定義 _ 對象的靜態(tài)方法,另一套是擴(kuò)展 _對象原型鏈上的方法。
_.uniqe = function() {} _.prototype.unique = function() {}
為了避免冗余代碼,可以將定義好的靜態(tài)方法復(fù)制一份成為原型鏈上的方法
(function(root){ ··· _.mixins = function() { // 復(fù)制靜態(tài)方法到原型上 } _.mixins() // 執(zhí)行方法 }(this))
mixins 方法的實(shí)現(xiàn),需要遍歷 underscore 對象上所有的靜態(tài)方法,因此需要先完成對 遍歷方法 _.each 的實(shí)現(xiàn)
_.each(list, iteratee, [context]) Alias: forEach
遍歷list中的所有元素,按順序用每個(gè)元素當(dāng)做參數(shù)調(diào)用 iteratee 函數(shù)。如果傳遞了context參數(shù),則把iteratee綁定到context對象上。每次調(diào)用iteratee都會(huì)傳遞三個(gè)參數(shù):(element, index, list)。如果list是個(gè)JavaScript對象,iteratee的參數(shù)是 (value, key, list))。返回list以方便鏈?zhǔn)秸{(diào)用。
each 的第一個(gè)參數(shù)按照文檔可以支持 數(shù)組,類數(shù)組,對象三種類型,數(shù)組類數(shù)組和對象在遍歷時(shí)的處理方式不同。前者回調(diào)函數(shù)處理的是 值和下標(biāo),后者處理的是 值和屬性。
// 判斷數(shù)組,類數(shù)組方法 (function(root) { ··· _.each = function (list, callback, context) { // context 存在會(huì)改變callback 中this 的指向 var i = 0; var key; if (isArrayLikeLike(list)) { // 數(shù)組,類數(shù)組 for (var i = 0; i < list.length; i++) { context ? callback.call(context, list[i], i, list) : callback(list[i], i, list) } } else { // 對象 for (key in list) { context ? callback.call(context, list[key], key) : callback(list[key], key) } } } var isArrayLike = function (collection) { // 返回參數(shù) collection 的 length 屬性值 var length = collection.length; // length是數(shù)值,非負(fù),且小于等于MAX_ARRAY_INDEX // MAX_ARRAY_INDEX = Math.pow(2, 53) - 1 return typeof length == "number" && length >= 0 && length <= MAX_ARRAY_INDEX; } }(this))
mixin方法的設(shè)計(jì),目的是為了在underscore原型對象上擴(kuò)展更多的方法,它既可以用來擴(kuò)展用戶自定義的方法,比如
_.mixin({ capitalize: function(string) { return string.charAt(0).toUpperCase() + string.substring(1).toLowerCase(); } }); _("fabio").capitalize(); => "Fabio"
當(dāng)然也可以用來內(nèi)部拷貝靜態(tài)方法到原型鏈的方法上。
(function(root){ ··· var push = Array.prototype.push var _ = function (obj) { if (!(this instanceof _)) { return new _(obj) } this.wrap = obj // 存儲(chǔ)實(shí)例對象傳過來的參數(shù) } _.mixins = function (obj) { _.each(obj, function (value, key) { _.prototype[key] = function () { var args = [this.wrap] push.apply(args, arguments) return value.apply(this, args) } }) } _.mixins(_) }(this))
其中關(guān)注點(diǎn)在arguments 的處理上,靜態(tài)方法需要傳遞目標(biāo)源作為方法的參數(shù) 即_.unique(目標(biāo)源, 回調(diào)函數(shù)),而實(shí)例方法的目標(biāo)源存儲(chǔ)在構(gòu)造對象的屬性中 ,即_(目標(biāo)源).unique(回調(diào)函數(shù)),因此定義實(shí)例方法時(shí)需要合并屬性和回調(diào)函數(shù)。即Array.prorotype.push.apply([this.wrap], arguments),之后將他作為參數(shù)傳遞給靜態(tài)方法并返回處理結(jié)果。
將類數(shù)組轉(zhuǎn)成數(shù)組的方法
Array.prototype.slice.call(類數(shù)組)
var a = []; Array.prototype.push.apply(a, 類數(shù)組); console.log(a);
var a = []; Array.prototype.concat.apply(a, 類數(shù)組); console.log(a);
ES6方法 Array.from(類數(shù)組)
ES6擴(kuò)展運(yùn)算符 var args = [...類數(shù)組]
1.5 鏈?zhǔn)秸{(diào)用返回一個(gè)封裝的對象. 在封裝的對象上調(diào)用方法會(huì)返回封裝的對象本身, 直道 value 方法調(diào)用為止。
underscore中方法的調(diào)用返回的是處理后的值,因此無法支持方法的鏈?zhǔn)秸{(diào)用。如果需要鏈?zhǔn)秸{(diào)用,需要使用chain()方法,chain的使用會(huì)使每次調(diào)用方法后返回underscore的實(shí)例對象,直到 調(diào)用value方法才停止返回。
(function(root){ ··· // chain方法會(huì)返回 _ 實(shí)例,并且標(biāo)注該實(shí)例是否允許鏈?zhǔn)秸{(diào)用的 _.chain = function(obj) { var instance = _(obj); instance.chain = true; return instance } // 增加是否支持鏈?zhǔn)秸{(diào)用的判斷,如果支持,則返回該實(shí)例,不支持則直接返回結(jié)果, var chainResult = function (instance, obj) { return instance.chain ? _(obj).chain() : obj } _.mixins = function (obj) { _.each(obj, function (value, key) { _.prototype[key] = function () { var args = [this.wrap] push.apply(args, arguments) return chainResult(this, value.apply(this, args)) // 修改實(shí)例方法的返回值,返回值通過chainResult 包裝,根據(jù)chainResult的判斷結(jié)果改變返回值 } }) } }(this))
因?yàn)殒準(zhǔn)秸{(diào)用會(huì)使underscore的方法返回他的實(shí)例對象,所以當(dāng)需要結(jié)束這一調(diào)用行為時(shí),需要使用value()。 value()方法會(huì)返回調(diào)用的結(jié)果。
(function(root){ ··· _.value = function(instance) { return instance.wrap } }(this))
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/99790.html
摘要:譯立即執(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分析類的文章,但總感覺少點(diǎn)什么。這也許就是紙上得來終覺淺,絕知此...
摘要:只有動(dòng)手,你才能真的理解作者的構(gòu)思的巧妙只有動(dòng)手,你才能真正掌握一門技術(shù)持續(xù)更新中項(xiàng)目地址求求求源碼系列跟一起學(xué)如何寫函數(shù)庫中高級前端面試手寫代碼無敵秘籍如何用不到行代碼寫一款屬于自己的類庫原理講解實(shí)現(xiàn)一個(gè)對象遵循規(guī)范實(shí)戰(zhàn)手摸手,帶你用擼 Do it yourself!!! 只有動(dòng)手,你才能真的理解作者的構(gòu)思的巧妙 只有動(dòng)手,你才能真正掌握一門技術(shù) 持續(xù)更新中…… 項(xiàng)目地址 https...
摘要:最近開始看源碼,并將源碼解讀放在了我的計(jì)劃中。今天就跟大家聊一聊中一些常用類型檢查方法,以及一些工具類的判斷方法。用是否含有屬性來判斷工具類判斷方法接下來看下一些常用的工具類判斷方法。 Why underscore 最近開始看 underscore.js 源碼,并將 underscore.js 源碼解讀 放在了我的 2016 計(jì)劃中。 閱讀一些著名框架類庫的源碼,就好像和一個(gè)個(gè)大師對話...
摘要:因?yàn)樵谖⑿判〕绦蛑校投际牵由嫌謴?qiáng)制使用嚴(yán)格模式,為,掛載就會(huì)發(fā)生錯(cuò)誤,所以就有人又發(fā)了一個(gè),代碼變成了這就是現(xiàn)在的樣子。 前言 在 《JavaScript 專題系列》 中,我們寫了很多的功能函數(shù),比如防抖、節(jié)流、去重、類型判斷、扁平數(shù)組、深淺拷貝、查找數(shù)組元素、通用遍歷、柯里化、函數(shù)組合、函數(shù)記憶、亂序等,可以我們該如何組織這些函數(shù),形成自己的一個(gè)工具函數(shù)庫呢?這個(gè)時(shí)候,我們就要借...
閱讀 3414·2021-11-24 09:38
閱讀 3193·2021-11-22 09:34
閱讀 2106·2021-09-22 16:03
閱讀 2363·2019-08-29 18:37
閱讀 376·2019-08-29 16:15
閱讀 1767·2019-08-26 13:56
閱讀 862·2019-08-26 12:21
閱讀 2204·2019-08-26 12:15