Kelebihan fungsi dalam Javascript - Praktik terbaik

783

Apa cara terbaik untuk kelebihan fungsi palsu di Javascript?

Saya tahu tidak mungkin untuk membebani fungsi dalam Javascript seperti pada bahasa lain. Jika saya membutuhkan fungsi dengan dua penggunaan foo(x)dan foo(x,y,z)yang merupakan cara terbaik / disukai:

  1. Menggunakan nama yang berbeda di tempat pertama
  2. Menggunakan argumen opsional seperti y = y || 'default'
  3. Menggunakan sejumlah argumen
  4. Memeriksa jenis argumen
  5. Atau bagaimana?
hamdiakoguz
sumber
14
Mungkin akan berguna untuk bertanya mengapa Anda pikir Anda perlu kelebihan fungsi untuk memulai. Saya pikir itu akan membuat kita lebih dekat ke solusi nyata.
Breton
1
Ini ditutup, tetapi saya melakukan yang berikut ini: this.selectBy = {instance: selectByInstance, // Teks fungsi: selectByText, // Nilai fungsi: selectByValue // Function};
Tahanan NOL
Jawaban saya menunjukkan bagaimana cara menjalankan fungsi waktu berlebih, ia memiliki penalti kecepatan dan saya tidak akan menyarankan melakukannya untuk menyiasati spesifikasi Javascript. Fungsi overloading benar-benar tugas kompilasi waktu, saya hanya memberikan jawaban untuk tujuan akademis dan menyerahkannya pada kebijaksanaan Anda sendiri, apakah akan menggunakannya dalam kode atau tidak.
Keldon Alleyne
2
Kalau-kalau berguna, saya telah membangun kerangka js ringan yang memungkinkan overloading metode berbasis tipe. Jelas peringatan yang sama berlaku untuk kinerja, tetapi sejauh ini berfungsi dengan baik untuk kebutuhan saya dan masih memiliki banyak ruang untuk perbaikan: blog.pebbl.co.uk/2013/01/describejs.html#methodoverloading
Pebbl

Jawaban:

602

Cara terbaik untuk melakukan overloading fungsi dengan parameter adalah tidak memeriksa panjang argumen atau jenisnya; memeriksa jenisnya hanya akan membuat kode Anda lambat dan Anda bersenang-senang dengan Array, nulls, Objects, dll.

Apa yang dilakukan sebagian besar pengembang adalah menempel pada objek sebagai argumen terakhir untuk metode mereka. Objek ini bisa menampung apa saja.

function foo(a, b, opts) {
  // ...
  if (opts['test']) { } //if test param exists, do something.. 
}


foo(1, 2, {"method":"add"});
foo(3, 4, {"test":"equals", "bar":"tree"});

Maka Anda bisa mengatasinya tetap Anda inginkan dalam metode Anda. [Ganti, jika ada, dll.]

epascarello
sumber
43
Bisakah Anda memberikan contoh implementasi foo () yang menggambarkan bagaimana parameter "memilih" ini digunakan / direferensikan?
Moe Howard
24
Moe // Bisa jadi ini; if(opts['test']) //if test param exists, do something.. if(opts['bar']) //if bar param exists, do something
Deckard
80
Ini bukan fungsi yang berlebihan. Function overloading memiliki dua fungsi terpisah dengan nama yang sama tetapi parameter yang berbeda. Apa yang Anda gambarkan hanyalah satu fungsi dengan argumen objek di akhir.
d512
66
@ user1334007 tidak mungkin untuk memiliki fungsi yang berlebihan seperti yang Anda lakukan di Java / .NET. Ya ini bukan "persis" kelebihan beban, tetapi itu berhasil.
epascarello
18
Saya terkejut tidak ada yang menanyakan hal ini: mengapa pengecekan arguments.lengthtidak direkomendasikan? Juga, saya pernah ke sini sebelumnya dan membaca Apa yang dilakukan kebanyakan pengembang adalah ... , tapi saya yakin ini adalah satu-satunya tempat yang pernah saya lihat. Metode itu juga merusak gula sintaksis karena memiliki 'kelebihan'!
c24w
169

Saya sering melakukan ini:

C #:

public string CatStrings(string p1)                  {return p1;}
public string CatStrings(string p1, int p2)          {return p1+p2.ToString();}
public string CatStrings(string p1, int p2, bool p3) {return p1+p2.ToString()+p3.ToString();}

CatStrings("one");        // result = one
CatStrings("one",2);      // result = one2
CatStrings("one",2,true); // result = one2true

Setara JavaScript:

function CatStrings(p1, p2, p3)
{
  var s = p1;
  if(typeof p2 !== "undefined") {s += p2;}
  if(typeof p3 !== "undefined") {s += p3;}
  return s;
};

CatStrings("one");        // result = one
CatStrings("one",2);      // result = one2
CatStrings("one",2,true); // result = one2true

Contoh khusus ini sebenarnya lebih elegan dalam javascript daripada C #. Parameter yang tidak ditentukan adalah 'tidak terdefinisi' dalam javascript, yang dievaluasi menjadi false dalam pernyataan if. Namun, definisi fungsi tidak menyampaikan informasi bahwa p2 dan p3 adalah opsional. Jika Anda membutuhkan banyak kelebihan, jQuery telah memutuskan untuk menggunakan objek sebagai parameter, misalnya, jQuery.ajax (opsi). Saya setuju dengan mereka bahwa ini adalah pendekatan kelebihan muatan yang paling kuat dan dapat didokumentasikan, tetapi saya jarang membutuhkan lebih dari satu atau dua parameter opsional cepat.

EDIT: mengubah tes IF sesuai saran Ian

rocketsarefast
sumber
16
Parameter yang tidak ditentukan undefineddalam JS, tidak null. Sebagai praktik terbaik, Anda tidak boleh mengatur apa pun untuk undefined, jadi itu seharusnya tidak menjadi masalah selama Anda mengubah tes Anda p2 === undefined.
Tamzin Blake
3
Jika Anda lulus falsesebagai argumen terakhir maka itu tidak akan "false"selesai sampai akhir, karena if(p3)tidak akan bercabang.
dreamlax
5
Hanya catatan singkat, Anda typeof p2 === "undefined"mungkin kebalikan dari apa yang Anda harapkan dalam contoh Anda, saya pikir typeof p2 !== "undefined"adalah apa yang Anda maksudkan. Juga, Bolehkah saya menyarankan untuk menggabungkan string, angka, dan boolean yang sebenarnya Anda lakukanp2 === "number"; p3 === "boolean"
WillFM
8
Saya suka melakukan ini: p3 = p3 || 'nilai default';
Dorian
3
Apa arti dari ===dan !==? Kenapa tidak pakai saja ==dan !=?
Ricardo Cruz
76

Tidak ada fungsi nyata kelebihan dalam JavaScript karena memungkinkan untuk melewati sejumlah parameter jenis apa pun. Anda harus memeriksa di dalam fungsi, berapa banyak argumen yang telah diajukan dan apa jenisnya.

Gumbo
sumber
1
John Resig (dari jQuery) pernah mencoba ini, tetapi upaya itu murni bersifat akademis dan tidak memberikan manfaat nyata.
scunliffe
14
Fungsi John Resig kelebihan muatan di sini ejohn.org/blog/javascript-method-overloading
Terrance
@Terrance: Saya suka metode Resig juga. Itu bekerja seperti pesona. Saya hanya perlu menemukan cara untuk membuat tes untuk memvalidasi kasus penggunaan.
chrisvillanueva
"Fungsi ini tidak akan mengubah dunia, tetapi singkat, singkat, dan menggunakan fitur JavaScript yang tidak jelas - jadi ia menang di buku saya." :-)
Frerich Raabe
68

