今天了解了js高阶使用之纯函数,以及loadsh的缓存原理,并且能够把他手写出来
ps:你看懂的只是你看懂的,你写出来的才是自己学会的
纯函数:一句简单介绍的话就是:
不管在任何时候使用这个函数,相同的参数执行这个函数会得到固定的结果
让我们先来看一个例子:
function add(num1, num2) {
return num1 + num2
}
var result = add(2, 4)
console.log(result) // 6
这是一个非常简单的计算加法的函数,同时,不管在今天还是明天,任何时候使用这个函数计算,并且参数是2和4,那他的结果就固定是6,那这个名为add的函数,就是一个纯函数
我们再来看看一个非纯函数的例子:
var num = 10
function sub(count) {
return num = num - count
}
console.log(sub(2)) // 8
console.log(sub(2)) // 6
console.log(sub(2)) // 4
通过打印可见,相同的sub(2)执行,返回的结果却每次不一样,这就与纯函数的概念不符,因此这个名为sub的函数是一个非纯函数
既然我们知道了纯函数是什么,那我们可以用他来做什么呢?
因为纯函数的特点相同的参数每次返回都是相同的值,那如果你多次用到这个函数,并且这个函数内部计算了上千行(略微夸张),那就很不值当了,因此缓存就出现了,下面我们来介绍一下loadsh的缓存:memoize
loadsh的安装我就不介绍了,可参考官网⬇
loadsh的配置使用sh的配置使用
使用例子:
const _ = require('loadsh')
// 求和
function add(num1, num2) {
console.log("开始计算")
return num1 + num2
}
const memoryGetAdd = _.memoize(add)
console.log(add(5, 6)) // 开始计算 11
console.log(add(5, 6)) // 开始计算 11
console.log(add(5, 6)) // 开始计算 11
console.log(memoryGetAdd(5, 6)) // 开始计算 11
console.log(memoryGetAdd(5, 6)) // 11
console.log(memoryGetAdd(5, 6)) // 11
如上图:如果我们没有使用loadsh处理过的函数add,会发现每次都打印了“开始计算”四个字,这是因为每次都进入函数进行了计算,但是如果使用了loadsh处理过的缓存函数memoryGetAdd去执行,我们就会发现处理第一次打印了“开始计算”,下面两次只打印了结果,这就证明了这个被处理过的函数直接返回了结果而不是通过计算完返回的结果
缓存可以很好的优化性能,那不是我们写的就不是我们自己的,接下来让我们手写一个一摸一样的
function Memorize(fn) {
let memory = {} // 定义一个缓存对象
return function () {
let key = JSON.stringify(fn) + JSON.stringify(arguments) // 确保唯一性,用函数作为缓存对象的键
memory[key] = memory[key] || fn.apply(fn, arguments) // 如果缓存过这个函数的结果就返回结果,否则就是第一次使用,那就计算并且保存
return memory[key]
}
}
function add(num1, num2) {
return num1 + num2
}
let newadd = Memorize(add)
console.log(newadd(5,10)) // 15
通过上图我们大致可以知道工作的原理
缓存就是运用了纯函数相同的参数返回的值永远不变的原理,保存到一个对象里,之后每次使用这个函数并且传参一致时直接返回值而不去进行计算
代码剖析:两个function先不执行,在Memorize(add)时,把add这个方法作为参数传给我们的Memorize方法,Memorize会返回一个新方法给newadd,在使用newadd(5,10)时,Memorize返回的新方法通过闭包访问了fn传参与缓存对象memory,通过查询之前有无缓存值来判断直接返回值还是进入fn函数执行
Memorize返回的新方法用到了 fn.apply(fn, arguments),是因为我们不知道传入的fn到底有需要参数,因此使用apply来传入所有参数arguments,第一个fn为改变this指向,我们不需要改变因此传入fn自身