簡體   English   中英

Javascript:擴展函數

[英]Javascript: Extend a Function

我想要它的主要原因是我想擴展我的初始化功能。

像這樣的東西:

// main.js

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

// extend.js

function extends init(){
    doSomethingHereToo();
}

所以我想擴展一個函數,就像我在 PHP 中擴展一個類一樣。

我想從其他文件擴展太多,所以,比如我有在原來的初始化函數main.js和擴展功能extended.js

通過更廣泛地了解您實際嘗試做什么以及您在做什么的背景下,我相信我們可以為您提供比問題的字面答案更好的答案。

但這是一個字面的答案:

如果您將這些函數分配給某處的某個屬性,則可以包裝原始函數並將替換的函數放在該屬性上:

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

有幾種方法可以解決這個問題,這取決於您的目的是什么,如果您只想在相同的上下文中執行該函數,您可以使用.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();
};

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

另一種選擇可能是:

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+解決方案

函數擴展的思想來自函數范式,從 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,他多年前在我還是個新手時親切地回答了我的問題。 多年以后,我還記得那種恭敬的態度,我努力效法這個好榜樣。

這是非常簡單和直接的。 看代碼。 嘗試掌握 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 是一種非常簡單的語言。 我們幾乎可以做任何事情。 愉快的 JavaScripting ......希望我能給你一個想法來在你的情況下使用。

使用extendFunction.js

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

但在您的特定情況下,擴展全局 onload 功能更容易:

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

我真的很喜歡你的問題,它讓我思考不同的用例。

對於 javascript 事件,您確實想添加和刪除處理程序 - 但是對於 extendFunction,您以后如何刪除功能? 我可以輕松地向擴展函數添加一個 .revert 方法,因此init = init.revert()將返回原始函數。 顯然,這可能會導致一些非常糟糕的代碼,但也許它可以讓您在不接觸代碼庫的外部部分的情況下完成某些工作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM