Javascript: Memperluas Fungsi

94

Alasan utama mengapa saya menginginkannya adalah karena saya ingin memperluas fungsi inisialisasi saya.

Sesuatu seperti ini:

// main.js

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

// extend.js

function extends init(){
    doSomethingHereToo();
}

Jadi saya ingin memperluas fungsi seperti saya memperluas kelas di PHP.

Dan saya ingin memperluasnya dari file lain juga, jadi misalnya saya memiliki fungsi init asli main.jsdan fungsi diperpanjang di extended.js.

Adam Halasz
sumber
Pembaruan tautan
NETCreator Hosting

Jawaban:

103

Dengan pandangan yang lebih luas tentang apa yang sebenarnya Anda coba lakukan dan konteks di mana Anda melakukannya, saya yakin kami dapat memberikan jawaban yang lebih baik daripada jawaban literal untuk pertanyaan Anda.

Tapi inilah jawaban literal:

Jika Anda menetapkan fungsi-fungsi ini ke beberapa properti di suatu tempat, Anda dapat menggabungkan fungsi asli dan meletakkan penggantinya di properti itu:

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

Jika fungsi Anda belum ada pada suatu objek, Anda mungkin ingin meletakkannya di sana untuk memfasilitasi hal di atas. Misalnya:

// In main.js
var MyLibrary = (function() {
    var publicSymbols = {};

    publicSymbols.init = init;
    function init() {
    }

    return publicSymbols;
})();

// In extended.js
(function() {
    var oldInit = MyLibrary.init;
    MyLibrary.init = extendedInit;
    function extendedInit() {
        oldInit.apply(MyLibrary); // Use #apply in case `init` uses `this`
        doSomething();
    }
})();

Tapi ada seperti cara yang lebih baik untuk melakukan itu. Seperti misalnya, menyediakan sarana untuk mendaftarkan initfungsi.

// In main.js
var MyLibrary = (function() {
    var publicSymbols = {},
        initfunctions = [];

    publicSymbols.init = init;
    function init() {
        var funcs = initFunctions;

        initFunctions = undefined;

        for (index = 0; index < funcs.length; ++index) {
            try { funcs[index](); } catch (e) { }
        }
    }

    publicSymbols.addInitFunction = addInitFunction;
    function addInitFunction(f) {
        if (initFunctions) {
            // Init hasn't run yet, rememeber it
            initFunctions.push(f);
        }
        else {
            // `init` has already run, call it almost immediately
            // but *asynchronously* (so the caller never sees the
            // call synchronously)
            setTimeout(f, 0);
        }
    }

    return publicSymbols;
})();

(Banyak hal di atas dapat ditulis sedikit lebih padat, tetapi saya ingin menggunakan nama yang jelas seperti publicSymbolsdaripada pubsliteral objek biasa atau anonim saya . Anda dapat menulisnya lebih ringkas jika Anda ingin memiliki fungsi anonim, tetapi saya tidak ' Tidak terlalu peduli dengan fungsi anonim .)

TJ Crowder
sumber
Terima kasih atas jawaban yang bagus. Masalah saya dengan contoh kedua adalah bahwa saya mungkin memerlukan hasil dari fungsi yang saya kembangkan.
Gerhard Davids
64

Ada beberapa cara untuk melakukannya, tergantung apa tujuan Anda, jika Anda hanya ingin menjalankan fungsinya juga dan dalam konteks yang sama, Anda dapat menggunakan .apply():

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

Jika Anda ingin menggantinya dengan yang lebih baru init, tampilannya akan seperti ini:

function init(){
  doSomething();
}
//anytime later
var old_init = init;
init = function() {
  old_init.apply(this, arguments);
  doSomethingHereToo();
};
Nick Craver
sumber
2
Terkadang Anda mungkin menginginkan .callmetode, bukan .apply. Lihat pertanyaan StackOverflow ini .
MrDanA
@ Nick, saya menemukan contoh JavaScript Anda untuk memperluas fungsi yang ada sangat berguna, tetapi saya ingin tahu bagaimana hal yang sama ini akan dilakukan melalui jQuery?
Sunil
+1 Terima kasih. Ini sangat berguna jika Anda ingin menambal beberapa plugin pihak ketiga tanpa memodifikasi js asli.
GFoley83
1
Tidak yakin bagaimana menggunakannya dengan fungsi yang mengharapkan parameter dan mengembalikan nilai.
Gerfried
5

Metode lain memang bagus tetapi tidak mempertahankan fungsi prototipe apa pun yang dilampirkan ke init. Untuk menyiasatinya, Anda dapat melakukan hal berikut (terinspirasi oleh kiriman dari 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;
}) ();
Sekutu
sumber
5

Pilihan lain bisa jadi:

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 );
Sérgio
sumber
0

Ini sangat sederhana dan lurus ke depan. Lihat kodenya. Cobalah untuk memahami konsep dasar di balik ekstensi javascript.

Pertama mari kita memperluas fungsi 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
}

Anda dapat memperluas fungsi ini dengan membuat fungsi anak dengan cara berikut

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
}

Sekarang Anda dapat menggunakan fungsi Anak sebagai berikut,

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

Kita juga dapat membuat Fungsi Javascript dengan memperluas kelas Javascript, seperti ini.

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
    }
}

Mari kita memperluas kelas ini dengan fungsi Anak seperti ini,

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

Sekali lagi Anda dapat menggunakan fungsi Anak sebagai berikut untuk mendapatkan hasil yang serupa,

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

Javascript adalah bahasa yang sangat mudah. Kami bisa melakukan hampir semua hal. Selamat JavaScripting ... Semoga saya bisa memberi Anda ide untuk digunakan dalam kasus Anda.

Antrian Pasar
sumber
-1

Gunakan extensionFunction.js

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

Namun dalam kasus spesifik Anda, lebih mudah untuk memperluas fungsi onload global:

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

Saya sebenarnya sangat menyukai pertanyaan Anda, ini membuat saya berpikir tentang kasus penggunaan yang berbeda.

Untuk acara javascript, Anda benar-benar ingin menambah dan menghapus penangan - tetapi untuk ekstensiFungsi, bagaimana Anda nantinya dapat menghapus fungsionalitas? Saya dapat dengan mudah menambahkan metode .revert ke fungsi yang diperluas, jadi init = init.revert()akan mengembalikan fungsi aslinya. Jelas ini dapat menyebabkan beberapa kode yang sangat buruk, tetapi mungkin ini memungkinkan Anda menyelesaikan sesuatu tanpa menyentuh bagian asing dari basis kode.

Devin G Rhode
sumber