摘要:判斷數(shù)據(jù)的類(lèi)型輸出高階函數(shù)實(shí)現(xiàn)面向切面編程的主要作用是把一些核心業(yè)務(wù)邏輯模塊無(wú)關(guān)的功能抽離出來(lái),這些無(wú)關(guān)的模塊包括日志統(tǒng)計(jì),安全控制,異常處理。
高階函數(shù)是指至少滿(mǎn)足以下條件之一的函數(shù):
函數(shù)可以作為參數(shù)被傳遞
函數(shù)可以作為返回值輸出
函數(shù)作為參數(shù)傳遞把參數(shù)當(dāng)作參數(shù)傳遞, 抽離出一部分容易變化的業(yè)務(wù)邏輯,將它放在函數(shù)參數(shù)中,這樣可以分離業(yè)務(wù)代碼中變化與不變的部分。其中一個(gè)重要的應(yīng)用場(chǎng)景就是回調(diào)函數(shù)。1. 回調(diào)函數(shù)
var appendDiv = function() { for (var i = 0; i < 100; i++) { var div = document.createElement("div") div.innerHTML = i document.body.appendChild(div) div.style.display = "none" } } appendDiv()
把div.style.display = "none"這種硬編碼放在appendDiv里顯然是不合理的,appendDiv未免有點(diǎn)個(gè)性化了,成為一個(gè)難復(fù)用的函數(shù),于是我們將div.style.display = "none這行代碼抽離出來(lái),用回調(diào)函數(shù)的形式調(diào)用
var appendDiv = function(callback) { for (var i = 0; i < 100; i++) { var div = document.createElement("div") div.innerHTML = i document.body.appendChild(div) if (typeof callback === "function") { callback(div) } } } appendDiv(function(node){ node.style.display = "none" })2. Array.prototype.sort
Array.prototype.sort接受一個(gè)函數(shù)當(dāng)作參數(shù),這個(gè)函數(shù)里封裝里數(shù)組元素的排序規(guī)則。
其中數(shù)組是不變,而排序規(guī)則是可變的,將可變的部分封裝在函數(shù)里。
[1,4,5].sort(function(a, b){ return a - b }) // 輸出 [1,3,4]函數(shù)作為返回值輸出
相比把函數(shù)作為參數(shù)傳遞,函數(shù)當(dāng)作返回值輸出的應(yīng)用場(chǎng)景也許更多,也更能體現(xiàn)出函數(shù)式編程的巧妙。讓函數(shù)返回一個(gè)可執(zhí)行的函數(shù),意味著運(yùn)算過(guò)程是可延續(xù)。判斷數(shù)據(jù)的類(lèi)型
var type = function(data) { if(arguments.length === 0) return; var typeStr = Object.prototype.toString.call(data) return typeStr.match(/[object (.*?)]/)[1].toLowerCase() } console.log(type("Array")) //輸出 string高階函數(shù)實(shí)現(xiàn)AOP
AOP(面向切面編程) 的主要作用是把一些核心業(yè)務(wù)邏輯模塊無(wú)關(guān)的功能抽離出來(lái),這些無(wú)關(guān)的模塊包括日志統(tǒng)計(jì),安全控制,異常處理。把這些功能抽離之后,再通過(guò)動(dòng)態(tài)織入的方式摻入業(yè)務(wù)邏輯中。這樣做的好處是保持業(yè)務(wù)邏輯模塊的純凈和高內(nèi)聚性,其次是可以很方便的復(fù)用次用模塊。在JavaScript中AOP的實(shí)現(xiàn)非常簡(jiǎn)單,這是與生俱來(lái)的能力。
Function.prototype.before = function(beforefn) { var _self = this // 保存原函數(shù)的引用 return function() { // 返回包含了原函數(shù)和新函數(shù)的"代理"函數(shù) beforefn.apply(this, arguments) // 執(zhí)行新函數(shù),修正this return _self.apply(this, arguments) // 執(zhí)行原函數(shù) } } Function.prototype.after = function(afterfn) { var _self = this return function() { var ret = _self.apply(this, arguments) //修正this值,并且執(zhí)行原函數(shù) afterfn.apply(this, arguments) //執(zhí)行新函數(shù) return ret } } var fnc = function(){ console.log(2) } fnc = fnc.before(function(){ console.log(1) }).after(function(){ console.log(3) }) fnc() // 輸出 1 2 3高階函數(shù)的其他應(yīng)用 1. 函數(shù)柯里化
函數(shù)柯里化(function currying)又稱(chēng)部分求值。一個(gè)currying的函數(shù)首先會(huì)接受一些參數(shù),接受了這些參數(shù)之后,該函數(shù)不會(huì)立即求值,而是繼續(xù)返回另外一個(gè)函數(shù),剛才傳入的參數(shù)在函數(shù)中形成的閉包中被保存起來(lái)。待到函數(shù)被真正需要求值的時(shí)候,之前傳入的所有參數(shù)都會(huì)被一次性用于求值。
// 通用currying函數(shù),接受一個(gè)參數(shù),即將要被currying的函數(shù) var currying = function(fn) { var args = [] return function() { if (arguments.length === 0) { return fn.apply(this, args) } else { [].push.apply(args, arguments) return arguments.callee } } } // 被currying的函數(shù) var cost = (function(){ var money = 0 return function() { for (var i = 0, l= arguments.length; i < l; i++) { money += arguments[i] } return money } })() var cost = currying(cost) cost(100) //未真正求值 cost(200) //未真正求值 cost(300) //未真正求值 console.log(cost()) // 求值并輸出:6002. uncurrying
>`uncurrying`的目的是將泛化this的過(guò)程提取出來(lái),將`fn.call`或者`fn.apply`抽象成通用的函數(shù)。 在javascript中,當(dāng)我們調(diào)用對(duì)象的某個(gè)方法時(shí),其實(shí)不用關(guān)心該對(duì)象原本是否擁有這個(gè)方法,這也是動(dòng)態(tài)類(lèi)型語(yǔ)言的特點(diǎn)??梢杂胉call`和`apply`去借用一個(gè)原本不屬于它的方法
var obj1 = { name: "sven" } var obj2 = { getName: function() { return this.name } } console.log(obj2.getName.call(obj1)) // 輸出: sven
通過(guò)uncurrying的方式,我們可以把Array.prototype上的方法"復(fù)制"到array對(duì)象上,同樣這些方法可操作的對(duì)象也不僅僅只是array對(duì)象
// uncurrying實(shí)現(xiàn) Function.prototype.uncurrying = function() { var self = this; return function() { return Function.prototype.call.apply(self, arguments); } }; // 將Array.prototype.push進(jìn)行uncurrying,此時(shí)push函數(shù)的作用就跟Array.prototype.push一樣了,且不僅僅局限于只能操作array對(duì)象。 var push = Array.prototype.push.uncurrying(); var obj = { "length": 1, "0": 1 }; push(obj, 2); console.log(obj); // 輸出:{0: 1, 1: 2, length: 2}3. 函數(shù)節(jié)流
當(dāng)一個(gè)函數(shù)被頻繁調(diào)用時(shí),如果會(huì)造成很大的性能問(wèn)題的時(shí)候,這個(gè)時(shí)候可以考慮函數(shù)節(jié)流,降低函數(shù)被調(diào)用的頻率。
throttle函數(shù)的原理是,將即將被執(zhí)行的函數(shù)用setTimeout延遲一段時(shí)間執(zhí)行。如果該次延遲執(zhí)行還沒(méi)有完成,則忽略接下來(lái)調(diào)用該函數(shù)的請(qǐng)求。throttle函數(shù)接受2個(gè)參數(shù),第一個(gè)參數(shù)為需要被延遲執(zhí)行的函數(shù),第二個(gè)參數(shù)為延遲執(zhí)行的時(shí)間。
var throttle = function(fn, interval) { var _self = fn, // 保存需要被延時(shí)執(zhí)行的函數(shù)引用 timer, // 定時(shí)器 firstTime = true // 是否是第一次調(diào)用 return function() { var args = arguments, _me = this if (firstTime) { // 如果是第一次調(diào)用,不延時(shí)執(zhí)行 _self.apply(_me, args) return firstTime = false } if (timer) { // 如果定時(shí)器還在,說(shuō)明前一次延時(shí)執(zhí)行還沒(méi)有完成 return false } timer = setTimeout(function(){ //延時(shí)執(zhí)行 clearTimeout(timer) timer = null _self.apply(_me, args) }, interval || 500) } } window.onresize = throttle(function(){ console.log(1) }, 500)4. 分時(shí)函數(shù)
當(dāng)一次的用戶(hù)操作會(huì)嚴(yán)重地影響頁(yè)面性能,如在短時(shí)間內(nèi)往頁(yè)面中大量添加DOM節(jié)點(diǎn)顯然也會(huì)讓瀏覽器吃不消,我們看到的結(jié)果往往就是瀏覽器的卡頓甚至假死。
這個(gè)問(wèn)題的解決方案之一是下面的timeChunk函數(shù),timeChunk函數(shù)讓創(chuàng)建節(jié)點(diǎn)的工作分批進(jìn)行,比如把1秒鐘創(chuàng)建1000個(gè)節(jié)點(diǎn),改為每隔200毫秒創(chuàng)建8個(gè)節(jié)點(diǎn)。
var timeChunk = function(ary, fn, count) { var t; var start = function() { for ( var i = 0; i < Math.min( count || 1, ary.length ); i++ ){ var obj = ary.shift(); fn( obj ); } }; return function() { t = setInterval(function() { if (ary.length === 0) { // 如果全部節(jié)點(diǎn)都已經(jīng)被創(chuàng)建好 return clearInterval(t); } start(); }, 200); // 分批執(zhí)行的時(shí)間間隔,也可以用參數(shù)的形式傳入 }; };5. 惰性加載
在Web開(kāi)發(fā)中,因?yàn)闉g覽器之間的實(shí)現(xiàn)差異,一些嗅探工作總是不可避免。比如我們需要一個(gè)在各個(gè)瀏覽器中能夠通用的事件綁定函數(shù)addEvent,常見(jiàn)的寫(xiě)法如下:
方案一:
var addEvent = function(elem, type, handler) { if (window.addEventListener) { return elem.addEventListener(type, handler, false) } if (window.attachEvent) { return elem.attachEvent("on" + type, handler) } }
缺點(diǎn):當(dāng)它每次被調(diào)用的時(shí)候都會(huì)執(zhí)行里面的if條件分支,雖然執(zhí)行這些if分支的開(kāi)銷(xiāo)不算大,但也許有一些方法可以讓程序避免這些重復(fù)的執(zhí)行過(guò)程。
方案二:
var addEvent = (function() { if (window.addEventListener) { return function(elem, type, handler) { elem.addEventListener(type, handler, false) } } if (window.attachEvent) { return function(elem, type, handler) { elem.attachEvent("on" + type, handler) } } })()
缺點(diǎn):也許我們從頭到尾都沒(méi)有使用過(guò)addEvent函數(shù),這樣看來(lái),一開(kāi)始的瀏覽器嗅探就是完全多余的操作,而且這也會(huì)稍稍延長(zhǎng)頁(yè)面ready的時(shí)間。
方案三:
var addEvent = function(elem, type, handler) { if (window.addEventListener) { addEvent = function(elem, type, handler) { elem.addEventListener(type, handler, false) } } else if (window.attachEvent) { addEvent = function(elem, type, handler) { elem.attachEvent("on" + type, handler) } } addEvent(elem, type, handler) }
此時(shí)addEvent依然被聲明為一個(gè)普通函數(shù),在函數(shù)里依然有一些分支判斷。但是在第一次進(jìn)入條件分支之后,在函數(shù)內(nèi)部會(huì)重寫(xiě)這個(gè)函數(shù),重寫(xiě)之后的函數(shù)就是我們期望的addEvent函數(shù),在下一次進(jìn)入addEvent函數(shù)的時(shí)候,addEvent函數(shù)里不再存在條件分支語(yǔ)句。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/99146.html
摘要:引言本期開(kāi)始介紹中的高階函數(shù),在中,函數(shù)是一種特殊類(lèi)型的對(duì)象,它們是。簡(jiǎn)單來(lái)說(shuō),高階函數(shù)是一個(gè)接收函數(shù)作為參數(shù)傳遞或者將函數(shù)作為返回值輸出的函數(shù)。我們來(lái)看看使用它們與不使用高階函數(shù)的方案對(duì)比。引言 本期開(kāi)始介紹 JavaScript 中的高階函數(shù),在 JavaScript 中,函數(shù)是一種特殊類(lèi)型的對(duì)象,它們是 Function objects。那什么是高階函數(shù)呢?本節(jié)將通過(guò)高階函數(shù)的定義來(lái)展...
摘要:高階函數(shù)接受和或返回另外一個(gè)函數(shù)的函數(shù)被稱(chēng)為高階函數(shù)。之所以是高階,是因?yàn)樗⒎亲址當(dāng)?shù)字或布爾值,而是從更高層次來(lái)操作函數(shù)。更高的可重用性高階函數(shù)的最大好處可能是更高的可重用性。如果沒(méi)有高階函數(shù),我們需要用循環(huán)來(lái)模仿的功能。 翻譯:瘋狂的技術(shù)宅原文:https://medium.freecodecamp.o... 本文首發(fā)微信公眾號(hào):jingchengyideng歡迎關(guān)注,每天都...
摘要:原文高階函數(shù)在中高階函數(shù)實(shí)際上就是控制函數(shù)的函數(shù),有別于普通函數(shù)傳遞變量,高階函數(shù)傳遞的是函數(shù),并且輸出函數(shù)這對(duì)于初學(xué)者來(lái)說(shuō)足夠燒腦,也足夠驚艷。初識(shí)時(shí)常常會(huì)被被震撼了,原來(lái)函數(shù)還可以這么用這是設(shè)計(jì)模式與開(kāi)發(fā)實(shí)踐的單例模式的一個(gè)高階函數(shù)。 原文 高階函數(shù) 在javascript中高階函數(shù)實(shí)際上就是控制函數(shù)的函數(shù),有別于普通函數(shù)傳遞變量,高階函數(shù)傳遞的是函數(shù),并且輸出函數(shù) 這對(duì)于js初學(xué)...
摘要:前言函數(shù)式編程在前端已經(jīng)成為了一個(gè)非常熱門(mén)的話(huà)題。整個(gè)過(guò)程就是體現(xiàn)了函數(shù)式編程的核心思想通過(guò)函數(shù)對(duì)數(shù)據(jù)進(jìn)行轉(zhuǎn)換。高階函數(shù)函數(shù)式編程傾向于復(fù)用一組通用的函數(shù)功能來(lái)處理數(shù)據(jù),它通過(guò)使用高階函數(shù)來(lái)實(shí)現(xiàn)。 前言 函數(shù)式編程在前端已經(jīng)成為了一個(gè)非常熱門(mén)的話(huà)題。在最近幾年里,我們看到非常多的應(yīng)用程序代碼庫(kù)里大量使用著函數(shù)式編程思想。 本文將略去那些晦澀難懂的概念介紹,重點(diǎn)展示在 JavaScrip...
摘要:和類(lèi)在開(kāi)始時(shí)遇到類(lèi)組件,只是需要有關(guān)類(lèi)的基礎(chǔ)。畢竟,中的條件呈現(xiàn)僅再次顯示大多數(shù)是而不是特定的任何內(nèi)容。 在我的研討會(huì)期間,更多的材料是關(guān)于JavaScript而不是React。其中大部分歸結(jié)為JavaScript ES6以及功能和語(yǔ)法,但也包括三元運(yùn)算符,語(yǔ)言中的簡(jiǎn)寫(xiě)版本,此對(duì)象,JavaScript內(nèi)置函數(shù)(map,reduce,filter)或更常識(shí)性的概念,如:可組合性,可重用...
閱讀 1391·2021-09-22 10:02
閱讀 1914·2021-09-08 09:35
閱讀 4065·2021-08-12 13:29
閱讀 2611·2019-08-30 15:55
閱讀 2266·2019-08-30 15:53
閱讀 2305·2019-08-29 17:13
閱讀 2766·2019-08-29 16:31
閱讀 2957·2019-08-29 12:24