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

169

我有一个函数a(),我想要覆盖它,但也希望根据上下文顺序执行原始的a()。例如,有时候在生成页面时,我想这样覆盖:

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

有时候是这样的:

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

在重载的a()函数中,如何获取original_a()函数?这个是否可能?

请不要建议其他覆盖方式,我知道很多种。我只是特别想了解这种方法。

12个回答

182

你可以像这样做:

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中。


27
像我这样的白痴需要注意结尾处的括号"()"——没有它,该函数会返回外部函数而不是内部函数 :) - Nerdmaster
1
@Nerdmaster 感谢您指出这一点。我添加了一条注释,希望能帮助人们注意到它。 - Matthew Crumley
5
哇,我尝试在没有命名空间的情况下做这个,结果导致堆栈溢出,哈哈。 - gosukiwi
我认为如果从函数中删除第一个和最后一个括号,结果将是相同的。就像这里 (functi..})。只要做 }(); 就会产生相同的结果,因为 (function{})() 第一组括号是用来声明匿名函数表达式的,你必须给它赋值。但是通过执行 (),你没有定义任何东西,只是返回一个函数。 - Muhammad Umer
1
@MuhammadUmer 你说得对,函数表达式周围的括号并不是必需的。我加上它们是因为如果没有它们,仅仅看前几行代码,就会(至少对我来说)感觉你正在将外部函数直接赋值给 a,而不是调用它并存储返回值。这些括号让我知道还有其他事情在发生。 - Matthew Crumley
无论我在做什么,当我从思维的轨道回来后,我都必须进行小型代码审查,弄清楚为什么这个函数被分配到这里,然后逐行阅读,才能想起来。 - Muhammad Umer

93

使用代理模式可能会帮助您:

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

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


11
确实。使用“apply”和“arguments”的方法使得它比其他答案更加健壮。 - Robert Levy
请注意,此模式不需要 jQuery —— 他们只是以 jQuery 的一个函数作为示例。 - Kev
在 String.prototype.match() 上使用这个会导致 NodeJS 在下一个 ' 符号处崩溃。 - user1944491

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),而是使用org_foo.call(this, args)。这可以保持this与窗口.foo正常调用(未代理)时一样。请参见此答案。 - cellepo

11

您可以使用以下结构覆盖函数:

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

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 吗? - Jonathan.

3

@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);

2
上面的例子没有正确地应用this或者正确地将arguments传递给函数覆盖(override)。Underscore _.wrap()对已有函数进行包装,正确地应用this和传递arguments。详情请参见:http://underscorejs.org/#wrap

2

我认为前面的答案不易读/不易维护,而其他答案没有正确地绑定上下文。这里提供了一个可读性较好的解决方案,使用ES6语法来解决这两个问题。

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

但是使用ES6语法会使其在使用上受到限制。你有一个可以在ES5中工作的版本吗? - eitch

0

所以我的答案最终成为了一种解决方案,使我可以使用_this变量指向原始对象。 我创建了一个新的“正方形”实例,但我讨厌“正方形”生成大小的方式。我认为它应该遵循我的特定需求。但是为了做到这一点,我需要让正方形具有更新的“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

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

然而,没有任何解决方案适用于我。

以下是在我的情况下有效的方法:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接