个人理解,所谓惰性求值,就是将所有的方法用数组存储起来,遇到一个开始计算的标志时,再遍历数组,执行方法。
1.第一种方式:面向对象的方法
// 惰性求值
const _lazy = (function () {
const MAX_ARRAY_LENGTH = 100;
const LAZY_FILTER_FLAG = 1;
const LAZY_MAP_FLAG = 2;
LazyWrapper.prototype.filter = filter;
LazyWrapper.prototype.take = take;
LazyWrapper.prototype.value = lazyValue;
LazyWrapper.prototype.map = map;
function LazyWrapper(value) {
this.__wrapped__ = value; // 存储数据
this.__iteratees__ = []; // 存储方法,便于后期迭代操作
this.__takeCount__ = MAX_ARRAY_LENGTH;
}
function lazy(value) {
return new LazyWrapper(value);
}
function filter(iteratee) {
this.__iteratees__.push({
'iteratee': iteratee,
'type': LAZY_FILTER_FLAG
});
return this;
}
function map(iteratee) {
this.__iteratees__.push({
'iteratee': iteratee,
'type': LAZY_MAP_FLAG
});
return this;
}
function take(n) {
this.__takeCount__ = n;
return this;
}
function lazyValue() {
const takeCount = this.__takeCount__; // 需要得到的数据个数
const iteCount = this.__iteratees__; // 存储的方法个数
const handleValue = this.__wrapped__; // 待处理的数据
const result = []; // 存储符合要求的数据
let resIndex = 0;
checkj: // 语句标签
for (let i = resIndex; result.length < takeCount && i < handleValue.length; i++) {
const value = handleValue[i];
for (let j = 0; j < iteCount.length; j++) {
if (iteCount[j].type === 1) {
const computed = iteCount[j].iteratee(value); // 每个方法都要调用处理
if (!computed) { // 如果不符合要求,则继续下一个数据
continue checkj; // 退出当前循环
}
}
}
result[resIndex++] = value;
}
for (let j = 0; j < result.length; j++) {
const value = result[j];
for (let k = 0; k < iteCount.length; k++) {
if (iteCount[k].type === 2) {
result[j] = iteCount[k].iteratee(value);
}
}
}
return result;
}
return lazy;
})();
总结:
- 用立即执行函数,封装性更好,不污染全局作用域,同时,外界无法访问其内部方法。
- 利用了原型模式和构造函数模式。
2.第二种:es6的代理Proxy
实现函数名的链式使用
const pipe = (function () {
return function (value) {
const funcStack = [];
const oproxy = new Proxy({}, {
/**
*
* @param pipeObject 目标对象
* @param fnName 属性名
*/
get : function (pipeObject ,fnName) {
if (fnName === 'get') {
/**
* val 为累加器
*/
return funcStack.reduce((val, fn) => {
return fn(val);
}, value);
}
funcStack.push(window[fnName]);
return oproxy;
}
});
return oproxy;
}
}());
// 这里必须是var声明的变量,因为变量是可以挂载在window对象上的。
// 用es6的let和const,window[fnName]为undefined
var double = n => n * 2;
var pow = n => n * n;
var reverseInt = n => n.toString().split("").reverse().join("") | 0;
console.log(pipe(3).double.pow.reverseInt.get);
总结: