Javascript函數(shù)緩存的實(shí)現(xiàn)及應(yīng)用場(chǎng)景
一、是什么
函數(shù)緩存,就是將函數(shù)運(yùn)算過(guò)的結(jié)果進(jìn)行緩存
本質(zhì)上就是用空間(緩存存儲(chǔ))換時(shí)間(計(jì)算過(guò)程)
常用于緩存數(shù)據(jù)計(jì)算結(jié)果和緩存對(duì)象
const add = (a,b) => a+b; const calc = memoize(add); // 函數(shù)緩存 calc(10,20);// 30 calc(10,20);// 30 緩存
緩存只是一個(gè)臨時(shí)的數(shù)據(jù)存儲(chǔ),它保存數(shù)據(jù),以便將來(lái)對(duì)該數(shù)據(jù)的請(qǐng)求能夠更快地得到處理
二、如何實(shí)現(xiàn)
實(shí)現(xiàn)函數(shù)緩存主要依靠閉包、柯里化、高階函數(shù),這里再簡(jiǎn)單復(fù)習(xí)下:
閉包
閉包可以理解成,函數(shù) + 函數(shù)體內(nèi)可訪問(wèn)的變量總和
(function() { var a = 1; function add() { const b = 2 let sum = b + a console.log(sum); // 3 } add() })()
add
函數(shù)本身,以及其內(nèi)部可訪問(wèn)的變量,即 a = 1
,這兩個(gè)組合在?起就形成了閉包
柯里化
把接受多個(gè)參數(shù)的函數(shù)轉(zhuǎn)換成接受一個(gè)單一參數(shù)的函數(shù)
// 非函數(shù)柯里化 var add = function (x,y) { return x+y; } add(3,4) //7 ? // 函數(shù)柯里化 var add2 = function (x) { //**返回函數(shù)** return function (y) { return x+y; } } add2(3)(4) //7
將一個(gè)二元函數(shù)拆分成兩個(gè)一元函數(shù)
高階函數(shù)
通過(guò)接收其他函數(shù)作為參數(shù)或返回其他函數(shù)的函數(shù)
function foo(){ var a = 2; ? function bar() { console.log(a); } return bar; } var baz = foo(); baz();//2
函數(shù) foo
如何返回另一個(gè)函數(shù) bar
,baz
現(xiàn)在持有對(duì) foo
中定義的bar
函數(shù)的引用。由于閉包特性,a
的值能夠得到
下面再看看如何實(shí)現(xiàn)函數(shù)緩存,實(shí)現(xiàn)原理也很簡(jiǎn)單,把參數(shù)和對(duì)應(yīng)的結(jié)果數(shù)據(jù)存在一個(gè)對(duì)象中,調(diào)用時(shí)判斷參數(shù)對(duì)應(yīng)的數(shù)據(jù)是否存在,存在就返回對(duì)應(yīng)的結(jié)果數(shù)據(jù),否則就返回計(jì)算結(jié)果
如下所示
const memoize = function (func, content) { let cache = Object.create(null) content = content || this return (...key) => { if (!cache[key]) { cache[key] = func.apply(content, key) } return cache[key] } }
調(diào)用方式也很簡(jiǎn)單
const calc = memoize(add); const num1 = calc(100,200) const num2 = calc(100,200) // 緩存得到的結(jié)果
過(guò)程分析:
在當(dāng)前函數(shù)作用域定義了一個(gè)空對(duì)象,用于緩存運(yùn)行結(jié)果
運(yùn)用柯里化返回一個(gè)函數(shù),返回的函數(shù)由于閉包特性,可以訪問(wèn)到
cache
然后判斷輸入?yún)?shù)是不是在
cache
的中。如果已經(jīng)存在,直接返回cache
的內(nèi)容,如果沒(méi)有存在,使用函數(shù)func
對(duì)輸入?yún)?shù)求值,然后把結(jié)果存儲(chǔ)在cache
中
三、應(yīng)用場(chǎng)景
雖然使用緩存效率是非常高的,但并不是所有場(chǎng)景都適用,因此千萬(wàn)不要極端的將所有函數(shù)都添加緩存
以下幾種情況下,適合使用緩存:
對(duì)于昂貴的函數(shù)調(diào)用,執(zhí)行復(fù)雜計(jì)算的函數(shù)
對(duì)于具有有限且高度重復(fù)輸入范圍的函數(shù)
對(duì)于具有重復(fù)輸入值的遞歸函數(shù)
對(duì)于純函數(shù),即每次使用特定輸入調(diào)用時(shí)返回相同輸出的函數(shù)
new操作符具體干了什么?
一、是什么
在JavaScript
中,new
操作符用于創(chuàng)建一個(gè)給定構(gòu)造函數(shù)的實(shí)例對(duì)象
例子
function Person(name, age){ this.name = name; this.age = age; } Person.prototype.sayName = function () { console.log(this.name) } const person1 = new Person('Tom', 20) console.log(person1) // Person {name: "Tom", age: 20} t.sayName() // 'Tom'
從上面可以看到:
new
通過(guò)構(gòu)造函數(shù)Person
創(chuàng)建出來(lái)的實(shí)例可以訪問(wèn)到構(gòu)造函數(shù)中的屬性new
通過(guò)構(gòu)造函數(shù)Person
創(chuàng)建出來(lái)的實(shí)例可以訪問(wèn)到構(gòu)造函數(shù)原型鏈中的屬性(即實(shí)例與構(gòu)造函數(shù)通過(guò)原型鏈連接了起來(lái))
現(xiàn)在在構(gòu)建函數(shù)中顯式加上返回值,并且這個(gè)返回值是一個(gè)原始類型
function Test(name) { this.name = name return 1 } const t = new Test('xxx') console.log(t.name) // 'xxx' 可以發(fā)現(xiàn),構(gòu)造函數(shù)中返回一個(gè)原始值,然而這個(gè)返回值并沒(méi)有作用 下面在構(gòu)造函數(shù)中返回一個(gè)對(duì)象 function Test(name) { this.name = name console.log(this) // Test { name: 'xxx' } return { age: 26 } } const t = new Test('xxx') console.log(t) // { age: 26 } console.log(t.name) // 'undefined'
從上面可以發(fā)現(xiàn),構(gòu)造函數(shù)如果返回值為一個(gè)對(duì)象,那么這個(gè)返回值會(huì)被正常使用
二、流程
從上面介紹中,我們可以看到new
關(guān)鍵字主要做了以下的工作:
創(chuàng)建一個(gè)新的對(duì)象
obj
將對(duì)象與構(gòu)建函數(shù)通過(guò)原型鏈連接起來(lái)
將構(gòu)建函數(shù)中的
this
綁定到新建的對(duì)象obj
上根據(jù)構(gòu)建函數(shù)返回類型作判斷,如果是原始值則被忽略,如果是返回對(duì)象,需要正常處理
舉個(gè)例子:
function Person(name, age){ this.name = name; this.age = age; } const person1 = new Person('Tom', 20) console.log(person1) // Person {name: "Tom", age: 20} t.sayName() // 'Tom'
三、手寫(xiě)new操作符
現(xiàn)在我們已經(jīng)清楚地掌握了new
的執(zhí)行過(guò)程
那么我們就動(dòng)手來(lái)實(shí)現(xiàn)一下new
function mynew(Func, ...args) { // 1.創(chuàng)建一個(gè)新對(duì)象 const obj = {} // 2.新對(duì)象原型指向構(gòu)造函數(shù)原型對(duì)象 obj.__proto__ = Func.prototype // 3.將構(gòu)建函數(shù)的this指向新對(duì)象 let result = Func.apply(obj, args) // 4.根據(jù)返回值判斷 return result instanceof Object ? result : obj }
測(cè)試一下
function mynew(func, ...args) { const obj = {} obj.__proto__ = func.prototype let result = func.apply(obj, args) return result instanceof Object ? result : obj } function Person(name, age) { this.name = name; this.age = age; } Person.prototype.say = function () { console.log(this.name) } ? let p = mynew(Person, "huihui", 123) console.log(p) // Person {name: "huihui", age: 123} p.say() // huihui
可以發(fā)現(xiàn),代碼雖然很短,但是能夠模擬實(shí)現(xiàn)new
到此這篇關(guān)于Javascript函數(shù)緩存的實(shí)現(xiàn)及應(yīng)用場(chǎng)景的文章就介紹到這了,更多相關(guān)Javascript函數(shù)緩存內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于Bootstrap使用jQuery實(shí)現(xiàn)輸入框組input-group的添加與刪除
這篇文章主要介紹了基于Bootstrap使用jQuery實(shí)現(xiàn)輸入框組input-group的添加與刪除的相關(guān)資料,需要的朋友可以參考下2016-05-05JS代碼判斷IE6,IE7,IE8,IE9的函數(shù)代碼
JS代碼判斷瀏覽器版本,支持IE6,IE7,IE8,IE9!做網(wǎng)頁(yè)有時(shí)候會(huì)用到JS檢測(cè)IE的版本,下面是檢測(cè)Microsoft Internet Explorer版本的三種代碼2013-08-08layui實(shí)現(xiàn)點(diǎn)擊按鈕給table添加一行
想實(shí)現(xiàn)點(diǎn)擊按鈕在表格添加一行的功能,但發(fā)現(xiàn)layui并未集成該工具欄,因此,需要自己手動(dòng)添加這個(gè)功能;這篇文章主要介紹了layui點(diǎn)擊按鈕給table添加一行,需要的朋友可以參考下2018-08-08原生js+canvas實(shí)現(xiàn)驗(yàn)證碼
這篇文章主要為大家詳細(xì)介紹了原生js+canvas實(shí)現(xiàn)驗(yàn)證碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-11-11使用Chrome調(diào)試JavaScript的斷點(diǎn)設(shè)置和調(diào)試技巧
這篇文章主要介紹了使用Chrome調(diào)試JavaScript的斷點(diǎn)設(shè)置和調(diào)試技巧,需要的朋友可以參考下2014-12-12基于JS實(shí)現(xiàn)網(wǎng)頁(yè)中的選項(xiàng)卡(兩種方法)
這篇文章主要介紹了用js語(yǔ)句實(shí)現(xiàn)網(wǎng)頁(yè)中的選項(xiàng)卡(兩種方法),非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-06-06