引用原始函数时覆盖JavaScript函数


160

我有一个a()要覆盖的函数,但也有一个a()要根据上下文按顺序执行的功能。例如,有时在生成页面时,我会想要像这样覆盖:

function a() {
    new_code();
    original_a();
}

有时像这样:

function a() {
    original_a();
    other_new_code();
}

我如何original_a()从最重要的方面得到它a()?可能吗

我知道很多人都建议不要以这种方式替代替代。我在问这种方式。

Answers:


179

您可以执行以下操作:

var a = (function() {
    var original_a = a;

    if (condition) {
        return function() {
            new_code();
            original_a();
        }
    } else {
        return function() {
            original_a();
            other_new_code();
        }
    }
})();

original_a在匿名函数内部进行声明可以防止其混乱全局名称空间,但可以在内部函数中使用它。

就像评论中提到的Nerdmaster一样,一定要()在最后加上。您想调用外部函数并将结果(两个内部函数之一)a存储在中,而不是将外部函数本身存储在中a


25
对于像我这样的白痴,请特别注意结尾处的“()”,否则,它会返回外部函数,而不是内部函数:)
Nerdmaster 2012年

@Nerdmaster感谢您指出这一点。我添加了说明,希望可以帮助人们注意。
马修·克鲁姆利

5
哇,我试图在没有名称空间的情况下执行此操作,但出现堆栈溢出,哈哈。
gosukiwi 2012年

我认为如果从函数中删除第一个和最后一个括号,结果将是相同的。喜欢从这里(functi..})。这样做}();会产生相同的结果,因为使用了(function{})()第一组括号,因为您不能只声明匿名函数表达式而必须为其分配值。但是通过执行(),您并没有定义任何东西,只是返回一个函数。
穆罕默德·乌默

@MuhammadUmer您说对了,不需要在函数表达式两边加上括号。我将它们包括在内是因为,如果没有它们,如果仅看一下前几行,它(至少对我而言)看起来就像是将外部函数直接分配给a,而不是调用它并存储返回值。括号让我知道还有其他事情在发生。
马修·克鲁姆利

88

代理模式可以帮助你:

(function() {
    // log all calls to setArray
    var proxied = jQuery.fn.setArray;
    jQuery.fn.setArray = function() {
        console.log( this, arguments );
        return proxied.apply( this, arguments );
    };
})();

上面的代码将其包装在一个函数中,以隐藏“代理”变量。它将jQuery的setArray方法保存在一个闭包中并覆盖它。然后,代理将所有调用记录到该方法,并将该调用委托给原始方法。使用apply(this,arguments)可确保调用者无法注意到原始方法与代理方法之间的区别。


10
确实。'apply'和'arguments'的用法比其他答案更可靠
Robert Levy 2012年

请注意,此模式不需要jQuery,它们仅以jQuery函数之一为例。
凯夫

28

谢谢大家,代理模式确实有所帮助.....实际上,我想调用全局函数foo ..在某些页面中,我需要做一些检查。所以我做了以下。

//Saving the original func
var org_foo = window.foo;

//Assigning proxy fucnc
window.foo = function(args){
    //Performing checks
    if(checkCondition(args)){
        //Calling original funcs
        org_foo(args);
    }
};

谢谢,这真的帮助了我


3
遵循代理模式时,在某些情况下,实现此答案代码中未提供的详细信息将很重要:而不是org_foo(args)call org_foo.call(this, args)。这保持this了window.foo正常调用(未代理)时的状态。看到这个答案
cellepo '16

10

您可以使用类似以下的结构来覆盖函数:

function override(f, g) {
    return function() {
        return g(f);
    };
}

例如:

 a = override(a, function(original_a) {
      if (condition) { new_code(); original_a(); }
      else { original_a(); other_new_code(); }
 });

编辑:修正了一个错字。


+1这比其他代理模式示例更具可读性!
Mu Mind

1
...但是如果我没记错的话,这仅限于零参数函数,或者至少是预定数量的参数。有什么方法可以将其归纳为任意数量的参数而又不会失去可读性吗?
Mu Mind

@MuMind:是的,但是正确使用了代理模式-请参阅我的答案
Dan Dascalescu 2014年

4

传递任意参数:

a = override(a, function(original_a) {
    if (condition) { new_code(); original_a.apply(this, arguments) ; }
    else { original_a.apply(this, arguments); other_new_code(); }
});