Jawaban yang benar adalah TIDAK ADA OVERLOADING DI JAVASCRIPT.

Memeriksa / Mengalihkan di dalam fungsi tersebut tidak MENGATASI.

Konsep overloading: Dalam beberapa bahasa pemrograman, fungsi overloading atau overloading metode adalah kemampuan untuk membuat beberapa metode dengan nama yang sama dengan implementasi yang berbeda. Panggilan ke fungsi yang kelebihan beban akan menjalankan implementasi spesifik dari fungsi yang sesuai dengan konteks panggilan, memungkinkan satu panggilan fungsi untuk melakukan tugas yang berbeda tergantung pada konteksnya.

Sebagai contoh, doTask () dan doTask (objek O) adalah metode kelebihan beban. Untuk memanggil yang terakhir, sebuah objek harus dilewatkan sebagai parameter, sedangkan yang pertama tidak memerlukan parameter, dan dipanggil dengan bidang parameter kosong. Kesalahan umum adalah untuk menetapkan nilai default ke objek dalam metode kedua, yang akan menghasilkan kesalahan panggilan ambigu, karena kompiler tidak akan tahu yang mana dari dua metode yang digunakan.

https://en.wikipedia.org/wiki/Function_overloading

Semua implementasi yang disarankan bagus, tapi jujur ​​saja, tidak ada implementasi asli untuk JavaScript.

Marco
sumber
4
akhirnya jawaban yang normal! TIDAK ADA OVERLOAD DI JAVASCRIPT.
Razvan Tudorica
4
OP meminta cara untuk memalsukan kelebihan muatan.
Ateur Games
Seperti yang saya katakan sebelumnya, kami di sini memang mendidik orang, kami tidak hanya memberikan jawaban kepada mereka tanpa memverifikasi bahwa apa yang mereka minta itu benar.
Marco
27

Ada dua cara Anda bisa mendekati ini dengan lebih baik:

  1. Lulus kamus (array asosiatif) jika Anda ingin memberikan banyak fleksibilitas

  2. Ambil objek sebagai argumen dan gunakan pewarisan berbasis prototipe untuk menambah fleksibilitas.

t3rse
sumber
1
ini adalah pemikiran awal saya, namun, jika fungsi yang Anda buat akan digunakan di perpustakaan atau oleh orang lain, menghitung nilai-nilai secara jelas dapat membantu
roberthuttinger
19

Berikut adalah pendekatan yang memungkinkan overloading metode nyata menggunakan tipe parameter, yang ditunjukkan di bawah ini:

Func(new Point());
Func(new Dimension());
Func(new Dimension(), new Point());
Func(0, 0, 0, 0);

Edit (2018) : Karena ini ditulis pada tahun 2011, kecepatan panggilan metode langsung telah sangat meningkat sementara kecepatan metode kelebihan beban tidak.

Ini bukan pendekatan yang saya rekomendasikan, tetapi merupakan pemikiran yang bermanfaat untuk memikirkan bagaimana Anda dapat memecahkan masalah-masalah seperti ini.


Berikut adalah patokan dari berbagai pendekatan - https://jsperf.com/function-overloading . Ini menunjukkan bahwa kelebihan fungsi (memperhitungkan jenis) dapat sekitar 13 kali lebih lambat di Google Chrome V8 pada 16.0 (beta) .

Serta melewati objek (yaitu {x: 0, y: 0}), seseorang juga dapat mengambil pendekatan C saat yang tepat, penamaan metode yang sesuai. Misalnya, Vector.AddVector (vektor), Vector.AddIntegers (x, y, z, ...) dan Vector.AddArray (integerArray). Anda dapat melihat pustaka C, seperti OpenGL untuk penamaan inspirasi.

Edit : Saya telah menambahkan patokan untuk meneruskan objek dan menguji objek menggunakan keduanya 'param' in argdan arg.hasOwnProperty('param'), dan fungsi kelebihan beban jauh lebih cepat daripada melewati objek dan memeriksa properti (setidaknya dalam patokan ini).

Dari perspektif desain, fungsi kelebihan hanya valid atau logis jika parameter kelebihan sesuai dengan tindakan yang sama. Jadi masuk akal bahwa seharusnya ada metode mendasar yang hanya memusatkan perhatian pada perincian spesifik, jika tidak, itu mungkin mengindikasikan pilihan desain yang tidak tepat. Jadi kita juga bisa menyelesaikan penggunaan fungsi kelebihan dengan mengkonversi data ke objek masing-masing. Tentu saja orang harus mempertimbangkan ruang lingkup masalah karena tidak perlu membuat desain yang rumit jika niat Anda hanya untuk mencetak nama, tetapi untuk desain kerangka kerja dan perpustakaan pemikiran seperti itu dibenarkan.

