Javascript:扩展函数

发布于 2024-10-10 04:21:24 字数 343 浏览 2 评论 0原文

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

像这样:

// main.js

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

// extend.js

function extends init(){
    doSomethingHereToo();
}

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

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

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.

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

半枫 2024-10-17 04:21:24

通过更广泛地了解您实际想要做的事情以及您正在做的事情的背景,我相信我们可以为您提供比您的问题的字面答案更好的答案。

但这里有一个字面答案:

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

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

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.

// 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:

// 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)
            }
        }
    };
})();
野侃 2024-10-17 04:21:24

有多种方法可以实现此目的,这取决于您的目的,如果您只想在相同的上下文中执行该函数,您可以使用 .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();
};

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():

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:

function init(){
  doSomething();
}
//anytime later
var old_init = init;
init = function() {
  old_init.apply(this, arguments);
  doSomethingHereToo();
};
意中人 2024-10-17 04:21:24

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

The other methods are great but they don't preserve any prototype functions attached to init. To get around that you can do the following (inspired by the post from 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;
}) ();
神妖 2024-10-17 04:21:24

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,他多年前就很好心地回答了我的问题,当时我还是个新手。时隔多年,我仍然记得那种恭敬的态度,并努力效仿这个好榜样。

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

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

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.

贪了杯 2024-10-17 04:21:24

另一种选择可能是:

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

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 );
丿*梦醉红颜 2024-10-17 04:21:24

这非常简单直接。看一下代码。尝试掌握 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...希望我能给你一个在你的案例中使用的想法。

This is very simple and straight forward. Look at the code. Try to grasp the basic concept behind javascript extension.

First let us extend javascript function.

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,

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.

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,

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,

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. We can do almost anything. Happy JavaScripting... Hope I was able to give you an idea to use in your case.

月亮是我掰弯的 2024-10-17 04:21:24

据我了解,您正在尝试获取连接到用户帐户的应用程序。你可以通过在API上发出请求来做到这一点,我不知道discord.js是否涵盖了这部分API

endpoint: https://discord.com/api/users/@me/connections 

Request type: GET Header:

Authorization: "Beareryou token"

response: [
   {...}
]

As I understand it, you are trying to fetch the applications connected to the user account. You can do this by making a request on the API, I don't know if discord.js covers this part of the API

endpoint: https://discord.com/api/users/@me/connections 

Request type: GET Header:

Authorization: "Beareryou token"

response: [
   {...}
]
动听の歌 2024-10-17 04:21:24

使用 extendFunction.js

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

但在你的具体情况下,扩展全局 onload 函数更容易:

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

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

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

Use extendFunction.js

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

But in your specific case, it's easier to extend the global onload function:

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? I could easily add a .revert method to extended functions, so init = init.revert() would return the original function. 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文