Loading

js-apply

包装函数:"apply"

我们也可以将代理(proxy)包装在函数周围。
apply(target, thisArg, args) 捕捉器能使代理以函数的方式被调用:

target 是目标对象(在 JavaScript 中,函数就是一个对象),
thisArg 是 this 的值。
args 是参数列表。

例如,让我们回忆一下我们在 装饰者模式和转发,call/apply 一章中所讲的 delay(f, ms) 装饰器。

在该章中,我们没有用 proxy 来实现它。调用 delay(f, ms) 会返回一个函数,该函数会在 ms 毫秒后把所有调用转发给 f。

这是以前的基于函数的实现:

function delay(f, ms) {
  // 返回一个包装器(wrapper),该包装器将在时间到了的时候将调用转发给函数 f
  return function() { // (*)
    setTimeout(() => f.apply(this, arguments), ms);
  };
}

function sayHi(user) {
  alert(`Hello, ${user}!`);
}

// 在进行这个包装后,sayHi 函数会被延迟 3 秒后被调用
sayHi = delay(sayHi, 3000);

sayHi("John"); // Hello, John! (after 3 seconds)

正如我们所看到的那样,大多数情况下它都是可行的。包装函数 (*) 在到达延迟的时间后后执行调用。

但是包装函数不会转发属性读取/写入操作或者任何其他操作。进行包装后,就失去了对原始函数属性的访问,例如 name,length 和其他属性:

function delay(f, ms) {
  return function() {
    setTimeout(() => f.apply(this, arguments), ms);
  };
}

function sayHi(user) {
  alert(`Hello, ${user}!`);
}

alert(sayHi.length); // 1(函数的 length 是函数声明中的参数个数)

sayHi = delay(sayHi, 3000);

alert(sayHi.length); // 0(在包装器声明中,参数个数为 0)

Proxy 的功能要强大得多,因为它可以将所有东西转发到目标对象。

让我们使用 Proxy 来替换掉包装函数:

function delay(f, ms) {
  return new Proxy(f, {
    apply(target, thisArg, args) {
      setTimeout(() => target.apply(thisArg, args), ms);
    }
  });
}

function sayHi(user) {
  alert(`Hello, ${user}!`);
}

sayHi = delay(sayHi, 3000);

alert(sayHi.length); // 1 (*) proxy 将“获取 length”的操作转发给目标对象

sayHi("John"); // Hello, John!(3 秒后)

结果是相同的,但现在不仅仅调用,而且代理上的所有操作都能被转发到原始函数。所以在 (*) 行包装后的 sayHi.length 会返回正确的结果。

我们得到了一个“更丰富”的包装器。

还存在其他捕捉器:完整列表在本文的开头。它们的使用模式与上述类似。

posted @ 2020-11-13 13:40  戴杭林  阅读(226)  评论(0编辑  收藏  举报