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