作用:同一个函数,在多次调用时,通过比较当前和上一次两者的参数是否相等来减少函数的请求次数,从而提高性能;
memoize-one其核心主要使用闭包的工作原理,简单的说就是将内部的函数存在内存中 ,那我们就由简到难,通过举一个简单的例子来介绍memoize-one的使用场景,并逐步深入下去:
未使用memoize-one,called被打印了两次:
const canThrow = (name) => {
console.log('called'); // 执行了两次
return { name };
};
const value1 = canThrow('Alex');
const value2 = canThrow('Alex');
使用memoize-one,called只打印了一次:
const canThrow = (name) => {
console.log('called'); // 执行了两次
return { name };
};
const memoized = memoizeOne(canThrow);
const value1 = canThrow('Alex');
const value2 = canThrow('Alex');
下面我们看一下memoize-one的源码:
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global = global || self, global.memoizeOne = factory());
}(this, function () { 'use strict';
// 浅比较两次调用的参数
function areInputsEqual(newInputs, lastInputs) {
// 判断两者长度[入参的个数]是否相等,如果不等的话,则返回false, 就没必要再继续比较了
if (newInputs.length !== lastInputs.length) {
return false;
}
// 判断两次入参[一一对应]的值是否相等,如果有一个不相等,证明入参是不一样的,返回false
for (var i = 0; i < newInputs.length; i++) {
if (newInputs[i] !== lastInputs[i]) {
return false;
}
}
return true;
}
function memoizeOne(resultFn, isEqual) {
/**
* @param {function} isEqual
* isEqual可以是一些深比较的方法,比如lodash.isequal
*/
// isEqual判断isEqual是否有传, 未传使用 areInputsEqual
if (isEqual === void 0) { isEqual = areInputsEqual; }
var lastThis;
var lastArgs = [];
var lastResult;
var calledOnce = false;
function memoized() {
var newArgs = [];
// 将arguments遍历复制给一个新的变量
for (var _i = 0; _i < arguments.length; _i++) {
newArgs[_i] = arguments[_i];
}
// 1. calledOnce、lastThis目的是判断memoized方法是第二次(多次)被调用 2. isEqual(newArgs, lastArgs)判断入参如果相等的话,就没必要再去调用resultFn这个函数了
if (calledOnce && lastThis === this && isEqual(newArgs, lastArgs)) {
return lastResult;
}
// 将参数整合array, 再通过apply去调用函数
lastResult = resultFn.apply(this, newArgs);
calledOnce = true;
lastThis = this;
lastArgs = newArgs;
return lastResult;
}
return memoized;
}
return memoizeOne;
}));
首先memoize-one接受两个参数, 第一个参数是要执行的函数,第二个参数(非必填)可以是自定义比较值是否相等的一些方法;
1. 我们在通过memoizeOne(canThrow)调用时,其实返回的是一个未被执行的函数;
2. canThrow('Alex') 第一次被调用时,会通过resultFn.apply(this, newArgs) 得出lastResult的值返回出去;
3. canThrow('Alex') 第二次(多次)被调用时,
if (calledOnce && lastThis === this && isEqual(newArgs, lastArgs)) {
return lastResult;
}
这个判断条件会进行比较,如果条件成立,则证明当前和上一次的参数是一样的,就会把上一次的结果返回出去,而不执行resultFn这个函数;
这样我们就基本清楚了memoize-one的工作原理;