Javascript:扩展功能

Ada*_*dam 85 javascript function extend

我想要它的主要原因是我想扩展我的初始化函数.

像这样的东西:

// main.js

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

// extend.js

function extends init(){
    doSomethingHereToo();
}
Run Code Online (Sandbox Code Playgroud)

所以我想扩展一个函数,比如我在PHP中扩展一个类.

我也想从其他文件扩展它,所以例如我有原始的init函数main.js和扩展函数extended.js.

T.J*_*der 93

通过更广泛地了解您实际上要做的事情以及您正在做的事情,我相信我们可以给您一个比您的问题的字面答案更好的答案.

但这是一个字面答案:

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

// 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);
Run Code Online (Sandbox Code Playgroud)

如果您的功能尚未出现在某个对象上,您可能希望将它们放在那里以方便上述操作.例如:

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

    publicSymbols.init = init;
    function init() {
    }

    return publicSymbols;
})();

// In extended.js
(function() {
    var oldInit = MyLibrary.init;
    MyLibrary.init = extendedInit;
    function extendedInit() {
        oldInit.apply(MyLibrary); // Use #apply in case `init` uses `this`
        doSomething();
    }
})();
Run Code Online (Sandbox Code Playgroud)

但也有这样的更好的方法来做到这一点.例如,提供一种注册init功能的方法.

// In main.js
var MyLibrary = (function() {
    var publicSymbols = {},
        initfunctions = [];

    publicSymbols.init = init;
    function init() {
        var funcs = initFunctions;

        initFunctions = undefined;

        for (index = 0; index < funcs.length; ++index) {
            try { funcs[index](); } catch (e) { }
        }
    }

    publicSymbols.addInitFunction = addInitFunction;
    function addInitFunction(f) {
        if (initFunctions) {
            // Init hasn't run yet, rememeber it
            initFunctions.push(f);
        }
        else {
            // `init` has already run, call it almost immediately
            // but *asynchronously* (so the caller never sees the
            // call synchronously)
            setTimeout(f, 0);
        }
    }

    return publicSymbols;
})();
Run Code Online (Sandbox Code Playgroud)

(上面的大部分内容可以写得更紧凑,但我想使用清晰的名称,publicSymbols而不是我的通常pubs或匿名对象文字.如果你想要匿名函数,你可以写得更紧凑,但我不是非常关心匿名函数.)


Nic*_*ver 61

有几种方法可以解决这个问题,这取决于你的目的是什么,如果你只想在同一个环境中执行该函数,你可以使用.apply():

function init(){
  doSomething();
}
function myFunc(){
  init.apply(this, arguments);
  doSomethingHereToo();
}
Run Code Online (Sandbox Code Playgroud)

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

function init(){
  doSomething();
}
//anytime later
var old_init = init;
init = function() {
  old_init.apply(this, arguments);
  doSomethingHereToo();
};
Run Code Online (Sandbox Code Playgroud)

  • 有时您可能需要`.call`方法而不是`.apply`.请参阅[this](http://stackoverflow.com/questions/1986896/what-is-the-difference-between-call-and-apply)StackOverflow问题. (2认同)

Jan*_*roň 7

2017+ solution

The idea of function extensions comes from functional paradigm, which is natively supported since ES6:

function init(){
    doSomething();
}

// extend.js

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

init();
Run Code Online (Sandbox Code Playgroud)

As per @TJCrowder's concern about stack dump, the browsers handle the situation much better today. If you save this code into test.html and run it, you get

test.html:3 Uncaught ReferenceError: doSomething is not defined
    at init (test.html:3)
    at test.html:8
    at test.html:12
Run Code Online (Sandbox Code Playgroud)

Line 12: the init call, Line 8: the init extension, Line 3: the undefined doSomething() call.

Note: Much respect to veteran T.J. Crowder, who kindly answered my question many years ago, when I was a newbie. After the years, I still remember the respectfull attitude and I try to follow the good example.


All*_*lly 6

其他方法很棒,但它们不保留任何附加到 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;
}) ();
Run Code Online (Sandbox Code Playgroud)


Sér*_*gio 5

另一种选择可能是:

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 );
Run Code Online (Sandbox Code Playgroud)