Contoh saya berasal dari implementasi Rectangle - maka disebutkan Dimensi dan Titik. Mungkin Rectangle dapat menambahkan GetRectangle()metode ke Dimensiondan Pointprototipe, dan kemudian masalah kelebihan fungsi diurutkan. Dan bagaimana dengan primitif? Kita memiliki panjang argumen, yang sekarang merupakan tes yang valid karena objek memiliki GetRectangle()metode.

function Dimension() {}
function Point() {}

var Util = {};

Util.Redirect = function (args, func) {
  'use strict';
  var REDIRECT_ARGUMENT_COUNT = 2;

  if(arguments.length - REDIRECT_ARGUMENT_COUNT !== args.length) {
    return null;
  }

  for(var i = REDIRECT_ARGUMENT_COUNT; i < arguments.length; ++i) {
    var argsIndex = i-REDIRECT_ARGUMENT_COUNT;
    var currentArgument = args[argsIndex];
    var currentType = arguments[i];
    if(typeof(currentType) === 'object') {
      currentType = currentType.constructor;
    }
    if(typeof(currentType) === 'number') {
      currentType = 'number';
    }
    if(typeof(currentType) === 'string' && currentType === '') {
      currentType = 'string';
    }
    if(typeof(currentType) === 'function') {
      if(!(currentArgument instanceof currentType)) {
        return null;
      }
    } else {
      if(typeof(currentArgument) !== currentType) {
        return null;
      }
    } 
  }
  return [func.apply(this, args)];
}

function FuncPoint(point) {}
function FuncDimension(dimension) {}
function FuncDimensionPoint(dimension, point) {}
function FuncXYWidthHeight(x, y, width, height) { }

function Func() {
  Util.Redirect(arguments, FuncPoint, Point);
  Util.Redirect(arguments, FuncDimension, Dimension);
  Util.Redirect(arguments, FuncDimensionPoint, Dimension, Point);
  Util.Redirect(arguments, FuncXYWidthHeight, 0, 0, 0, 0);
}

Func(new Point());
Func(new Dimension());
Func(new Dimension(), new Point());
Func(0, 0, 0, 0);
Keldon Alleyne
sumber
16

Cara terbaik sangat tergantung pada fungsi dan argumennya. Setiap pilihan Anda adalah ide bagus dalam situasi yang berbeda. Saya biasanya mencoba ini dalam urutan berikut sampai salah satu dari mereka berfungsi:

  1. Menggunakan argumen opsional seperti y = y || 'default'. Ini nyaman jika Anda bisa melakukannya, tetapi mungkin tidak selalu berhasil secara praktis, misalnya ketika 0 / null / undefined akan menjadi argumen yang valid.

  2. Menggunakan sejumlah argumen. Mirip dengan opsi terakhir tetapi dapat bekerja ketika # 1 tidak berfungsi.

  3. Memeriksa jenis argumen. Ini dapat bekerja dalam beberapa kasus di mana jumlah argumennya sama. Jika Anda tidak dapat menentukan jenisnya dengan andal, Anda mungkin perlu menggunakan nama yang berbeda.

  4. Menggunakan nama yang berbeda di tempat pertama. Anda mungkin perlu melakukan ini jika opsi lain tidak berfungsi, tidak praktis, atau untuk konsistensi dengan fungsi terkait lainnya.

Matthew Crumley
sumber
14

Jika saya membutuhkan fungsi dengan dua menggunakan foo (x) dan foo (x, y, z) yang merupakan cara terbaik / disukai?

Masalahnya adalah bahwa JavaScript TIDAK mendukung kelebihan metode secara bawaan. Jadi, jika ia melihat / mem-parsing dua atau lebih fungsi dengan nama yang sama, ia hanya akan mempertimbangkan fungsi yang didefinisikan terakhir dan menimpa yang sebelumnya.

Salah satu cara yang menurut saya cocok untuk sebagian besar kasus adalah sebagai berikut -

Katakanlah Anda memiliki metode

function foo(x)
{
} 

Alih-alih metode overloading yang tidak mungkin dilakukan dalam javascript, Anda dapat menentukan metode baru

fooNew(x,y,z)
{
}

dan kemudian memodifikasi fungsi 1 sebagai berikut -

function foo(arguments)
{
  if(arguments.length==2)
  {
     return fooNew(arguments[0],  arguments[1]);
  }
} 

Jika Anda memiliki banyak metode kelebihan seperti itu pertimbangkan untuk menggunakan switch bukan hanya if-elsepernyataan.

( lebih detail )

PS: Tautan di atas menuju ke blog pribadi saya yang memiliki detail tambahan.

Aniket Thakur
sumber
9

Saya tidak yakin tentang praktik terbaik, tetapi inilah cara saya melakukannya:

/*
 * Object Constructor
 */
var foo = function(x) {
    this.x = x;
};

/*
 * Object Protoype
 */
foo.prototype = {
    /*
     * f is the name that is going to be used to call the various overloaded versions
     */
    f: function() {

        /*
         * Save 'this' in order to use it inside the overloaded functions
         * because there 'this' has a different meaning.
         */   
        var that = this;  

        /* 
         * Define three overloaded functions
         */
        var f1 = function(arg1) {
            console.log("f1 called with " + arg1);
            return arg1 + that.x;
        }

        var f2 = function(arg1, arg2) {
             console.log("f2 called with " + arg1 + " and " + arg2);
             return arg1 + arg2 + that.x;
        }

        var f3 = function(arg1) {
             console.log("f3 called with [" + arg1[0] + ", " + arg1[1] + "]");
             return arg1[0] + arg1[1];
        }

        /*
         * Use the arguments array-like object to decide which function to execute when calling f(...)
         */
        if (arguments.length === 1 && !Array.isArray(arguments[0])) {
            return f1(arguments[0]);
        } else if (arguments.length === 2) {
            return f2(arguments[0], arguments[1]);
        } else if (arguments.length === 1 && Array.isArray(arguments[0])) {
            return f3(arguments[0]);
        }
    } 
}

/* 
 * Instantiate an object
 */
var obj = new foo("z");

/*
 * Call the overloaded functions using f(...)
 */
