通过编程向JavaScript函数添加代码

126

我正在尝试在不修改原始JS代码的情况下自定义现有的JS库。该代码会加载几个外部JS文件,我可以访问这些文件,我想做的是更改原始文件中包含的一个函数,而不是将整个函数复制并粘贴到第二个JS文件中。
例如,被禁止的JS可能有一个像这样的函数:

var someFunction = function(){
    alert("done");
}

我想要能够将一些JS代码添加到该函数中,无论是作为开头还是结尾都可以。主要原因是在未经修改的JS代码中,该函数非常庞大,如果该JS代码得到更新,那么我覆盖它的函数将会过时。

我不确定这是否可能,但我想检查一下。


4
这个链接的第一个答案应该能帮到你。 - DarkAjax
你是否需要类似于回调函数的东西? - sinemetu1
6个回答

237
如果someFunction是全局可用的,那么你可以缓存该函数,创建自己的函数,并让自己的函数调用它。
所以如果这是原始内容...
someFunction = function() {
    alert("done");
}

你可以这样做...

someFunction = (function() {
    var cached_function = someFunction;

    return function() {
        // your code

        var result = cached_function.apply(this, arguments); // use .apply() to call it

        // more of your code

        return result;
    };
})();

这里是 JSFiddle


请注意我使用.apply来调用缓存函数。这让我保留了期望的this值,并将传入的任何参数作为单独的参数传递,无论有多少个参数。

12
这个回答真的应该排在第一位--它保留了所讨论函数的功能... +1 我赞成。 - Reid
20
使用apply加一分:这是唯一一个真正解决问题的答案。 - lonesomeday
2
@minitech:什么……你不熟悉JavaScript的“funciton”关键字吗?;) 感谢编辑。 - user1106925
1
@gdoron:我刚刚添加了一条评论。除非我现在真的很困惑,否则我不知道有任何浏览器不会接受实际的Arguments对象作为.apply()的第二个参数。但是如果它是类似数组的对象,例如jQuery对象,那么是的,某些浏览器会抛出错误。 - user1106925
2
这个答案恰好解决了使用实例化对象来控制通过YouTube Iframe API嵌入的各个YouTube视频的问题。你不知道我已经试图绕过API需要回调为单个全局函数的事实有多久了,而我正在尝试创建一个类以独特的模块化方式处理每个视频的数据。你是我今天的英雄。 - Carnix
显示剩余9条评论

34

首先将实际函数存储在一个变量中。

var oldFunction = someFunction;

然后定义你自己的:

someFunction = function(){
  // do something before
  oldFunction();
  // do something after
};

9
如果你的函数是一个方法,你需要使用 apply 来调用它。请参考 am 的回答。 - hugomg
它对我起作用,但是需要在上面的代码中将someFunction替换为window.someFunction。原因是我的函数是在jquery的$(document).ready()处理程序中声明的。 - Nelson

10

您可以创建一个调用您的代码并再次调用该函数的函数。

var old_someFunction = someFunction;
someFunction = function(){
    alert('Hello');
    old_someFunction();
    alert('Goodbye');
}

6

不确定您能否更新该函数,但根据其引用方式,您可以创建一个新函数来替换它:

var the_old_function = someFunction;
someFunction = function () {
    /* ..My new code... */
    the_old_function();
    /* ..More of my new code.. */
}

6
此外,如果您想更改本地上下文,则需要重新创建函数。例如:
var t = function() {
    var a = 1;
};

var z = function() {
    console.log(a);
};

现在
z() // => log: undefined

那么

var ts = t.toString(),
    zs = z.toString();

ts = ts.slice(ts.indexOf("{") + 1, ts.lastIndexOf("}"));
zs = zs.slice(zs.indexOf("{") + 1, zs.lastIndexOf("}"));

var z = new Function(ts + "\n" + zs);

并且

z() // => log: 1

但这只是最简单的例子。要处理参数、注释和返回值仍需要大量工作。此外,还有许多陷阱。
toString | slice | indexOf | lastIndexOf | new Function

2
最初的回答:代理模式(由user1106925使用)可以放在一个函数中。我下面写的这个函数适用于不在全局作用域中甚至适用于原型的函数。你可以像这样使用它:
extender(
  objectContainingFunction,
  nameOfFunctionToExtend,
  parameterlessFunctionOfCodeToPrepend,
  parameterlessFunctionOfCodeToAppend
)

在下面的代码片段中,您可以看到我使用该函数来扩展test.prototype.doIt()。原始答案被翻译成"最初的回答"。

// allows you to prepend or append code to an existing function
function extender (container, funcName, prepend, append) {

    (function() {

        let proxied = container[funcName];

        container[funcName] = function() {
            if (prepend) prepend.apply( this );
            let result = proxied.apply( this, arguments );
            if (append) append.apply( this );
            return result;
        };

    })();

}

// class we're going to want to test our extender on
class test {
    constructor() {
        this.x = 'instance val';
    }
    doIt (message) {
        console.log(`logged: ${message}`);
        return `returned: ${message}`;
    }
}

// extends test.prototype.doIt()
// (you could also just extend the instance below if desired)
extender(
    test.prototype, 
    'doIt', 
    function () { console.log(`prepended: ${this.x}`) },
    function () { console.log(`appended: ${this.x}`) }
);

// See if the prepended and appended code runs
let tval = new test().doIt('log this');
console.log(tval);


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