简体   繁体   English

Javascript:扩展函数

[英]Javascript: Extend a Function

The main reason why I want it is that I want to extend my initialize function.我想要它的主要原因是我想扩展我的初始化功能。

Something like this:像这样的东西:

// main.js

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

// extend.js

function extends init(){
    doSomethingHereToo();
}

So I want to extend a function like I extend a class in PHP.所以我想扩展一个函数,就像我在 PHP 中扩展一个类一样。

And I would like to extend it from other files too, so for example I have the original init function in main.js and the extended function in extended.js .我想从其他文件扩展太多,所以,比如我有在原来的初始化函数main.js和扩展功能extended.js

With a wider view of what you're actually trying to do and the context in which you're doing it, I'm sure we could give you a better answer than the literal answer to your question.通过更广泛地了解您实际尝试做什么以及您在做什么的背景下,我相信我们可以为您提供比问题的字面答案更好的答案。

But here's a literal answer:但这是一个字面的答案:

If you're assigning these functions to some property somewhere, you can wrap the original function and put your replacement on the property instead:如果您将这些函数分配给某处的某个属性,则可以包装原始函数并将替换的函数放在该属性上:

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

If your functions aren't already on an object, you'd probably want to put them there to facilitate the above.如果您的函数还没有在对象上,您可能希望将它们放在那里以方便上述操作。 For instance:例如:

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

But there are better ways to do that.但是有更好的方法来做到这一点。 Like for instance, providing a means of registering init functions.例如,提供一种注册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);
            }
        }
    };
})();

Here in 2020 (or really any time after ~2016), that can be written a bit more compactly:在 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)
            }
        }
    };
})();

There are several ways to go about this, it depends what your purpose is, if you just want to execute the function as well and in the same context, you can use .apply() :有几种方法可以解决这个问题,这取决于您的目的是什么,如果您只想在相同的上下文中执行该函数,您可以使用.apply()

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

If you want to replace it with a newer init , it'd look like this:如果你想用更新的init替换它,它看起来像这样:

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

The other methods are great but they don't preserve any prototype functions attached to init.其他方法很棒,但它们不保留任何附加到 init 的原型函数。 To get around that you can do the following (inspired by the post from Nick Craver).为了解决这个问题,您可以执行以下操作(灵感来自 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;
}) ();

Another option could be:另一种选择可能是:

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+ solution 2017+解决方案

The idea of function extensions comes from functional paradigm, which is natively supported since ES6:函数扩展的思想来自函数范式,从 ES6 开始就支持它:

function init(){
    doSomething();
}

// extend.js

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

init();

As per @TJCrowder's concern about stack dump, the browsers handle the situation much better today.根据@TJCrowder 对堆栈转储的担忧,今天的浏览器处理情况要好得多。 If you save this code into test.html and run it, you get如果您将此代码保存到test.html 中并运行它,您将得到

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

Line 12: the init call, Line 8: the init extension, Line 3: the undefined doSomething() call.第 12 行:init 调用,第 8 行:init 扩展,第 3 行:未定义的doSomething()调用。

Note: Much respect to veteran TJ Crowder, who kindly answered my question many years ago, when I was a newbie.注意:非常尊重老将 TJ Crowder,他多年前在我还是个新手时亲切地回答了我的问题。 After the years, I still remember the respectfull attitude and I try to follow the good example.多年以后,我还记得那种恭敬的态度,我努力效法这个好榜样。

This is very simple and straight forward.这是非常简单和直接的。 Look at the code.看代码。 Try to grasp the basic concept behind javascript extension.尝试掌握 javascript 扩展背后的基本概念。

First let us extend javascript function.首先让我们扩展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
}

You can extend this function by creating child function in following way您可以通过以下方式创建子函数来扩展此函数

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
}

Now you can use Child function as follows,现在您可以按如下方式使用 Child 函数,

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

We can also create Javascript Function by extending Javascript classes, like this.我们还可以通过扩展 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
    }
}

Let us extend this class with Child function like this,让我们像这样用 Child 函数扩展这个类,

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

Again you can use Child function as follows to get similar result,同样,您可以按如下方式使用 Child 函数来获得类似的结果,

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

Javascript is very easy language. Javascript 是一种非常简单的语言。 We can do almost anything.我们几乎可以做任何事情。 Happy JavaScripting... Hope I was able to give you an idea to use in your case.愉快的 JavaScripting ......希望我能给你一个想法来在你的情况下使用。

Use extendFunction.js使用extendFunction.js

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

But in your specific case, it's easier to extend the global onload function:但在您的特定情况下,扩展全局 onload 功能更容易:

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

I actually really like your question, it's making me think about different use cases.我真的很喜欢你的问题,它让我思考不同的用例。

For javascript events, you really want to add and remove handlers - but for extendFunction, how could you later remove functionality?对于 javascript 事件,您确实想添加和删除处理程序 - 但是对于 extendFunction,您以后如何删除功能? I could easily add a .revert method to extended functions, so init = init.revert() would return the original function.我可以轻松地向扩展函数添加一个 .revert 方法,因此init = init.revert()将返回原始函数。 Obviously this could lead to some pretty bad code, but perhaps it lets you get something done without touching a foreign part of the codebase.显然,这可能会导致一些非常糟糕的代码,但也许它可以让您在不接触代码库的外部部分的情况下完成某些工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM