一、惰性函数
惰性函数:顾名思义就是懒惰的函数,举一个简单的例子,如果我们需要判断当前浏览器是哪一种浏览器,我们需要封装一个函数,根据判断来进行返回浏览器类型,但是如果你在同一个浏览器中调用2次这个函数,那么这个函数中就需要做2次判断。其实这是一种无用的性能消耗,因此我们的惰性函数就登场了
原理:根据第一次调用的判断重写当前函数
function getStyle(obj,attr){
if(obj.currentStyle) {
return obj.currentStyle[attr]
} else {
return getComputedStyle(obj,false)[attr];
}
}
上述方法我们知道用来获取元素的属性值,但是问题是我们每次调用该函数都要进行一次判断,如果在同一个浏览器中连续调用2次,每次都进行一次判断明显这是无用功,因此我们可以根据惰性函数原理进行优化
function getStyle(obj,attr){
if(obj.currentStyle) {
getStyle = function(obj,attr) {
return obj.currentStyle[attr]
}
} else {
getStyle = function(obj,attr) {
return getComputedStyle(obj,false)[attr];
}
}
return getStyle(obj,attr);
}
例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
#box{width: 200px;}
#box1{width:300px;}
</style>
</head>
<body>
<div id="box"></div>
<div id="box1"></div>
</body>
</html>
<script>
var div = document.getElementsByTagName("div")[0];
function getStyle(obj,attr){
if(obj.currentStyle){
getStyle = function(obj,attr){
return obj.currentStyle[attr]
}
}else{
getStyle = function(obj,attr){
return getComputedStyle(obj,false)[attr]
}
}
return getStyle(obj,attr)
}
console.log(getStyle)
/*
function getStyle(obj,attr){
if(obj.currentStyle){
getStyle = function(obj,attr){
return obj.currentStyle[attr]
}
}else{
getStyle = function(obj,attr){
return getComputedStyle(obj,false)[attr]
}
}
return getStyle(obj,attr)
}
*/
console.log(getStyle(box,"width"))
console.log(getStyle)
/*
chrome浏览器
(obj,attr){
return getComputedStyle(obj,false)[attr]
}
*/
</script>
二、记忆函数
如果熟悉Vue的同学应该知道vue中有一个属性叫做
computed
它可以缓存计算后的结果。记忆函数的功能也类似于computed
。它可以将上次的结果缓存起来,当下次调用的时候,如果传递的参数相同,则直接返回缓存中的数据
举个例子:
function add(a, b) {
return a + b;
}
// 假设 memoize 可以实现函数记忆
var memoizedAdd = memoize(add);
memoizedAdd(1, 2) // 3
memoizedAdd(1, 2) // 相同的参数,第二次调用时,从缓存中取出数据,而非重新计算一次
原理
我们只需要把参数和对应的结果存到一个对象中,调用时,判断参数对应的数据是否存在,如果存在则直接返回对应的结果
第一版
function memoize(f) {
var cache = {};
return function(){
var key = arguments.length + Array.prototype.join.call(arguments, ",");
if (key in cache) {
return cache[key]
}
else {
return cache[key] = f.apply(this, arguments)
}
}
}
// 调用
var add = function(a, b, c) {
return a + b + c
}
var memoizedAdd = memoize(add)
memoizedAdd(1, 2, 3) // 5
因为第一版使用了
join
方法,我们很容易想到当参数是对象的时候,就会自动调用toString
方法转换成[Object object]
,再拼接字符串作为 key 值。这样就很容易造成key值相同
var propValue = function(obj){
return obj.value
}
var memoizedAdd = memoize(propValue)
console.log(memoizedAdd({value: 1})) // 1
console.log(memoizedAdd({value: 2})) // 1
两者都返回了 1,显然是有问题的,所以我们看看 underscore 的 memoize 函数是如何实现的:
// 第二版 (来自 underscore 的实现)
var memoize = function(func, hasher) {
var memoize = function(key) {
var cache = memoize.cache;
var address = '' + (hasher ? hasher.apply(this, arguments) : key);
if (!cache[address]) {
cache[address] = func.apply(this, arguments);
}
return cache[address];
};
memoize.cache = {};
return memoize;
};
可以看出
underscore
默认使用 function 的第一个参数作为 key,所以如果直接使用
var add = function(a, b, c) {
return a + b + c
}
var memoizedAdd = memoize(add)
memoizedAdd(1, 2, 3) // 6
memoizedAdd(1, 2, 4) // 6
肯定是有问题的,如果要支持多参数,我们就需要传入 hasher 函数,自定义存储的 key 值。所以我们考虑使用 JSON.stringify:
var memoizedAdd = memoize(add, function(){
var args = Array.prototype.slice.call(arguments)
return JSON.stringify(args)
})
console.log(memoizedAdd(1, 2, 3)) // 6
console.log(memoizedAdd(1, 2, 4)) // 7
总结:
使用场景:当有需要大量重复计算或者计算依赖之前的结果的函数,我们都可以使用记忆函数。