"個人博客" 概念 《javascript設計模式和開發實踐》中定義 函數既可作為參數被傳遞,也可以作為返回值輸出 滿足以下條件: 1. 接受一個或多個函數作為輸入 2. 輸出一個函數 高階函數一般是那些函數型包含多於函數。在函數式編程中,返回另一個函數的高階函數被稱為Curry化的函數。 函數作為 ...
概念
《javascript設計模式和開發實踐》中定義 函數既可作為參數被傳遞,也可以作為返回值輸出
滿足以下條件:
- 接受一個或多個函數作為輸入
- 輸出一個函數
高階函數一般是那些函數型包含多於函數。在函數式編程中,返回另一個函數的高階函數被稱為Curry化的函數。
函數作為參數傳遞
將函數作為參數傳遞,我們就可以抽離以部分容易變化的業務邏輯,這樣可以分離業務代碼中變與不變的部分
回調函數:
將函數傳進一個方法中,函數不會立即執行,等待出來結果之後在執行。
let func = function (callback){
if(n === 'owen'){
callback() //回調函數
}
}
function say (){
console.log('Hello Word')
}
func(say)
Array 對象常用的方法
[1,2,3,4].forEach(iteration)
function iteration(v){
console.log(v)
}
作為返回值輸出
讓函數繼續返回一個可執行的函數,意味著運行過程是可延續的。
判斷數據類型
let type = type =>{
return obj => Object.prototype.toString.call(obj) === `[object ${type}]`
}
let isArray = type('Array'),isString = type('String'),isNumber = type('Number'),isObject = type('Object');
// or
let Type = (function(){
let type = {},types = ['Object','Array','Number','String']
for (let val of types) {
(function(str){
type[`is${str}`] = (obj) => Object.prototype.toString.call( obj ) === `[object ${str}]`
}(val))
}
console.log(type)
return type
}())
Type.isNumber(2) // true
實現AOP(面向切片編程)
AOP 通過預編譯方式和運行期動態代理實現程式功能的統一維護的一種技術。
JAVA 語言中 AOP 將一些跟核心業務邏輯模塊無關的功能抽離出來,通常包括日誌統計、安全控制、異常處理燈。再通過“動態織入”的方式摻入業務邏輯中。
好處: 可以保持業務邏輯模塊的純凈和高內聚,方便復用日誌統計等功能模塊。
JavaScript中實現AOP是指把一個函數“動態織入”到另一個函數之中
具體實現:
Function.prototype.before = function(beforeFn){
let that = this; // 誰調用指向誰 下麵是由 func 函數調用所以是指向 func
return function( ...args){
beforeFn.apply(this,args) // 執行回調函數 beforeFn
return that.apply(this,args) // 執行原函數
}
}
Function.prototype.after = function(afterFn){
let that = this; // 誰調用指向誰 下麵是由befor函數調用所以是指向 befor
return function( ...args){
let ret = that.apply(this,args) // 執行並接收原對象
afterFn.apply(this,args) // 執行回調函數 beforeFn
return ret
}
}
var func = function (){
console.log(2)
}
func = func.before(function (){
console.log(1)
}).after(function (){
console.log(3)
})
func()
// 1 2 3
函數柯里化 (function currying)
在數學和電腦科學中,柯里化是將多個參數的函數轉換成一系列使用一個參數的函數,且返回接受餘下的參數的新函數
curring 又稱部分求值;一個 curring 函數首先會接收一些參數,該函數並不會立即求值,而是繼續返回另外一個函數,而剛傳入的參數會被保存在形成的閉包中,待函數真正需要求值的時候,之前的所以參數都會被一次性用於求值
簡單示例:
function add(a,b) {
return a + b
}
add(1,2) // 3
接下來使用 currying 實現一個幾天之內消費總和的函數
// 普通方法
var cost = (function() {
var args = [];
return function(){
if(!arguments.length){
let money = 0
for (let val of args ){
money += val;
}
return money
}else{
[].push.apply(args,arguments)
}
}
})()
cost(100);
cost(100);
cost(100);
cost(); // 300
// currying
/**
* 保存原函數參數返回到新函數中使用
*/
// func(100,100,100) //300
function count (...args){
let num = 0;
if(args.length>1){
for (let v of args){
num +=v
}
return num
}else{
return args[0]
}
}
var curry = function(func){
let args = []
return function fn(...Args){
if (Args.length){
[].push.apply(args,Args)
return fn
}else{
return func.apply(this,args)
}
}
}
cost = curry(count);
cost(100);
cost(100);
cost(100);
cost(); // 300
函數節流
JavaScript 中大多數情況都是用戶主動出發函數,除非函數本身的實現不合理,否則一般不會遇到跟性能相關的問題,少數情況下,函數不是由用戶直接觸發控制,可能被頻繁調用造成嚴重的性能問題。
比如:
window.addEventListener('resize', function(e) {
// do something...
});
window.addEventListener('scroll', function(e) {
// do something...
});
Dom.addEventListener('mousemove', function(e) {
// do something...
});
// progress
xhr.upload.addEventListener("progress", function(result) {
// do something...
}, false);
// ...
上述事件1秒種觸發很多次,並且常常操作DOM節點,非常損耗性能,瀏覽器會因此吃不消而卡頓;實際我們不需要觸發如此高的頻率因此我們可以在一段時間內忽略掉一些執行次數
節流原理:
如果持續觸發事件,可每隔一段時間只執行一次。
使用定時器實現節流
將即將被執行的函數用 setTimeout
函數延遲一段時間執行,如果該定時器未執行完成則忽略接下下來的需被執行的函數。
function throttle(func,wait) {
let timer, firstFlag = true; //第一次立即執行
return function(...args) {
if(timer) return false; // 如果存在定時器這不執行
let that = this;
if(firstFlag){
firstFlag = false;
return func.apply(that,args);
}
timer = setTimeout(function(){
clearTimeout(timer);
timer = null;
func.apply(that,args);
},wait)
}
}
window.addEventListener('scroll', throttle(function(e) {
console.log(e)
},1000));
函數防抖
和節流一定時間段內只調用一次事件處理函數不同,防抖是一定時間段內沒有再觸發事件,事件處理函數才會執行一次,如果設定的時間到來之前,又一次觸發了事件,就重新開始延時。(用戶不再觸發對應事件才執行一次事件)
function debounce(func,wait) {
let timer;
return function(...args) {
let that = this;
clearTimeout(timer);
timer = setTimeout(function(){
func.apply(that,args)
},wait)
}
}
window.addEventListener('scroll', debounce(function(e) {
console.log(e)
},1000));
個人博客
參考資料
《JavaScript設計模式與開發實踐》
JavaScript專題之跟著 underscore 學節流