Javascript:扩展函数

IT技术 javascript function extend
2021-02-21 11:42:54

我想要它的主要原因是我想扩展我的初始化功能。

像这样的东西:

// main.js

window.onload = init();
function init(){
     doSomething();
}

// extend.js

function extends init(){
    doSomethingHereToo();
}

所以我想扩展一个函数,就像我在 PHP 中扩展一个类一样。

我也想从其他文件中扩展它,例如,我main.jsextended.js.

6个回答

通过更广泛地了解您实际尝试做什么以及您在做什么的背景下,我相信我们可以为您提供比问题的字面答案更好的答案

但这是一个字面的答案:

如果您将这些函数分配给某处的某个属性,则可以包装原始函数并将替换的函数放在该属性上:

// Original code in main.js
var theProperty = init;

function init(){
     doSomething();
}

// Extending it by replacing and wrapping, in extended.js
theProperty = (function(old) {
    function extendsInit() {
        old();
        doSomething();
    }

    return extendsInit;
})(theProperty);

如果您的函数还没有在对象上,您可能希望将它们放在那里以方便上述操作。例如:

// In main.js
var MyLibrary = {
    init: function init() {
    }
};

// In extended.js
(function() {
    var oldInit = MyLibrary.init;
    MyLibrary.init = extendedInit;
    function extendedInit() {
        oldInit.call(MyLibrary); // Use #call in case `init` uses `this`
        doSomething();
    }
})();

但是有更好的方法来做到这一点。例如,提供一种注册init函数的方法。

// In main.js
var MyLibrary = (function() {
    var initFunctions = [];
    return {
        init: function init() {
            var fns = initFunctions;
            initFunctions = undefined;
            for (var index = 0; index < fns.length; ++index) {
                try { fns[index](); } catch (e) { }
            }
        },
        addInitFunction: function addInitFunction(fn) {
            if (initFunctions) {
                // Init hasn't run yet, remember it
                initFunctions.push(fn);
            } else {
                // `init` has already run, call it almost immediately
                // but *asynchronously* (so the caller never sees the
                // call synchronously)
                setTimeout(fn, 0);
            }
        }
    };
})();

在 2020 年(或实际上是 2016 年之后的任何时间),可以写得更紧凑一点:

// In main.js
const MyLibrary = (() => {
    let initFunctions = [];
    return {
        init() {
            const fns = initFunctions;
            initFunctions = undefined;
            for (const fn of fns) {
                try { fn(); } catch (e) { }
            }
        },
        addInitFunction(fn) {
            if (initFunctions) {
                // Init hasn't run yet, remember it
                initFunctions.push(fn);
            } else {
                // `init` has already run, call it almost immediately
                // but *asynchronously* (so the caller never sees the
                // call synchronously)
                setTimeout(fn, 0);
                // Or: `Promise.resolve().then(() => fn());`
                // (Not `.then(fn)` just to avoid passing it an argument)
            }
        }
    };
})();
这对我帮助很大!谢谢
2021-05-13 11:42:54
谢谢你的好回答。我对第二个示例的问题是我可能需要我正在扩展的函数的结果。
2021-05-14 11:42:54

有几种方法可以解决这个问题,这取决于您的目的是什么,如果您只想在相同的上下文中执行该函数,您可以使用.apply()

function init(){
  doSomething();
}
function myFunc(){
  init.apply(this, arguments);
  doSomethingHereToo();
}

如果你想用更新的替换它init,它看起来像这样:

function init(){
  doSomething();
}
//anytime later
var old_init = init;
init = function() {
  old_init.apply(this, arguments);
  doSomethingHereToo();
};
有时您可能需要.call方法而不是.apply. 请参阅StackOverflow 问题。
2021-04-25 11:42:54
+1 谢谢。如果您想在不修改原始 js 的情况下修补某些 3rd 方插件,这真的很方便。
2021-04-27 11:42:54
不确定如何将它与需要参数和返回值的函数一起使用。
2021-04-29 11:42:54
@Nick,我发现您用于扩展现有函数的 JavaScript 示例非常有用,但我很好奇如何通过 jQuery 完成同样的事情?
2021-05-18 11:42:54

其他方法很棒,但它们不保留任何附加到 init 的原型函数。为了解决这个问题,您可以执行以下操作(灵感来自 Nick Craver 的帖子)。

(function () {
    var old_prototype = init.prototype;
    var old_init = init;
    init = function () {
        old_init.apply(this, arguments);
        // Do something extra
    };
    init.prototype = old_prototype;
}) ();

另一种选择可能是:

var initial = function() {
    console.log( 'initial function!' );
}

var iWantToExecuteThisOneToo = function () {
    console.log( 'the other function that i wanted to execute!' );
}

function extendFunction( oldOne, newOne ) {
    return (function() {
        oldOne();
        newOne();
    })();
}

var extendedFunction = extendFunction( initial, iWantToExecuteThisOneToo );

2017+解决方案

函数扩展的思想来自函数范式,从 ES6 开始就支持它:

function init(){
    doSomething();
}

// extend.js

init = (f => u => { f(u)
    doSomethingHereToo();
})(init);

init();

根据@TJCrowder 对堆栈转储的担忧,今天的浏览器处理情况要好得多。如果您将此代码保存到test.html 中并运行它,您将得到

test.html:3 Uncaught ReferenceError: doSomething is not defined
    at init (test.html:3)
    at test.html:8
    at test.html:12

第 12 行:init 调用,第 8 行:init 分机,第 3 行:未定义的doSomething()调用。

注意:非常尊重老将 TJ Crowder,他多年前在我还是个新手时亲切地回答了我的问题。多年后,我仍然记得尊重的态度,我努力学习好榜样。

(并感谢您的非常友好的评论。真的很感激!)
2021-04-27 11:42:54
“根据@TJCrowder 对堆栈转储的担忧,今天的浏览器处理情况要好得多。” 事实上,这些改进甚至被编入规范中。(我也可以避免匿名问题,即使在 2011 年,使用命名函数表达式;即使 IE 在 IE8 及以下版本中将它们弄错了,它弄错的方式也是无害的。生活和学习。:-))
2021-05-08 11:42:54