Javascript:扩展功能


93

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

像这样:

// main.js

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

// extend.js

function extends init(){
    doSomethingHereToo();
}

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

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



Answers:


102

通过更广泛地了解您实际要执行的操作以及执行操作所处的环境,我相信我们可以给您比对您的问题的字面答案更好的答案。

但这是字面上的答案:

如果要将这些函数分配给某个地方的某个属性,则可以包装原始函数,然后将替换项放在该属性上:

// 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 = (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();
    }
})();

但也有这样的更好的方法来做到这一点。例如,提供一种注册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;
})();

(上面的许多内容可以更紧凑地编写,但是我想使用类似的清晰名称,publicSymbols而不是通常的pubs或匿名的对象文字。如果要使用匿名函数,可以更紧凑地编写,但是我不希望这样做。对匿名功能非常在意。)


感谢您的好回答。第二个示例的问题是,我可能需要扩展函数的结果。
Gerhard Davids,

64

有几种解决方法,这取决于您的目的,如果您只想同时执行该功能,并且在相同的上下文中,则可以使用.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();
};

2
有时,您可能需要.call方法而不是.apply。请参阅 StackOverflow问题。
MrDanA 2014年

@Nick,我发现扩展现有功能的JavaScript示例非常有用,但是我很好奇如何通过jQuery完成同样的事情?
Sunil 2014年

+1谢谢。如果您想在不修改原始js的情况下修补某些第三方插件,这将非常方便。
GFoley83

1
不确定如何将其与需要参数和返回值的函数一起使用。
杰弗里德(Gerfried)

5

其他方法很棒,但是它们不保留附加到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;
}) ();

4

另一种选择是:

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

0

这非常简单直接。看代码。尝试掌握javascript扩展背后的基本概念。

首先让我们扩展javascript功能。

function Base(props) {
    const _props = props
    this.getProps = () => _props

    // We can make method private by not binding it to this object. 
    // Hence it is not exposed when we return this.
    const privateMethod = () => "do internal stuff" 

    return this
}

您可以通过以下方式创建子功能来扩展此功能

function Child(props) {
    const parent = Base(props)
    this.getMessage = () => `Message is ${parent.getProps()}`;

    // You can remove the line below to extend as in private inheritance, 
    // not exposing parent function properties and method.
    this.prototype = parent
    return this
}

现在,您可以按以下方式使用Child函数,

let childObject = Child("Secret Message")
console.log(childObject.getMessage())     // logs "Message is Secret Message"
console.log(childObject.getProps())       // logs "Secret Message"

我们还可以通过扩展Javascript类来创建Javascript函数。

class BaseClass {
    constructor(props) {
        this.props = props
        // You can remove the line below to make getProps method private. 
        // As it will not be binded to this, but let it be
        this.getProps = this.getProps.bind(this)
    }

    getProps() {
        return this.props
    }
}

让我们用这样的Child函数扩展此类,

function Child(props) {
    let parent = new BaseClass(props)
    const getMessage = () => `Message is ${parent.getProps()}`;
    return { ...parent, getMessage} // I have used spread operator. 
}

同样,您可以按以下方式使用Child函数来获得相似的结果,

let childObject = Child("Secret Message")
console.log(childObject.getMessage())     // logs "Message is Secret Message"
console.log(childObject.getProps())       // logs "Secret Message"

Javascript是非常简单的语言。我们几乎可以做任何事。JavaScript编程愉快...希望我能给您一个思路,供您参考。


-1

使用extendFunction.js

init = extendFunction(init, function(args) {
  doSomethingHereToo();
});

但是在您的特定情况下,扩展全局onload函数更容易:

extendFunction('onload', function(args) {
  doSomethingHereToo();
});

我实际上真的很喜欢您的问题,这使我考虑了不同的用例。

对于javascript事件,您确实要添加和删除处理程序-但对于extendFunction,您以后如何删除功能?我可以轻松地向扩展函数添加.revert方法,因此init = init.revert()将返回原始函数。显然,这可能会导致一些非常糟糕的代码,但是也许它可以使您完成某些工作而不会触及代码库的外部部分。

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.