Javascript:扩展函数

Posted

技术标签:

【中文标题】Javascript:扩展函数【英文标题】:Javascript: Extend a Function 【发布时间】:2011-06-02 11:32:50 【问题描述】:

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

类似这样的:

// main.js

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


// extend.js

function extends init()
    doSomethingHereToo();

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

我也想从其他文件中扩展它,例如我在main.js 中有原始初始化函数,在extended.js 中有扩展函数。

【问题讨论】:

相关...jondavidjohn.com/blog/2013/10/extend-javascript-functions 链接更新:jondavidjohn.com/extend-javascript-functions 【参考方案1】:

有几种方法可以解决这个问题,这取决于您的目的是什么,如果您只想在相同的上下文中执行该功能,您可以使用.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。请参阅this *** 问题。 @Nick,我发现你的扩展现有函数的 JavaScript 示例非常有用,但我很好奇如何通过 jQuery 完成同样的事情? +1 谢谢。如果你想在不修改原始 js 的情况下修补一些 3rd 方插件,这真的很方便。 不确定如何将它与需要参数和返回值的函数一起使用。【参考方案2】:

通过更广泛地了解您实际尝试做的事情以及您做这件事的背景,我相信我们可以给您一个比文字更好的答案你的问题。

但这是一个字面上的答案:

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

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

【讨论】:

感谢您的精彩回答。我对第二个示例的问题是我可能需要我正在扩展的函数的结果。 这对我帮助很大!谢谢【参考方案3】:

其他方法很好,但它们不保留任何附加到 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】:

使用extendFunction.js

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

但在您的具体情况下,扩展全局 onload 功能更容易:

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

我真的很喜欢你的问题,它让我思考不同的用例。

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

【讨论】:

【参考方案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 );

【讨论】:

【参考方案6】:

这非常简单直接。看代码。尝试掌握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

现在你可以像下面这样使用子函数了,

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 是一种非常简单的语言。我们几乎可以做任何事情。愉快的 JavaScripting... 希望我能给你一个想法,在你的情况下使用。

【讨论】:

【参考方案7】:

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 行:初始化调用,第 8 行:初始化扩展,第 3 行:未定义的doSomething() 调用。

注意:非常尊重老将 T.J.克劳德,多年前,当我还是个新手时,他很好地回答了我的问题。多年过去了,我依然记得那份恭敬的态度,我努力学习。

【讨论】:

“根据@TJCrowder 对堆栈转储的担忧,现在浏览器可以更好地处理这种情况。” 事实上,这些改进甚至被编入规范。 (即使在 2011 年,我也可以避免匿名问题,使用命名函数表达式;即使 IE 在 IE8 及更低版本中出错,但出错的方式是无害的。生活和学习。:-)) (感谢你们非常友好的 cmets。非常感谢!)

以上是关于Javascript:扩展函数的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript ES6 - 函数扩展

扩展 Javascript 承诺并在构造函数中解决或拒绝它

javascript 扩展构造函数以接收参数

JavaScript自执行函数和jquery扩展方法

在本机 safari 应用程序扩展中,我们如何使用 swift 的参数调用 javascript 函数?

JavaScript子集和扩展