console.log(obj.f("x"));         // executes f1, returns "xz"
console.log(obj.f("x", "y"));    // executes f2, returns "xyz"
console.log(obj.f(["x", "y"]));  // executes f3, returns "xy"
sarang catur
sumber
2
@Luis: Saya telah menambahkan beberapa komentar mudah-mudahan bermanfaat.
chessweb
6

Saya baru saja mencoba ini, mungkin itu sesuai dengan kebutuhan Anda. Bergantung pada jumlah argumen, Anda dapat mengakses fungsi yang berbeda. Anda menginisialisasi saat pertama kali Anda menyebutnya. Dan peta fungsi disembunyikan di penutupan.

TEST = {};

TEST.multiFn = function(){
    // function map for our overloads
    var fnMap = {};

    fnMap[0] = function(){
        console.log("nothing here");
        return this;    //    support chaining
    }

    fnMap[1] = function(arg1){
        //    CODE here...
        console.log("1 arg: "+arg1);
        return this;
    };

    fnMap[2] = function(arg1, arg2){
        //    CODE here...
        console.log("2 args: "+arg1+", "+arg2);
        return this;
    };

    fnMap[3] = function(arg1,arg2,arg3){
        //    CODE here...
        console.log("3 args: "+arg1+", "+arg2+", "+arg3);
        return this;
    };

    console.log("multiFn is now initialized");

    //    redefine the function using the fnMap in the closure
    this.multiFn = function(){
        fnMap[arguments.length].apply(this, arguments);
        return this;
    };

    //    call the function since this code will only run once
    this.multiFn.apply(this, arguments);

    return this;    
};

Menguji.

TEST.multiFn("0")
    .multiFn()
    .multiFn("0","1","2");
AntouanK
sumber
5

Karena JavaScript tidak memiliki fungsi, objek opsi overload dapat digunakan sebagai gantinya. Jika ada satu atau dua argumen yang diperlukan, lebih baik memisahkannya dari objek opsi. Berikut adalah contoh tentang cara menggunakan objek opsi dan nilai yang dihuni ke nilai default jika nilai tidak diteruskan dalam objek opsi.

    function optionsObjectTest(x, y, opts) {
        opts = opts || {}; // default to an empty options object

        var stringValue = opts.stringValue || "string default value";
        var boolValue = !!opts.boolValue; // coerces value to boolean with a double negation pattern
        var numericValue = opts.numericValue === undefined ? 123 : opts.numericValue;

        return "{x:" + x + ", y:" + y + ", stringValue:'" + stringValue + "', boolValue:" + boolValue + ", numericValue:" + numericValue + "}";

}

di sini adalah contoh tentang cara menggunakan objek opsi

Vlad Bezden
sumber
4

Tidak ada cara untuk berfungsi kelebihan dalam javascript. Jadi, saya sarankan seperti berikut dengan typeof()metode alih-alih beberapa fungsi ke pemuatan berlebih palsu.

function multiTypeFunc(param)
{
    if(typeof param == 'string') {
        alert("I got a string type parameter!!");
     }else if(typeof param == 'number') {
        alert("I got a number type parameter!!");
     }else if(typeof param == 'boolean') {
        alert("I got a boolean type parameter!!");
     }else if(typeof param == 'object') {
        alert("I got a object type parameter!!");
     }else{
        alert("error : the parameter is undefined or null!!");
     }
}

Semoga berhasil!

Smith Lee
sumber
24
Demi tuhan! Gunakan pernyataan beralih!
jedmao
8
Juga, jika Anda bersikeras tidak menggunakan sakelar, Anda hanya perlu memanggil satu kali. var type = typeof param; if (type === 'string') ...
Walter Stabosz
+1 memberi komentar untuk "===". Keuntungan lain dari pernyataan switch jika (... == ...) adalah tipe-safe.
Nathan Cooper
4

PENGANTAR

Sejauh ini membaca begitu banyak jawaban akan membuat orang sakit kepala. Siapa pun yang ingin tahu konsepnya perlu mengetahui prasyarat berikut .

Function overloading Definition, Function Length property,Function argument property

Function overloadingdalam bentuknya yang paling sederhana berarti bahwa suatu fungsi melakukan tugas yang berbeda berdasarkan jumlah argumen yang diteruskan kepadanya. Khususnya TASK1, TASK2 dan TASK3 disorot di bawah ini dan dilakukan berdasarkan jumlah argumentsyang diteruskan ke fungsi yang sama fooYo.

// if we have a function defined below
function fooYo(){
     // do something here
}
// on invoking fooYo with different number of arguments it should be capable to do different things

fooYo();  // does TASK1
fooYo('sagar'); // does TASK2
fooYo('sagar','munjal'); // does TAKS3

CATATAN - JS tidak memberikan kemampuan kelebihan fungsi yang bawaan.

Alternatif

John E Resig (pembuat JS) telah menunjukkan alternatif yang menggunakan prasyarat di atas untuk mencapai kemampuan untuk mengimplementasikan fungsi overloading.

Kode di bawah ini menggunakan pendekatan langsung tetapi naif dengan menggunakan if-elseatau switchpernyataan.

  • mengevaluasi argument-lengthproperti.
  • nilai yang berbeda menghasilkan permintaan fungsi yang berbeda.

var ninja = {
  whatever: function() {
       switch (arguments.length) {
         case 0:
           /* do something */
           break;
         case 1:
           /* do something else */
           break;
         case 2:
           /* do yet something else */
           break;
       //and so on ...
    } 
  }
}

Teknik lain jauh lebih bersih dan dinamis. Sorotan teknik ini adalah addMethodfungsi generik.

  • kita mendefinisikan fungsi addMethodyang digunakan untuk menambahkan fungsi yang berbeda ke objek dengan nama yang sama tetapi fungsi yang berbeda .

  • di bawah addMethodfungsi menerima tiga nama objek params object, nama fungsi namedan fungsi yang ingin kita panggil fn.

  • Di dalam addMethoddefinisi var oldmenyimpan referensi ke yang sebelumnya functiondisimpan dengan bantuan penutupan - gelembung pelindung.

