1. ホーム
  2. javascript

[解決済み] ジャバスクリプト 関数を拡張する

2022-10-10 23:39:24

質問

初期化関数を拡張したいからです。

こんな感じかな。

// main.js

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

// extend.js

function extends init(){
    doSomethingHereToo();
}

そこで、PHPでクラスを拡張するように、関数を拡張してみたいと思います。

そして、他のファイルからも拡張したいと思います。例えば、私はオリジナルのinit関数を 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)
            }
        }
    };
})();