JS专题系列之惰性函数与记忆函数

一、惰性函数

惰性函数:顾名思义就是懒惰的函数,举一个简单的例子,如果我们需要判断当前浏览器是哪一种浏览器,我们需要封装一个函数,根据判断来进行返回浏览器类型,但是如果你在同一个浏览器中调用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);
}

例:




    
    Document
    


    

二、记忆函数

如果熟悉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

总结:

使用场景:当有需要大量重复计算或者计算依赖之前的结果的函数,我们都可以使用记忆函数。

你可能感兴趣的:(JS专题系列之惰性函数与记忆函数)