function addMethod(object, name, fn) {
  var old = object[name];
  object[name] = function(){
    if (fn.length == arguments.length)
      return fn.apply(this, arguments)
    else if (typeof old == 'function')
      return old.apply(this, arguments);
  };
};

  • gunakan debugger untuk memahami aliran kode.
  • di bawah ini addMethodmenambahkan tiga fungsi yang ketika dipanggil digunakan ninja.whatever(x)dengan jumlah argumen xyang bisa berupa apa saja yaitu kosong atau satu atau lebih dari satu memanggil fungsi yang berbeda seperti yang didefinisikan saat memanfaatkan addMethodfungsi.

var ninja = {};
debugger;


addMethod(ninja,'whatever',function(){ console.log("I am the one with ZERO arguments supplied") });
addMethod(ninja,'whatever',function(a){ console.log("I am the one with ONE arguments supplied") });
addMethod(ninja,'whatever',function(a,b){ console.log("I am the one with TWO arguments supplied") });


ninja.whatever();
ninja.whatever(1,2);
ninja.whatever(3);

Sagar Munjal
sumber
4

Cara lain untuk mendekati ini adalah dengan menggunakan variabel khusus: argumen , ini adalah implementasi:

function sum() {
    var x = 0;
    for (var i = 0; i < arguments.length; ++i) {
        x += arguments[i];
    }
    return x;
}

sehingga Anda dapat mengubah kode ini menjadi:

function sum(){
    var s = 0;
    if (typeof arguments[0] !== "undefined") s += arguments[0];
.
.
.
    return s;
}
Bashir Abdelwahed
sumber
3

lihat ini. Itu sangat keren. http://ejohn.org/blog/javascript-method-overloading/ Trik Javascript untuk memungkinkan Anda melakukan panggilan seperti ini:

var users = new Users();
users.find(); // Finds all
users.find("John"); // Finds users by name
users.find("John", "Resig"); // Finds users by first and last name
Jaider
sumber
Halo Jaider, periksa jawaban saya, ini berisi kode untuk overloading metode javascript yang sebenarnya . Saya sedang berbicara Func(new Point())dan Func(new Rectangle())akan menjalankan fungsi yang berbeda. Tetapi saya harus menunjukkan bahwa ini adalah hack kotor, karena metode overloading sebenarnya adalah tugas kompilasi waktu tidak berjalan waktu.
Keldon Alleyne
3

Fungsi overloading di Javascript:

Function overloading adalah kemampuan bahasa pemrograman untuk membuat banyak fungsi dengan nama yang sama dengan implementasi yang berbeda. ketika fungsi kelebihan beban dipanggil akan menjalankan fungsi implementasi spesifik dari fungsi yang sesuai dengan konteks panggilan. Konteks ini biasanya jumlah argumen yang diterima, dan memungkinkan satu pemanggilan fungsi untuk berperilaku berbeda tergantung pada konteks.

Javascript tidak memiliki kelebihan fungsi bawaan. Namun, perilaku ini dapat ditiru dengan banyak cara. Inilah yang sederhana dan nyaman:

function sayHi(a, b) {
  console.log('hi there ' + a);
  if (b) { console.log('and ' + b) } // if the parameter is present, execute the block
}

sayHi('Frank', 'Willem');

Dalam skenario di mana Anda tidak tahu berapa banyak argumen yang akan Anda dapatkan, Anda dapat menggunakan operator lainnya yang terdiri dari tiga titik .... Ini akan mengubah sisa argumen menjadi sebuah array. Waspadalah terhadap kompatibilitas browser. Berikut ini sebuah contoh:

function foo (a, ...b) {
  console.log(b);
}

foo(1,2,3,4);
foo(1,2);

Willem van der Veen
sumber
2

Karena posting ini sudah mengandung banyak solusi yang berbeda, saya pikir saya memposting yang lain.

function onlyUnique(value, index, self) {
    return self.indexOf(value) === index;
}

function overload() {
   var functions = arguments;
   var nroffunctionsarguments = [arguments.length];
    for (var i = 0; i < arguments.length; i++) {
        nroffunctionsarguments[i] = arguments[i].length;
    }
    var unique = nroffunctionsarguments.filter(onlyUnique);
    if (unique.length === arguments.length) {
        return function () {
            var indexoffunction = nroffunctionsarguments.indexOf(arguments.length);
            return functions[indexoffunction].apply(this, arguments);
        }
    }
    else throw new TypeError("There are multiple functions with the same number of parameters");

}

ini dapat digunakan seperti yang ditunjukkan di bawah ini:

var createVector = overload(
        function (length) {
            return { x: length / 1.414, y: length / 1.414 };
        },
        function (a, b) {
            return { x: a, y: b };
        },
        function (a, b,c) {
            return { x: a, y: b, z:c};
        }
    );
console.log(createVector(3, 4));
console.log(createVector(3, 4,5));
console.log(createVector(7.07));

Solusi ini tidak sempurna tetapi saya hanya ingin menunjukkan bagaimana itu bisa dilakukan.

remyH
sumber
2

Anda dapat menggunakan 'addMethod' dari John Resig. Dengan metode ini Anda dapat "membebani" metode berdasarkan jumlah argumen.

// addMethod - By John Resig (MIT Licensed)
function addMethod(object, name, fn){
    var old = object[ name ];
    object[ name ] = function(){
        if ( fn.length == arguments.length )
            return fn.apply( this, arguments );
        else if ( typeof old == 'function' )
            return old.apply( this, arguments );
    };
}

Saya juga telah membuat alternatif untuk metode ini yang menggunakan caching untuk menyimpan variasi fungsi. Perbedaannya dijelaskan di sini

// addMethod - By Stavros Ioannidis
function addMethod(obj, name, fn) {
  obj[name] = obj[name] || function() {
    // get the cached method with arguments.length arguments
    var method = obj[name].cache[arguments.length];

    // if method exists call it 
    if ( !! method)
      return method.apply(this, arguments);
    else throw new Error("Wrong number of arguments");
  };

  // initialize obj[name].cache
  obj[name].cache = obj[name].cache || {};

  // Check if a method with the same number of arguments exists  
  if ( !! obj[name].cache[fn.length])
    throw new Error("Cannot define multiple '" + name +
      "' methods with the same number of arguments!");

  // cache the method with fn.length arguments
  obj[name].cache[fn.length] = function() {
    return fn.apply(this, arguments);
  };
}
istavros
sumber
2

