網(wǎng)站首頁 編程語言 正文
柯里化函數(shù):
編程思想,函數(shù)執(zhí)行,產(chǎn)生一個閉包,(不被釋放的上下文)把一些信息"預(yù)先存儲起來",目的是提供下級上下文中調(diào)使用,這樣的預(yù)先存儲思想,就叫做柯理化函數(shù)編程思想
作用域 vs上下文:
- 都是函數(shù)執(zhí)行,在棧內(nèi)存中所分配出來的空間
- 創(chuàng)建函數(shù)的時候,在那個上下文中創(chuàng)建的,那么其作用域就是誰(作用域不是函數(shù)自己執(zhí)行產(chǎn)生的這個空間,而是創(chuàng)建函數(shù)所在的這個空間)
- 而上下文是函數(shù)自己執(zhí)行產(chǎn)生的==>函數(shù)執(zhí)行產(chǎn)生的私有上下文,它的上級上下文是它的作用域
應(yīng)用場景
const fn = function fn(...params) {
// params[1,2]
return function proxy(...args) {
// args[3]
return params.concat(args).reduce((x, item) => x + item)
}
}
let res = fn(1, 2)(3)//fn(1,2)-->proxy(3)
console.log(res);
?了解:新版瀏覽器無法實現(xiàn)
add一直持續(xù)執(zhí)行,執(zhí)行幾次是不確定的,我們最后需要把每一次執(zhí)行的值累加求和?
在瀏覽器沒有生級之前console.log(函數(shù))會把函數(shù)轉(zhuǎn)換為字符串輸出
函數(shù)(Symbol.toPrimitive)-->函數(shù).valueof()--->函數(shù).tostring,但凡其中有一項返回了對象的值則控制臺以返回輸出為主,但是升級之后,轉(zhuǎn)換為字符串的操作還會觸發(fā),但是控制臺最后呈現(xiàn)的還是函數(shù)
const curring = function curring() {
let params = [];
const add = (...args) => {
params = params.concat(args);
return add;
};
add[Symbol.toPrimitive] = () => params.reduce((result, item) => result + item);
return add;
};
let add = curring();
let res = add(1)(2)(3);
console.log(res); //->6
add = curring();
res = add(1, 2, 3)(4);
console.log(res); //->10
add = curring();
res = add(1)(2)(3)(4)(5);
console.log(res); //->15
函數(shù)式編程vs命令式編程
函數(shù)式編程:WHAT? 把具體執(zhí)行的步驟封裝到一個函數(shù)中,后期需要處理的時候,只要把函數(shù)執(zhí)行即可,我們不在關(guān)注執(zhí)行的步驟,只關(guān)注后期處理結(jié)果
- 優(yōu)點:低耦合好內(nèi)聚,快捷化開發(fā),方便維護
- 缺點:不能靈活掌握程序處理步驟,無法在某一個步驟做些特殊處理
命令式編程:HOW 更關(guān)注處理步驟,需要自己去實現(xiàn)每一步的操作
- 優(yōu)點:靈活想怎么處理就怎么處理
- 缺點:代碼冗余度高,開發(fā)效率慢
真實項目中推薦使用函數(shù)式編程
栗子
// forEach就是函數(shù)式編程,函數(shù)內(nèi)部實現(xiàn)了對數(shù)組迭代的封裝,每一次迭代都把回調(diào)函數(shù)執(zhí)行,并且把當前迭代這一項及索引傳遞過來
let arr = [10, 20, 30]
arr.forEach((item, index) => {
console.log(item, index);
});
// 命令式編程
for (let i = 0; i < arr.length; i++) {
console.log(arr[i], i);
}
//封裝forEach
Array.prototype.forEach = function (callback) {
//this--->要操作的數(shù)組
let self = this;
for (let i = 0; i < self.length; i++) {
callback(self[i], i)
}
}
面試題:foreach和for循環(huán)的區(qū)別
forEach是數(shù)組內(nèi)置的方法,內(nèi)部對數(shù)組迭代進行了封裝,直接使用就可以 函數(shù)式編程,不支持以任何形式中斷它的迭代;forEach內(nèi)部依次迭代每一項,每一次迭代把傳遞的回調(diào)函數(shù)執(zhí)行,把迭代的內(nèi)容及其索引傳遞給回調(diào)函數(shù)..直到這個數(shù)字迭代完畢
for循環(huán)是命令式編程,需要我們自己手動實現(xiàn)數(shù)組迭代,可以中斷,可以設(shè)置間隔幾個迭代
在項目開發(fā)的時候一般應(yīng)用的是forEach,這樣可以提高我的開發(fā)效率,減少代碼冗余,但是遇到一些靈活的代碼需求,則自己基于for循環(huán)操作
?組合函數(shù)
/*
在函數(shù)式編程當中有一個很重要的概念就是函數(shù)組合, 實際上就是把處理數(shù)據(jù)的函數(shù)像管道一樣連接起來, 然后讓數(shù)據(jù)穿過管道得到最終的結(jié)果。 例如:
const add1 = (x) => x + 1;
const mul3 = (x) => x * 3;
const div2 = (x) => x / 2;
div2(mul3(add1(add1(0)))); //=>3
?
而這樣的寫法可讀性明顯太差了,我們可以構(gòu)建一個compose函數(shù),它接受任意多個函數(shù)作為參數(shù)(這些函數(shù)都只接受一個參數(shù)),然后compose返回的也是一個函數(shù),達到以下的效果:
const operate = compose(div2, mul3, add1, add1)
operate(0) //=>相當于div2(mul3(add1(add1(0))))
operate(2) //=>相當于div2(mul3(add1(add1(2))))
?
簡而言之:compose可以把類似于f(g(h(x)))這種寫法簡化成compose(f, g, h)(x),請你完成 compose函數(shù)的編寫
*/
const add1 = x => x + 1;
const mul3 = x => x * 3;
const div2 = x => x / 2;
const compose = function compose(...funcs) {
//funcs:數(shù)組,一次存儲要執(zhí)行的函數(shù),而且從右到左的順序執(zhí)行
return function operate(x) {
let len = funcs.length
// x 函數(shù)執(zhí)行的初始值
if (len === 0) return x;//如果不傳初始值是啥返回啥
if (len === 1) return funs(0)(x)
return funcs.reduceRight((x, item) => {
return item(x)
}, x)
}
};
const operate = compose(div2, mul3, add1, add1)
console.log(operate(0));//=> 3 相當于div2(mul3(add1(add1(0))))
console.log(operate(2));//=>6
?惰性函數(shù):懶 執(zhí)行一次可以搞定,決對不會執(zhí)行第二次
獲取元素樣式:
- getComputedStyle() 方法用于獲取指定元素的 CSS 樣式。
- currentStyle ie678
- 獲取的樣式是元素在瀏覽器中最終渲染效果的樣式。
封裝兼容獲取樣式方法
瑕疵:瀏覽器沒換,頁面沒關(guān),第一次處理需要判斷兼容性是必須的,但是第二次即以后執(zhí)行,如果還是要判斷兼容性是沒必要的
var getCss = function (elem, attr) {
if (window.getComputedStyle) {
getCss = function (elem, attr) {
return window.getComputedStyle(elem)[attr];
}
} else {
getCss = function (elem, attr) {
return elem.currentStyle[attr]
};
}
return getCss(elem, attr)
}
console.log(getCss(document.body, "margin"));
console.log(getCss(document.body, "padding"));
,函數(shù)的防抖和節(jié)流
防抖
防抖:防止"老年帕金森",在用戶頻繁點擊某項操作的時候,我們只識別一次{自定義頻繁規(guī)則,自定義觸發(fā)邊界}
const submit = document.getElementById('submit')
//模擬服務(wù)器異步獲取數(shù)據(jù)的操作
const query = callback => {
setTimeout(() => {
callback({
code: 0,
msg: "success"
})
}, 1000);
}
submit.onclick = function () {
console.log('click,start');
query(value => {
console.log(value);
})
}
簡單版本的防抖處理
點擊按鈕執(zhí)行回調(diào)函數(shù)獲取數(shù)據(jù),在上一次請求沒有完成之前,在次點擊按鈕不在執(zhí)行callback獲取數(shù)據(jù) 設(shè)置標識
const submit = document.getElementById('submit')
let runing = false;//設(shè)置標識判斷
//點擊按鈕執(zhí)行回調(diào)函數(shù)獲取數(shù)據(jù),在上一次請求沒有完成之前,在次點擊按鈕不在執(zhí)行callback獲取數(shù)據(jù)
submit.onclick = function () {
if (runing) return;//如果true 就不在執(zhí)行
runing = true;//點擊之后把標識改為true
submit.disabled = true;//禁用按鈕
query(value => {
console.log(value);
runing = false;//當獲取數(shù)據(jù)之后,改為false
submit.disabled = false//恢復(fù)按鈕禁用
})
}
通用防抖方法的封裝處理?
思路:
- 假設(shè):300ms內(nèi)多次觸發(fā)事件,算做頻繁操作:
- 第一次點擊;等待300ms 設(shè)置定時器(執(zhí)行具體要做的事情)
- 第二次點擊:...
- 每一次點擊都設(shè)置一個定時器,等待300ms,300ms之后執(zhí)行要做的事情
- 如果定時器還沒有到時間,要做的事情還沒有干(300m以內(nèi))在次觸發(fā)這個事件,首先把之前設(shè)置的定時器清除掉,在重新設(shè)置一個新的
?掃盲:如何清除定時器
- timer是個數(shù)字 記錄第幾個定時器 例如1???clearTimeout(1/2 timer)清除第幾個定時器
- 問題:沒有任何的方法直接讓我們檢測這個定時器是否被清除掉,
- 所以我們每次設(shè)置定時器的時候,一般會把timer設(shè)置為null,設(shè)置定時器timer是個數(shù)字 代表定時器存在,清除定時器我們把timer也設(shè)置為null了,那以后我們只要判斷timer的值,只要這個值不是null,說明這個定時器沒有被清除掉,如果為null是被清除掉的
timer = setTimeout(() => {
func();
}, wait);
代碼
// submit.onclick =operate 瘋狂點擊,會觸發(fā)operate函數(shù),我們要做的事情就是保證func(真實要干的事情,只執(zhí)行一次)
const clearTimer = function clearTimer(timer) {
if (timer !== null) clearTimeout(timer)
return null
}
//不傳:debounce(函數(shù))-->debounce(函數(shù),300,false)
// debounce(函數(shù), 300)-->debounce(函數(shù),300,false)
// debounce(函數(shù),true)-->debounce(函數(shù),300,false)
const debounce = function debounce(func, wait, immediate) {
//func要做的事情 wait時間 事件綁定一定是函數(shù)
if (typeof func !== "function") throw new TypeError('func is not a function!')
if (typeof wait === "boolean") { immediate = wait; console.log(wait); };
if (typeof wait !== "number") { wait = 300; console.log(wait); }
if (typeof immediate !== "boolean") immediate = false;
let timer = null;
return function operate(...params) {
let now = !timer && immediate;
//清除之前設(shè)置的定時器,this-->submit
timer = clearTimer(timer)
timer = setTimeout(() => {
console.log(timer);
// 清除最后一次設(shè)置的定時器
timer = clearTimer(timer)
// 設(shè)置在結(jié)束邊界觸發(fā)
if (!immediate) func.apply(this, params)
}, wait);
// 設(shè)置開始邊界觸發(fā)
if (now) {
console.log('開始邊界', now); return func.apply(this, params)
}
}
}
//模擬服務(wù)器異步獲取數(shù)據(jù)的操作
const query = callback => {
setTimeout(() => {
callback({
code: 0,
msg: "success"
})
}, 1000);
}
const submit = document.getElementById('submit')
submit.onclick = debounce(function (ev) {
console.log("click start", ev);
console.log(this);
query(value => {
console.log(value);
})
})
節(jié)流
??window.onscroll//監(jiān)聽瀏覽器滾動條事件(默認觸發(fā)頻率 按照瀏覽器最快反應(yīng)時間)(谷歌5-7ms觸發(fā),這樣的觸發(fā)頻率太快了,非常消耗性能)
節(jié)流:'降頻',在用戶頻繁點擊某項操作的時候,我們降低默認的觸發(fā)頻率
思路window.οnscrοll=throttle 滾動過程中,operate函數(shù)會間隔 5ms觸發(fā)一次,而我們編寫throttle方法的目的,是operate執(zhí)行的過程中,控制要真正執(zhí)行的func,間隔300ms觸發(fā)一次
- 第一次滾動,記錄當前觸發(fā)的時間,@A
- 間隔5ms第二次觸發(fā)的時候,拿當前觸發(fā)時間@A,和300做對比,發(fā)現(xiàn)還有295ms才應(yīng)該去做func這件事,設(shè)置一個定時器
- 第三次及其以后觸發(fā),如果發(fā)現(xiàn)目前已經(jīng)有一個等待執(zhí)行的定時器,就啥也不干即可...,
- 直到上一次設(shè)置的定時器已經(jīng)執(zhí)行(兩次間隔的時間超過300了),定時器已經(jīng)被清掉了,此時我們在次設(shè)置一個定時器,還是讓其等待300ms之后執(zhí)行
- 特殊情況 發(fā)現(xiàn)兩次時間差超過300,此時立即觸發(fā)
代碼
const clearTimer = function clearTimer(timer) {
if (timer !== null) clearTimeout(timer)
return null
}
const throttle = function throttle(func, wait) {
if (typeof func !== "function") throw new TypeError('func is not a function!')
if (typeof wait === "boolean") { immediate = wait };
let timer = null,
previous = 0;//記錄上一次觸發(fā)的時間
return function operate(...params) {
let now = +new Date(),//當前時間
remaining = wait - (now - previous)//兩次觸發(fā)的間隔時間跟300做對比
if (remaining <= 0) {
//說明兩次操作的間隔時間大于300的,此時立即觸發(fā)執(zhí)行
timer = clearTimer(timer)
previous = +new Date;//把觸發(fā)時間記錄下來
func.apply(this, params)
}
//間隔時間差不足300,如果還沒有設(shè)置過定時器,則直接設(shè)置定時等著執(zhí)行,如果已經(jīng)設(shè)置過定時器啥事都不用干
if (!timer) {
timer = setTimeout(() => {
//清除定時器,保證每次定時器執(zhí)行完為null
timer = clearTimer(timer)
previous = +new Date;//把觸發(fā)時間記錄下來
func.apply(this, params)
}, remaining)
}
}
}
window.onscroll = throttle(function (ev) {
console.log("scrolling");
}, 300)
原文鏈接:https://blog.csdn.net/qq_63358859/article/details/122261894
相關(guān)推薦
- 2023-04-02 bash腳本截取字符串的兩種方法_linux shell
- 2022-03-16 Linux系統(tǒng)中日志詳細介紹_Linux
- 2023-01-01 MongoDB?Shell常用基本操作命令詳解_MongoDB
- 2022-12-21 詳解Golang中Channel的原理和使用技巧_Golang
- 2023-03-22 Python裝飾器使用方法全面梳理_python
- 2023-07-29 iview的表格實現(xiàn)單元格行編輯功能
- 2023-04-20 Error in mounted hook: “TypeError: Cannot read pro
- 2022-10-31 Golang?template?包基本原理分析_Golang
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支