1
original_a虽然不会引用参数吗?
乔纳森。

2

@Matthew Crumley提供的答案是利用立即调用的函数表达式,将较旧的“ a”函数关闭到返回函数的执行上下文中。我认为这是最好的答案,但就我个人而言,我更愿意将函数“ a”作为参数传递给IIFE。我认为这是可以理解的。

   var a = (function(original_a) {
        if (condition) {
            return function() {
                new_code();
                original_a();
            }
        } else {
            return function() {
                original_a();
                other_new_code();
            }
        }
    })(a);

1

上面的示例未正确应用thisarguments正确传递给函数重写。下划线_.wrap()包装现有函数,应用thisarguments正确传递。请参阅:http//underscorejs.org/#wrap


0

我有一些其他人编写的代码,想在代码中找不到的函数上添加一行。因此,作为一种解决方法,我想覆盖它。

但是,没有一种解决方案对我有用。

这是我的情况下有效的方法:

if (typeof originalFunction === "undefined") {
    originalFunction = targetFunction;
    targetFunction = function(x, y) {
        //Your code
        originalFunction(a, b);
        //Your Code
    };  
}

0

我为类似的情况创建了一个小助手,因为我经常需要覆盖多个库中的函数。该帮助器接受一个“命名空间”(函数容器),函数名称和重写函数。它将用新的命名空间替换所引用的命名空间中的原始函数。

新函数将原始函数作为第一个参数,并将原始函数作为其余参数。它将每次都保留上下文。它也支持void和non-void功能。

function overrideFunction(namespace, baseFuncName, func) {
    var originalFn = namespace[baseFuncName];
    namespace[baseFuncName] = function () {
        return func.apply(this, [originalFn.bind(this)].concat(Array.prototype.slice.call(arguments, 0)));
    };
}

在Bootstrap中的用法例如:

overrideFunction($.fn.popover.Constructor.prototype, 'leave', function(baseFn, obj) {
    // ... do stuff before base call
    baseFn(obj);
    // ... do stuff after base call
});

我没有创建任何性能测试。它可能会增加一些不必要的开销,根据情况的不同,开销可能很大,也可能不会很大。


0

在我看来,最重要的答案是不可读/不可维护的,其他答案不能正确地绑定上下文。这是使用ES6语法解决这两个问题的可读解决方案。

const orginial = someObject.foo;
someObject.foo = function() {
  if (condition) orginial.bind(this)(...arguments);
};

但是使用ES6语法使其在使用中受到很大限制。您是否有适用于ES5的版本?
eitch

0

因此,我的答案最终是一个解决方案,使我可以使用_this变量指向原始对象。我创建了一个“ Square”的新实例,但是我讨厌“ Square”生成其大小的方式。我认为它应该符合我的特定需求。但是,为了做到这一点,我需要正方形具有更新的“ GetSize”函数,该函数的内部调用该正方形中已经存在的其他函数,例如this.height,this.GetVolume()。但是为了做到这一点,我需要做到这一点而没有任何疯狂的破解。所以这是我的解决方案。

其他一些对象初始化器或帮助器函数。

this.viewer = new Autodesk.Viewing.Private.GuiViewer3D(
  this.viewerContainer)
var viewer = this.viewer;
viewer.updateToolbarButtons =  this.updateToolbarButtons(viewer);

在另一个对象中起作用。

updateToolbarButtons = function(viewer) {
  var _viewer = viewer;
  return function(width, height){ 
blah blah black sheep I can refer to this.anything();
}
};

0

不知道它是否在所有情况下都能正常工作,但是在我们的案例中,我们试图覆盖describeJest中的函数,以便我们可以解析名称,并在describe满足某些条件的情况下跳过整个块。

这是对我们有用的东西:

function describe( name, callback ) {
  if ( name.includes( "skip" ) )
    return this.describe.skip( name, callback );
  else
    return this.describe( name, callback );
}

这里至关重要的两件事:

  1. 我们不使用箭头功能() =>

    箭头函数会更改对的引用this,我们需要将其作为文件的this

  2. 使用this.describethis.describe.skip代替正义describedescribe.skip

再次重申,不确定是否对任何人有价值,但我们最初试图摆脱Matthew Crumley的 出色回答,但需要使我们的方法成为函数并接受参数,以便在条件条件下解析它们。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.