Pola Penerusan => praktik terbaik tentang JS overloading

Teruskan ke fungsi lain yang namanya dibangun dari poin ke-3 & ke-4:

  1. Menggunakan sejumlah argumen
  2. Memeriksa jenis argumen
window['foo_'+arguments.length+'_'+Array.from(arguments).map((arg)=>typeof arg).join('_')](...arguments)

Aplikasi pada kasus Anda:

 function foo(){
          return window['foo_'+arguments.length+Array.from(arguments).map((arg)=>typeof arg).join('_')](...arguments);

  }
   //------Assuming that `x` , `y` and `z` are String when calling `foo` . 

  /**-- for :  foo(x)*/
  function foo_1_string(){
  }
  /**-- for : foo(x,y,z) ---*/
  function foo_3_string_string_string(){

  }

Sampel Kompleks lainnya:

      function foo(){
          return window['foo_'+arguments.length+Array.from(arguments).map((arg)=>typeof arg).join('_')](...arguments);
       }

        /** one argument & this argument is string */
      function foo_1_string(){

      }
       //------------
       /** one argument & this argument is object */
      function foo_1_object(){

      }
      //----------
      /** two arguments & those arguments are both string */
      function foo_2_string_string(){

      }
       //--------
      /** Three arguments & those arguments are : id(number),name(string), callback(function) */
      function foo_3_number_string_function(){
                let args=arguments;
                  new Person(args[0],args[1]).onReady(args[3]);
      }

       //--- And so on ....   
Abdennour TOUMI
sumber
2

Function Overloading melalui Dynamic Polymorphism dalam 100 baris JS

Ini dari tubuh yang lebih besar dari kode yang meliputi isFn, isArr, dll jenis fungsi pemeriksaan. Versi VanillaJS di bawah ini telah dikerjakan ulang untuk menghapus semua dependensi eksternal, namun Anda harus menentukan fungsi memeriksa jenis Anda sendiri untuk digunakan dalam .add()panggilan.

Catatan: Ini adalah fungsi self-executing (sehingga kita dapat memiliki lingkup penutupan / tertutup), karenanya tugas untuk window.overloaddaripada function overload() {...}.

window.overload = function () {
    "use strict"

    var a_fnOverloads = [],
        _Object_prototype_toString = Object.prototype.toString
    ;

    function isFn(f) {
        return (_Object_prototype_toString.call(f) === '[object Function]');
    } //# isFn

    function isObj(o) {
        return !!(o && o === Object(o));
    } //# isObj

    function isArr(a) {
        return (_Object_prototype_toString.call(a) === '[object Array]');
    } //# isArr

    function mkArr(a) {
        return Array.prototype.slice.call(a);
    } //# mkArr

    function fnCall(fn, vContext, vArguments) {
        //# <ES5 Support for array-like objects
        //#     See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply#Browser_compatibility
        vArguments = (isArr(vArguments) ? vArguments : mkArr(vArguments));

        if (isFn(fn)) {
            return fn.apply(vContext || this, vArguments);
        }
    } //# fnCall

    //# 
    function registerAlias(fnOverload, fn, sAlias) {
        //# 
        if (sAlias && !fnOverload[sAlias]) {
            fnOverload[sAlias] = fn;
        }
    } //# registerAlias

    //# 
    function overload(vOptions) {
        var oData = (isFn(vOptions) ?
                { default: vOptions } :
                (isObj(vOptions) ?
                    vOptions :
                    {
                        default: function (/*arguments*/) {
                            throw "Overload not found for arguments: [" + mkArr(arguments) + "]";
                        }
                    }
                )
            ),
            fnOverload = function (/*arguments*/) {
                var oEntry, i, j,
                    a = arguments,
                    oArgumentTests = oData[a.length] || []
                ;

                //# Traverse the oArgumentTests for the number of passed a(rguments), defaulting the oEntry at the beginning of each loop
                for (i = 0; i < oArgumentTests.length; i++) {
                    oEntry = oArgumentTests[i];

                    //# Traverse the passed a(rguments), if a .test for the current oArgumentTests fails, reset oEntry and fall from the a(rgument)s loop
                    for (j = 0; j < a.length; j++) {
                        if (!oArgumentTests[i].tests[j](a[j])) {
                            oEntry = undefined;
                            break;
                        }
                    }

                    //# If all of the a(rgument)s passed the .tests we found our oEntry, so break from the oArgumentTests loop
                    if (oEntry) {
                        break;
                    }
                }

                //# If we found our oEntry above, .fn.call its .fn
                if (oEntry) {
                    oEntry.calls++;
                    return fnCall(oEntry.fn, this, a);
                }
                //# Else we were unable to find a matching oArgumentTests oEntry, so .fn.call our .default
                else {
                    return fnCall(oData.default, this, a);
                }
            } //# fnOverload
        ;

        //# 
        fnOverload.add = function (fn, a_vArgumentTests, sAlias) {
            var i,
                bValid = isFn(fn),
                iLen = (isArr(a_vArgumentTests) ? a_vArgumentTests.length : 0)
            ;

            //# 
            if (bValid) {
                //# Traverse the a_vArgumentTests, processinge each to ensure they are functions (or references to )
                for (i = 0; i < iLen; i++) {
                    if (!isFn(a_vArgumentTests[i])) {
                        bValid = _false;
                    }
                }
            }

            //# If the a_vArgumentTests are bValid, set the info into oData under the a_vArgumentTests's iLen
            if (bValid) {
                oData[iLen] = oData[iLen] || [];
                oData[iLen].push({
                    fn: fn,
                    tests: a_vArgumentTests,
                    calls: 0
                });

                //# 
                registerAlias(fnOverload, fn, sAlias);

                return fnOverload;
            }
            //# Else one of the passed arguments was not bValid, so throw the error
            else {
                throw "poly.overload: All tests must be functions or strings referencing `is.*`.";
            }
        }; //# overload*.add

        //# 
        fnOverload.list = function (iArgumentCount) {
            return (arguments.length > 0 ? oData[iArgumentCount] || [] : oData);
        }; //# overload*.list

        //# 
        a_fnOverloads.push(fnOverload);
        registerAlias(fnOverload, oData.default, "default");

        return fnOverload;
    } //# overload

    //# 
    overload.is = function (fnTarget) {
        return (a_fnOverloads.indexOf(fnTarget) > -1);
    } //# overload.is

    return overload;
}();

