【问题标题】:TS / JS: Overwrite a function and call original function withinTS / JS:覆盖函数并在其中调用原始函数
【发布时间】:2020-12-15 10:33:06
【问题描述】:

我想知道是否有办法修改 TypeScript 中的函数并访问其中的原始函数。这是我如何让它工作的一个例子:

let obj = {
  shout: () => {
    console.log("AHHHHH!");
  },
};

let s = obj.shout;

obj.shout = () => {
  console.log("I'm going to shout.");
  s();
};

obj.shout(); //-> "I'm going to shout", "AHHHHH!"

通过这种方式,我可以在呼喊函数被调用时为其添加警告 - 但我觉得这样做很丑陋,所以我想知道是否有更好的方法。

【问题讨论】:

  • s.call(obj)s.apply(obj),可能
  • 通常你也会关心传递正确的接收者 (this) 和所有参数到函数和 return 它的返回值,但如果你知道你不需要它们(在你的情况下,一个没有参数和没有返回值的箭头函数)那么这完全有效。这是正常的做法。
  • 您正在寻找的可以称为方法修改,您的第一种方法尽可能纯粹。考虑到 Bergi 的评论,人们可以考虑提供一些抽象,以涵盖拦截和更改方法的控制流(从而修改函数/方法)的最有用的情况,这是示例性完成的在我下面的答案中。

标签: javascript typescript methods metaprogramming modifier


【解决方案1】:

OP 的方法是最直观/最自然的方法。对于 JavaScript 应用程序,有时需要拦截和/或修改 控件流一个人不拥有或由于其他原因不允许触摸的功能。

对于这种情况,除了通过包装其原始实现来保留和更改此类逻辑之外别无他法。这种能力不是 JavaScript 独有的。通过反射Self-实现元编程的编程语言已有相当长的历史。修改

当然,人们可以/应该为人们可以想到的所有可能的修饰符用例提供防弹但方便的抽象......立即从最明显和最容易实现的 OP 用例开始,这可能由 before 修饰符处理 ...

const obj = {
  shout: function (...args) {

    console.log('AHHHHH!');

    console.log('args : ', args);
    console.log('this is me : ', this);
  }
};
obj.shout();


obj.shout = obj.shout.before(function () {

  console.log("I'm going to shout.");

}, obj);

obj.shout('test of "before" modifier');


const anotherOne = {
  name: 'anotherOne'
};
obj.shout.call(anotherOne, 'delegation and self reflection');


/*
console.log(
  'Object.getOwnPropertyDescriptors(Function.prototype) :',
  Object.getOwnPropertyDescriptors(Function.prototype)
);
*/
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script>
  (function (Function) {

    const fctPrototype = Function.prototype;
    const FUNCTION_TYPE = (typeof Function);

    function isFunction(type) {
      return (
           (typeof type == FUNCTION_TYPE)
        && (typeof type.call == FUNCTION_TYPE)
        && (typeof type.apply == FUNCTION_TYPE)
      );
    }
    function getSanitizedTarget(target) {
      return ((target != null) && target) || null;
    }

    function before/*Modifier*/(handler, target) {
      target = getSanitizedTarget(target);

      const proceed = this;
      return (

        isFunction(handler) &&
        isFunction(proceed) &&

        function () {
          const context = target || getSanitizedTarget(this);
          const args = arguments;

        //handler.apply(context, args);
          handler.call(context, args);

          return proceed.apply(context, args);
        }

      ) || proceed;
    }
    // before.toString = () => 'before() { [native code] }';

    Object.defineProperty(fctPrototype, 'before', {
      configurable: true,
      writable: true,
      value: before/*Modifier*/ 
    });

  }(Function));
</script>

从使用额外提供的实现的示例代码中可以看出,Function.prototype 非常适合在 JavaScript 中启用 方法修饰符

但无论如何/在何处实现此类修饰符功能,以下适用于任何可靠的修饰符代码(Bergi 已经指向它)...

  1. 修饰符必须支持target 对象(用于委派正确的this 上下文,如果需要)。
  2. 修饰符必须要么正确转发包装方法的参数,要么分别处理其返回值(调用失败的例外)。

我不介意有一天,JavaScript 会正式推出... Function.prototype[before|after||around|afterThrowing|afterFinally ].

【讨论】:

    【解决方案2】:

    当您将新函数分配给现有对象时,您会丢失对旧值的引用。

    但是如果你使用类,你确实可以通过super 访问重载方法。

    class ObjA {
        shout() {
            console.log('AHHHHH!')
        }
    }
    
    class ObjB extends ObjA {
        shout() {
            super.shout()
            console.log("I'm going to shout.")
        }
    }
    
    const obj = new ObjB()
    obj.shout() //-> "I'm going to shout", "AHHHHH!"
    

    【讨论】:

      猜你喜欢
      • 2012-05-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-01
      • 2010-09-22
      • 1970-01-01
      • 2010-10-25
      相关资源
      最近更新 更多