(JavaScript)装饰器模式和转发(透明缓存、“func.call”函数、如何传递多个参数、func.apply)

透明缓存

利用装饰者模式给一个函数添加缓存

function slow(x) {
  // 这里可能会有重负载的 CPU 密集型工作
  alert(`Called with ${x}`);
  return x;
}

//缓存装饰器:将函数func运算结果添加到缓存中
function cachingDecorator(func) {
  let cache = new Map();

  return function(x) {
    if (cache.has(x)) {    
      return cache.get(x); 
    }

    let result = func(x);  

    cache.set(x, result);  
    return result;
  };
}

slow = cachingDecorator(slow);

//1不会和装饰器中的形参匹配,直接保存在arguments中,和java有区别
alert( slow(1) ); // slow(1) 被缓存下来了
alert( "Again: " + slow(1) ); // 一样的

alert( slow(2) ); // slow(2) 被缓存下来了
alert( "Again: " + slow(2) ); // 和前面一行结果相同

“func.call”函数

// 我们将对 worker.slow 的结果进行缓存
let worker = {
  someMethod() {
    return 1;
  },

  slow(x) {
    return x * this.someMethod(); 
  }
};

// 缓存装饰器
function cachingDecorator(func) {
  let cache = new Map();
  return function(x) {
    if (cache.has(x)) {
      return cache.get(x);
    }
    let result = func(x); 
    cache.set(x, result);
    return result;
  };
}

alert( worker.slow(1) ); // 原始方法有效

worker.slow = cachingDecorator(worker.slow); // 现在对其进行缓存

alert( worker.slow(2) ); // Error: Cannot read property 'someMethod' of undefined

和下面代码情况类似:

let func = worker.slow;
func(2);

包装器将调用传递给原始方法,但没有上下文 this。因此,发生了错误。


解决方案:
//第一个参数作为this,后面的作为参数,func是需要调用call()方法的函数
func.call(context, arg1, arg2, ...)
代码:
let worker = {
  someMethod() {
    return 1;
  },

  slow(x) {
    return x * this.someMethod(); 
  }
};

function cachingDecorator(func) {
  let cache = new Map();
  return function(x) {
    if (cache.has(x)) {
      return cache.get(x);
    }
    let result = func.call(this, x); // 现在 "this" 被正确地传递了
    cache.set(x, result);
    return result;
  };
}

worker.slow = cachingDecorator(worker.slow); 

alert( worker.slow(2) ); // 工作正常
alert( worker.slow(2) ); // 工作正常,没有调用原始函数(使用的缓存)

如何传递多个参数:

map储存只能以key,value一对一形式储存,也就是一个参数对应一个结果,如何储存多个参数呢?

解决方案:

1.实现一个新的(或使用第三方的)类似 map 的更通用并且允许多个键的数据结构。
2.使用嵌套 map:cache.set(min) 将是一个存储(键值)对 (max, result) 的 Map。所以我们可以使用 cache.get(min).get(max) 来获取 result。
3.将参数转化为字符串

第三种方式代码:
let worker = {
  slow(min, max) {
    return min + max;
  }
};

function cachingDecorator(func, hash) {
  let cache = new Map();
  return function() {
    let key = hash(arguments); //hash函数用来将参数转化为字符串
    if (cache.has(key)) {
      return cache.get(key);
    }

    let result = func.call(this, ...arguments); // (**)

    cache.set(key, result);
    return result;
  };
}

function hash(args) {
  return args[0] + ',' + args[1];
}

worker.slow = cachingDecorator(worker.slow, hash);

alert( worker.slow(3, 5) ); 
alert( "Again " + worker.slow(3, 5) ); // same (cached)

func.apply

func.apply(context, args)

call 和 apply 之间唯一的语法区别是,call 期望一个参数列表,而 apply 期望一个包含这些参数的类数组对象。

因此,这两个调用几乎是等效的:


对hash函数进行改进

function hash(args) {
  return args.join();
}

这种方法不可以,因为arguments 对象既是可迭代对象又是类数组对象,但它并不是真正的数组。

function hash() {
  alert( [].join.call(arguments) ); // 1,2
}

hash(1, 2);

这个技巧称为方法借用
我们从常规数组 [].join 中获取(借用)join 方法,并使用 [].join.call 在 arguments 的上下文中运行它。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值