Pemakaian:

Penelepon mendefinisikan fungsi kelebihannya dengan menetapkan variabel untuk kembalinya overload(). Berkat rantai, kelebihan beban tambahan dapat didefinisikan secara seri:

var myOverloadedFn = overload(function(){ console.log("default", arguments) })
    .add(function(){ console.log("noArgs", arguments) }, [], "noArgs")
    .add(function(){ console.log("str", arguments) }, [function(s){ return typeof s === 'string' }], "str")
;

Argumen opsional tunggal untuk overload()menetapkan fungsi "default" untuk dipanggil jika tanda tangan tidak dapat diidentifikasi. Argumen untuk .add()adalah:

  1. fn: functionmendefinisikan kelebihan;
  2. a_vArgumentTests: Arrayof functions mendefinisikan tes untuk dijalankan pada arguments. Masing-masing functionmenerima satu argumen dan mengembalikan trueargumen Anda jika argumen itu valid;
  3. sAlias(Opsional): stringmendefinisikan alias untuk secara langsung mengakses fungsi overload ( fn), mis. myOverloadedFn.noArgs()Akan memanggil fungsi itu secara langsung, menghindari tes polimorfisme dinamis dari argumen.

Implementasi ini sebenarnya memungkinkan lebih dari sekedar kelebihan fungsi tradisional karena a_vArgumentTestsargumen kedua .add()dalam praktiknya mendefinisikan tipe kustom. Jadi, Anda bisa membuat gerbang argumen tidak hanya berdasarkan jenis, tetapi pada rentang, nilai, atau koleksi nilai!

Jika Anda melihat melalui 145 baris kode untuk overload()Anda akan melihat bahwa setiap tanda tangan dikategorikan berdasarkan jumlah yang argumentsditeruskan ke sana. Ini dilakukan agar kami membatasi jumlah tes yang kami jalankan. Saya juga melacak jumlah panggilan. Dengan beberapa kode tambahan, array fungsi yang kelebihan beban dapat disortir ulang sehingga fungsi yang lebih umum diuji terlebih dahulu, kembali menambahkan beberapa ukuran peningkatan kinerja.

Sekarang, ada beberapa peringatan ... Karena Javascript diketik secara longgar, Anda harus berhati-hati dengan Anda vArgumentTestskarena integerdapat divalidasi sebagai float, dll.

Versi JSCompress.com (1114 byte, 744 byte g-zip):

window.overload=function(){'use strict';function b(n){return'[object Function]'===m.call(n)}function c(n){return!!(n&&n===Object(n))}function d(n){return'[object Array]'===m.call(n)}function e(n){return Array.prototype.slice.call(n)}function g(n,p,q){if(q=d(q)?q:e(q),b(n))return n.apply(p||this,q)}function h(n,p,q){q&&!n[q]&&(n[q]=p)}function k(n){var p=b(n)?{default:n}:c(n)?n:{default:function(){throw'Overload not found for arguments: ['+e(arguments)+']'}},q=function(){var r,s,t,u=arguments,v=p[u.length]||[];for(s=0;s<v.length;s++){for(r=v[s],t=0;t<u.length;t++)if(!v[s].tests[t](u[t])){r=void 0;break}if(r)break}return r?(r.calls++,g(r.fn,this,u)):g(p.default,this,u)};return q.add=function(r,s,t){var u,v=b(r),w=d(s)?s.length:0;if(v)for(u=0;u<w;u++)b(s[u])||(v=_false);if(v)return p[w]=p[w]||[],p[w].push({fn:r,tests:s,calls:0}),h(q,r,t),q;throw'poly.overload: All tests must be functions or strings referencing `is.*`.'},q.list=function(r){return 0<arguments.length?p[r]||[]:p},l.push(q),h(q,p.default,'default'),q}var l=[],m=Object.prototype.toString;return k.is=function(n){return-1<l.indexOf(n)},k}();
Campbeln
sumber
2

Anda sekarang dapat melakukan fungsi overloading di ECMAScript 2018 tanpa polyfill, memeriksa var length / type, dll., Cukup gunakan sintaks spread .

function foo(var1, var2, opts){
  // set default values for parameters
  const defaultOpts = {
    a: [1,2,3],
    b: true,
    c: 0.3289,
    d: "str",
  }
  // merge default and passed-in parameters
  // defaultOpts must go first!
  const mergedOpts = {...defaultOpts, ...opts};

  // you can now refer to parameters like b as mergedOpts.b,
  // or just assign mergedOpts.b to b
  console.log(mergedOpts.a);
  console.log(mergedOpts.b);
  console.log(mergedOpts.c);  
  console.log(mergedOpts.d);
}
// the parameters you passed in override the default ones
// all JS types are supported: primitives, objects, arrays, functions, etc.
let var1, var2="random var";
foo(var1, var2, {a: [1,2], d: "differentString"});

// parameter values inside foo:
//a: [1,2]
//b: true
//c: 0.3289
//d: "differentString"

Apa itu sintaksis penyebaran?

Properti Istirahat / Sebarkan proposal ECMAScript (tahap 4) menambahkan properti sebar ke objek literal. Ini menyalin properti enumerable sendiri dari objek yang disediakan ke objek baru. Lebih lanjut tentang mdn

Catatan: menyebar sintaks dalam objek literal tidak berfungsi di Edge dan IE dan itu adalah fitur eksperimental. lihat kompatibilitas browser

AlienKevin
sumber
2

Hal seperti ini dapat dilakukan untuk fungsi yang berlebihan.

function addCSS(el, prop, val) {
  return {
    2: function() {
      // when two arguments are set
      // now prop is an oject
      for (var i in prop) {
          el.style[i] = prop[i];
      }
    },
    3: function() {
      // when three arguments are set
      el.style[prop] = val;
    }
    }[arguments.length]();
}
// usage
var el = document.getElementById("demo");
addCSS(el, "color", "blue");
addCSS(el, {
    "backgroundColor": "black",
  "padding": "10px"
});

Sumber

Supun Kavinda
sumber
1

Ini adalah pertanyaan lama tetapi satu yang saya pikir perlu entri lain (walaupun saya ragu ada yang akan membacanya). Penggunaan Ekspresi Fungsi Segera Diminta (IIFE) dapat digunakan bersama dengan fungsi penutupan dan inline untuk memungkinkan kelebihan fungsi. Pertimbangkan contoh berikut (dibuat-buat):

var foo;

// original 'foo' definition
foo = function(a) {
  console.log("a: " + a);
}

// define 'foo' to accept two arguments
foo = (function() {
  // store a reference to the previous definition of 'foo'
  var old = foo;

  // use inline function so that you can refer to it internally
  return function newFoo(a,b) {

    // check that the arguments.length == the number of arguments 
    // defined for 'newFoo'
    if (arguments.length == newFoo.length) {
      console.log("a: " + a);
      console.log("b: " + b);

    // else if 'old' is a function, apply it to the arguments
    } else if (({}).toString.call(old) === '[object Function]') {
      old.apply(null, arguments);
    }
  }
})();

foo(1);
> a: 1
foo(1,2);
> a: 1
> b: 2
foo(1,2,3)
> a: 1

Singkatnya, penggunaan IIFE menciptakan ruang lingkup lokal, memungkinkan kita untuk mendefinisikan variabel pribadi olduntuk menyimpan referensi ke definisi awal fungsi foo. Fungsi ini kemudian mengembalikan fungsi inline newFooyang mencatat konten dari kedua argumen jika dilewatkan tepat dua argumen adan batau memanggil oldfungsi jika arguments.length !== 2. Pola ini dapat diulang beberapa kali untuk memberikan satu variabel dengan beberapa definisi fungsional yang berbeda.

wvandaal
sumber
1

Saya ingin membagikan contoh berguna tentang pendekatan seperti kelebihan beban.

function Clear(control)
{
  var o = typeof control !== "undefined" ? control : document.body;
  var children = o.childNodes;
  while (o.childNodes.length > 0)
    o.removeChild(o.firstChild);
}

Penggunaan: Hapus (); // Membersihkan semua dokumen

Bersihkan (myDiv); // Membersihkan panel yang direferensikan oleh myDiv

Alexander Chernosvitov
sumber
1

JavaScript adalah bahasa yang tidak diketik, dan saya hanya berpikir itu masuk akal untuk membebani metode / fungsi sehubungan dengan jumlah params. Oleh karena itu, saya akan merekomendasikan untuk memeriksa apakah parameternya telah ditentukan:

myFunction = function(a, b, c) {
     if (b === undefined && c === undefined ){
          // do x...
     }
     else {
          // do y...
     }
};
Tony
sumber
1
Hanya ingin mencatat bahwa tidak ditandai bukan berarti "tidak ada tipe".
hamdiakoguz
1

Pada Juli 2017, berikut ini adalah teknik umum. Perhatikan bahwa kami juga dapat melakukan pemeriksaan tipe dalam fungsi.

function f(...rest){   // rest is an array
   console.log(rest.length);
   for (v of rest) if (typeof(v)=="number")console.log(v);
}
f(1,2,3);  // 3 1 2 3
Chong Lip Phang
sumber
1

Untuk kasus penggunaan Anda, ini adalah bagaimana saya akan mengatasinya ES6(karena ini sudah akhir tahun 2017):

const foo = (x, y, z) => {
  if (y && z) {
    // Do your foo(x, y, z); functionality
    return output;
  }
  // Do your foo(x); functionality
  return output;
}

Anda dapat dengan jelas mengadaptasi ini untuk bekerja dengan sejumlah parameter dan hanya mengubah pernyataan bersyarat Anda sesuai.

Barry Michael Doyle
sumber
1

tidak ada overloading aktual di JS, toh kita masih bisa mensimulasikan metode overloading dalam beberapa cara:

metode # 1: gunakan objek

function test(x,options){
  if("a" in options)doSomething();
  else if("b" in options)doSomethingElse();
}
test("ok",{a:1});
test("ok",{b:"string"});

metode # 2: gunakan parameter sisa (sebaran)

function test(x,...p){
 if(p[2])console.log("3 params passed"); //or if(typeof p[2]=="string")
else if (p[1])console.log("2 params passed");
else console.log("1 param passed");
}

Metode # 3: gunakan undefined

function test(x, y, z){
 if(typeof(z)=="undefined")doSomething();
}

metode # 4: memeriksa jenis

function test(x){
 if(typeof(x)=="string")console.log("a string passed")
 else ...
}
xx yy
sumber
1

Meskipun parameter Default tidak kelebihan beban, ini mungkin bisa menyelesaikan beberapa masalah yang dihadapi pengembang di area ini. Input benar-benar ditentukan oleh pesanan, Anda tidak dapat memesan ulang seperti yang Anda inginkan seperti pada overloading klasik:

function transformer(
    firstNumber = 1,
    secondNumber = new Date().getFullYear(),
    transform = function multiply(firstNumber, secondNumber) {
        return firstNumber * secondNumber;
    }
) {
    return transform(firstNumber, secondNumber);
}

console.info(transformer());
console.info(transformer(8));
console.info(transformer(2, 6));
console.info(transformer(undefined, 65));

function add(firstNumber, secondNumber) {
    return firstNumber + secondNumber;
}
console.info(transformer(undefined, undefined, add));
console.info(transformer(3, undefined, add));

Hasil dalam (untuk tahun 2020):

2020
16160
12
65
2021
2023

Info lebih lanjut: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters

Karl Morrison
sumber
0

Opsi pertama benar-benar layak mendapat perhatian karena itu adalah hal yang saya buat dalam pengaturan kode yang cukup kompleks. Jadi, jawabanku adalah

  1. Menggunakan nama yang berbeda di tempat pertama

Dengan sedikit tapi petunjuk penting, nama akan terlihat berbeda untuk komputer, tetapi tidak untuk Anda. Nama kelebihan fungsi seperti: func, func1, func2.

alehro
sumber
Saya akan mencoba overloading tetapi memutuskan untuk hanya menggunakan nama yang berbeda, misalnya getDeviceInfoByID dan getDeviceInfoByType ...
CramerTV