Javascript'te aşırı işlev yüklemesi - En iyi uygulamalar


783

Javascript'te sahte fonksiyon aşırı yüklemesinin en iyi yolu nedir?

Javascript işlevlerinin diğer dillerde olduğu gibi aşırı yüklenmesinin mümkün olmadığını biliyorum. İki kullanımlı bir fonksiyona ihtiyacım varsa foo(x)ve foo(x,y,z)en iyi / tercih edilen yol ise:

  1. İlk etapta farklı isimler kullanmak
  2. Gibi isteğe bağlı argümanlar kullanma y = y || 'default'
  3. Bağımsız değişken sayısını kullanma
  4. Argüman türlerini kontrol etme
  5. Veya nasıl?

14
Başlamak için neden fonksiyon aşırı yüklemesine ihtiyacınız olduğunu düşündüğünüzü sormak faydalı olabilir. Bu bizi gerçek bir çözüme yaklaştırır diye düşünüyorum.
Breton

1
Bu kapalı, ancak aşağıdakileri yaparım: this.selectBy = {örnek: selectByInstance, // İşlev metni: selectByText, // İşlev değeri: selectByValue // Function};
Mahkum ZERO

Benim cevabım, çalışma süresi fonksiyonunun aşırı yüklenmesinin nasıl yapıldığını gösterir, bir hız cezası vardır ve Javascript'in spesifikasyonunu aşmak için bunu tavsiye etmem. Fonksiyon aşırı yüklenmesi gerçekten derleme zamanı bir görevdir, sadece akademik amaçlar için cevap veriyorum ve kodda kullanılıp kullanılmayacağı konusunda kendi takdirinize bırakıyorum.
Keldon Alleyne

2
Yararlı olması durumunda, tür tabanlı yöntem aşırı yüklemesine izin veren hafif bir js çerçevesi oluşturdum. Açıkçası aynı uyarılar performans açısından da geçerlidir, ancak şu ana kadar ihtiyaçlarım için iyi çalıştı ve hala iyileştirilmesi gereken çok şey var: blog.pebbl.co.uk/2013/01/describejs.html#methodoverloading
Pebbl

Yanıtlar:


602

Parametrelerle fonksiyon aşırı yüklemesi yapmanın en iyi yolu, argüman uzunluğunu veya türlerini kontrol etmektir; türleri kontrol etmek kodunuzu yavaşlatacak ve Diziler, boş değerler, Nesneler vb.

Çoğu geliştiricinin yaptığı şey, yöntemlerine son argüman olarak bir nesneye yapışmaktır. Bu nesne her şeyi tutabilir.

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

Daha sonra yönteminizde istediğiniz şekilde işleyebilirsiniz. [Anahtar, eğer varsa vb.]


43
Bu "opts" parametrelerinin nasıl kullanıldığını / referans verildiğini gösteren foo () uygulamasının örnek bir uygulamasını sağlayabilir misiniz?
Moe Howard

24
Moe // Böyle olabilir; if(opts['test']) //if test param exists, do something.. if(opts['bar']) //if bar param exists, do something
Deckard

80
Bu fonksiyon aşırı yüklenmesi değil. İşlev aşırı yüklenmesi, aynı ada ancak farklı parametrelere sahip iki ayrı işleve sahiptir. Açıkladığınız şey, sonunda bir nesne argümanına sahip tek bir işlevdir.
d512

66
@ user1334007 Java / .NET'de yaptığınız gibi aşırı fonksiyon yüklemesi yapmak imkansızdır. Evet, bu "tam olarak" aşırı yüklenme değildir, ancak işi yapar.
epascarello

18
Kimse bunu zaten sormadı şaşırttı: neden kontrol arguments.lengthtavsiye edilmiyor? Ayrıca, daha önce burada bulundum ve çoğu geliştiricinin yaptığı şeyleri okudum ... , ama eminim burası gördüğüm tek yer. Bu yöntem aynı zamanda 'aşırı yüklenmeler' sözdizimsel şekerliğini bozar!
c24w

169

Bunu sık sık yaparım:

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

JavaScript Eşdeğeri:

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

Bu özel örnek, javascript'te aslında C # 'den daha zariftir. Belirtilmeyen parametreler, bir if deyiminde false olarak değerlendirilen javascript içinde 'undefined' şeklindedir. Bununla birlikte, işlev tanımı p2 ve p3'ün isteğe bağlı olduğu bilgisini iletmez. Çok fazla yüklemeye ihtiyacınız varsa, jQuery bir nesne parametre olarak kullanmaya karar verdi, örneğin, jQuery.ajax (seçenekler). Onlara, aşırı yüklenmeye en güçlü ve açıkça belgelendirilebilir yaklaşım olduğuna katılıyorum, ancak nadiren bir veya iki hızlı isteğe bağlı parametreye ihtiyacım var.

EDIT: Ian'ın önerisi doğrultusunda IF testini değiştirdi


16
Belirtilmeyen parametreler undefinedJS'de değil, JS'de bulunmaktadır null. En iyi uygulama olarak, asla bir şey ayarlamamalısınız undefined, bu yüzden testinizi değiştirdiğiniz sürece sorun olmamalıdır p2 === undefined.
Tamzin Blake

3
falseSon argüman olarak geçerseniz "false", sonuna kadar birleştirilmez , çünkü if(p3)dallanmaz.
dreamlax

5
Sadece hızlı bir not, typeof p2 === "undefined"muhtemelen örneğinizde beklediğiniz şeyin tersidir, bence typeof p2 !== "undefined"amaçladığınız şey budur. Ayrıca, aslında yaptığınız dize, sayı ve boole birleştirmek gerekiyordu öneririzp2 === "number"; p3 === "boolean"
WillFM

8
Bunu yapmayı seviyorum: p3 = p3 || 'varsayılan değer';
Dorian

3
Anlamı nedir ===ve !==? Neden sadece ==ve değil !=?
Ricardo Cruz

76

Herhangi bir türden herhangi bir sayıda parametrenin geçmesine izin verdiği için JavaScript'te gerçek bir aşırı yükleme yoktur. İşlev içinde kaç argümanın iletildiğini ve ne tür olduklarını kontrol etmelisiniz .


1
John Resig (jQuery'den) bir zamanlar bunu denedi, ancak girişim tamamen akademikti ve gerçek bir fayda sağlamadı.
scunliffe

14

@Terrance: Resig'in yöntemini de seviyorum. Mucizevi şekilde çalışır. Kullanım örneklerini doğrulamak için bir test oluşturmanın bir yolunu bulmam gerekiyor.
chrisvillanueva

"Bu işlev dünyayı değiştirmez, ancak kısa, özlüdür ve belirsiz bir JavaScript özelliği kullanır - bu yüzden kitabımda kazanır." :-)
Frerich Raabe

67

Doğru cevap JAVASCRIPT'DE AŞIRI YÜKLEME YOKTUR.

Fonksiyonun içindeki kontrol / anahtarlama aşırı yüklenmiyor.

Aşırı yükleme kavramı: Bazı programlama dillerinde, işlev aşırı yüklemesi veya yöntem aşırı yüklemesi, farklı uygulamalarla aynı adda birden çok yöntem oluşturma yeteneğidir. Aşırı yüklenmiş bir işleve yapılan çağrılar, çağrının bağlamına uygun olarak bu işlevin belirli bir uygulamasını çalıştırarak bir işlev çağrısının bağlama bağlı olarak farklı görevler gerçekleştirmesine izin verir.

Örneğin, doTask () ve doTask (nesne O) aşırı yüklenmiş yöntemlerdir. İkincisini çağırmak için, bir nesne parametre olarak geçirilmelidir, oysa birincisi parametre gerektirmez ve boş bir parametre alanı ile çağrılır. Genel bir hata, derleyicinin iki yöntemden hangisini kullanacağını bilemeyeceğinden, ikinci yöntemde nesneye varsayılan bir değer atamaktır.

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

Önerilen tüm uygulamalar harika, ancak söylenecek gerçek, JavaScript için yerel bir uygulama yok.


4
sonunda normal bir cevap! JAVASCRIPT'DE AŞIRI YÜKLENME YOKTUR.
Razvan Tudorica

4
OP için bir yol istedi sahte aşırı yüklenme.
Ateur Games

Daha önce söylediğim gibi, burada insanları eğitmek için buradayız, sadece sordukları şeyin doğru olduğunu doğrulamadan onlara cevap vermiyoruz.
Marco

27

Buna daha iyi yaklaşmanın iki yolu vardır:

  1. Çok fazla esneklik bırakmak istiyorsanız bir sözlük (ilişkisel dizi) geçirin

  2. Bir nesneyi argüman olarak alın ve esneklik eklemek için prototip tabanlı miras kullanın.


1
ancak bu benim ilk düşüncemdi, eğer oluşturduğunuz işlev bir kütüphanede veya başkaları tarafından kullanılacaksa, değerleri açıkça numaralandırmak yararlı olabilir
roberthuttinger

19

Aşağıda gösterilen parametre türlerini kullanarak gerçek yöntem aşırı yüklemesine izin veren bir yaklaşım:

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

Edit (2018) : Bu, 2011 yılında yazıldığından, aşırı yöntemlerin hızı artmazken, doğrudan yöntem çağrılarının hızı büyük ölçüde arttı.

Tavsiye edebileceğim bir yaklaşım değil, ancak bu tür sorunları nasıl çözebileceğinizi düşünmek faydalı bir düşünce egzersizi.


İşte farklı yaklaşımların bir ölçütü - https://jsperf.com/function-overloading . O bu işlevi (hesaba türlerini alarak) aşırı yükleme etrafında olabilir gösterir 13 kat daha yavaş tarihinde de Chrome'un V8 itibariyle 16.0 (beta) .

Bir nesneyi (yani {x: 0, y: 0}) geçirmenin yanı sıra , uygun olduğunda C yaklaşımını da uygun şekilde adlandırabilir. Örneğin, Vector.AddVector (vektör), Vector.AddIntegers (x, y, z, ...) ve Vector.AddArray (integerArray). İlham vermek için OpenGL gibi C kitaplıklarına bakabilirsiniz.

Düzenleme : Ben bir nesne geçirerek ve ikisini de kullanarak nesne için test etmek için bir kriter ekledik 'param' in argve arg.hasOwnProperty('param')ve fonksiyon aşırı yükleme çok daha hızlı (en azından bu kriter olarak) özellikleri için bir nesne ve denetimi geçen aşıyor.

Tasarım perspektifinden bakıldığında, aşırı yük fonksiyonu yalnızca aşırı yüklenmiş parametreler aynı eyleme karşılık geliyorsa geçerlidir veya mantıklıdır. Bu nedenle, yalnızca belirli ayrıntılarla ilgilenen, aksi takdirde uygun olmayan tasarım seçeneklerini gösterebilecek altta yatan bir yöntem olması gerektiği anlamına gelir. Böylece, ilgili bir nesneye veri dönüştürerek aşırı işlev yükünün kullanımı da çözülebilir. Tabii ki problemin kapsamını düşünmek gerekir, çünkü niyetiniz sadece bir isim yazdırmaksa, ayrıntılı tasarımlar yapmaya gerek yoktur, ancak çerçevelerin ve kütüphanelerin tasarımı için bu düşünce haklıdır.

Örneğim bir Dikdörtgen uygulamasından geliyor - dolayısıyla Boyut ve Noktadan bahsediliyor. Belki de Dikdörtgen ve prototipine bir GetRectangle()yöntem ekleyebilir ve daha sonra işlev aşırı yükleme sorunu sıralanır. Peki ya ilkel öğeler? Nesnelerin bir yöntemi olduğu için geçerli bir test olan argüman uzunluğumuz var .DimensionPointGetRectangle()

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

16

En iyi yol gerçekten işleve ve argümanlara bağlıdır. Seçeneklerinizin her biri farklı durumlarda iyi bir fikirdir. Bunlardan biri çalışana kadar bunları genellikle aşağıdaki sırayla denerim:

  1. Y = y || gibi isteğe bağlı bağımsız değişkenler kullanma 'varsayılan'. Bu, bunu yapabiliyorsanız uygundur, ancak her zaman pratik olarak çalışmayabilir, örneğin 0 / null / undefined geçerli bir argüman olduğunda.

  2. Argüman sayısını kullanma. Son seçeneğe benzer, ancak # 1 çalışmadığında çalışabilir.

  3. Argüman türlerini kontrol etme. Bu, argüman sayısının aynı olduğu bazı durumlarda işe yarayabilir. Türleri güvenilir bir şekilde belirleyemiyorsanız, farklı adlar kullanmanız gerekebilir.

  4. İlk etapta farklı isimler kullanmak. Diğer seçenekler çalışmazsa, pratik değilse veya diğer ilgili işlevlerle tutarlılık için bunu yapmanız gerekebilir.


14

İki kullanımlı bir fonksiyona ihtiyacım olursa en iyi / tercih edilen yöntem foo (x) ve foo (x, y, z)?

Sorun, JavaScript'in yöntem aşırı yüklemesini yerel olarak desteklememesidir. Dolayısıyla, aynı adlara sahip iki veya daha fazla işlevi görür / ayrıştırırsa, yalnızca en son tanımlanan işlevi dikkate alır ve öncekilerin üzerine yazar.

Davanın çoğu için uygun olduğunu düşündüğüm yollardan biri şudur -

Diyelim ki yönteminiz var

function foo(x)
{
} 

Javascript'te mümkün olmayan aşırı yükleme yöntemi yerine yeni bir yöntem tanımlayabilirsiniz

fooNew(x,y,z)
{
}

ve ardından 1. işlevi aşağıdaki gibi değiştirin -

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

Bu tür aşırı yüklenmiş yöntemleriniz varsa, ifadeleri kullanmaktan switchziyade kullanmayı düşünün if-else.

( daha fazla detay )

Not: Yukarıdaki bağlantı, daha fazla ayrıntı içeren kişisel bloguma gidiyor.


9

En iyi uygulamalardan emin değilim, ama işte böyle yapıyorum:

/*
 * 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"

2
@ Louis: Umarım faydalı bazı yorumlar ekledim.
chessweb

6

Ben sadece denedim, belki sizin ihtiyaçlarınıza uygun. Bağımsız değişkenlerin sayısına bağlı olarak farklı bir işleve erişebilirsiniz. İlk aradığınızda onu başlatırsınız. Ve fonksiyon haritası kapanışta gizlidir.

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

Dene.

TEST.multiFn("0")
    .multiFn()
    .multiFn("0","1","2");

5

JavaScript işlev aşırı yükleme seçeneklerine sahip olmadığından, bunun yerine nesne kullanılabilir. Gerekli bir veya iki bağımsız değişken varsa, bunları options nesnesinden ayrı tutmak daha iyidir. Burada, seçenekler nesnesine değer iletilmemişse, seçenekler nesnesinin ve doldurulmuş değerlerin varsayılan değere nasıl kullanılacağına ilişkin bir örnek verilmiştir.

    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 + "}";

}

İşte seçenekler nesnesinin nasıl kullanılacağı ile ilgili bir örnek


4

Javascript'te aşırı yükleme işlevinin bir yolu yoktur. Bu nedenle, typeof()sahte aşırı yüklemeye birden çok işlev yerine aşağıdaki yöntemle tavsiye ederim .

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!!");
     }
}

İyi şanslar!


24
Tanrı aşkına! Bir anahtar deyimi kullanın!
jedmao

8
Ayrıca, bir anahtar kullanmama konusunda ısrar ederseniz, typeof komutunu yalnızca bir kez çağırmalısınız. var type = typeof param; if (type === 'string') ...
Walter Stabosz

"===" için yorum yapmak üzere +1. Anahtar ifadesinin diğer avantajı (... == ...) türünde güvenli olmasıdır.
Nathan Cooper

4

GİRİŞ

Şimdiye kadar çok sayıda cevabı okumanız herkese baş ağrısı verecektir. Kavramı tanımaya çalışan herkes aşağıdaki ön koşulları bilmelidir .

Function overloading Definition, Function Length property,Function argument property

Function overloadingen basit haliyle, bir işlevin kendisine iletilen argüman sayısı temelinde farklı görevleri yerine getirdiği anlamına gelir. Özellikle, TASK1, TASK2 ve TASK3 aşağıda vurgulanmıştır ve argumentsaynı işleve geçme sayısına göre gerçekleştirilmektedir 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

NOT - JS, fonksiyon aşırı yüklemesi için dahili bir yetenek sağlamaz.

Alternatif

John E Resig (JS'nin yaratıcısı), işlev aşırı yüklemesini uygulama yeteneğini elde etmek için yukarıdaki önkoşulları kullanan bir alternatife dikkat çekti.

Aşağıdaki kod, if-elseveya switchdeyimini kullanarak basit ama naif bir yaklaşım kullanır .

  • argument-lengthmülkü değerlendirir .
  • farklı değerler farklı işlevlerin çağrılmasına neden olur.

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

Başka bir teknik çok daha temiz ve dinamik. Bu tekniğin en önemli özelliği addMethodgenel işlevdir.

  • aynı ada ancak farklı işlevlereaddMethod sahip bir nesneye farklı işlevler eklemek için kullanılan bir işlev tanımlarız .

  • addMethodfonksiyonun altında üç params nesne adı object, fonksiyon adı nameve çağrılmak istediğimiz fonksiyon kabul edilir fn.

  • İç addMethodtanım var old, functionbir koruyucu kabarcık olan kapatma yardımı ile saklanan öncekine referansı saklar .

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

  • kod akışını anlamak için hata ayıklayıcı kullanın.
  • Aşağıda, boş veya bir ya da birden fazla herhangi bir şey olabilen bağımsız değişken sayısı ile addMethodçağrıldığında , işlevi kullanırken tanımlanan farklı işlevleri çağıran üç işlev ekler .ninja.whatever(x)xaddMethod

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


4

Buna yaklaşmanın başka bir yolu da şu özel değişkeni kullanmaktır: argümanlar ;

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

böylece bu kodu şu şekilde değiştirebilirsiniz:

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

3

şuna bir bak. Çok havalı. http://ejohn.org/blog/javascript-method-overloading/ Aşağıdaki gibi aramalar yapabilmeniz için Javascript'i al:

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

Merhaba Jaider, cevabımı kontrol et, gerçek javascript yönteminin aşırı yüklenmesi için kod içeriyor . Konuşuyorum Func(new Point())ve Func(new Rectangle())farklı işlevleri yerine getireceğim. Ancak, yöntem aşırı yüklenmesi gerçekten çalışma zamanı değil derleme zamanı görev olduğundan, bu kirli bir kesmek olduğunu belirtmek gerekir.
Keldon Alleyne

3

Javascript ile aşırı fonksiyon:

İşlev aşırı yüklenmesi, bir programlama dilinin farklı uygulamalarla aynı adda birden çok işlev oluşturma yeteneğidir. aşırı yüklenmiş bir fonksiyon çağrıldığında, o fonksiyonun çağrının bağlamına uygun belirli bir uygulamasını çalıştırır. Bu bağlam genellikle alınan argüman miktarıdır ve bir fonksiyon çağrısının bağlama bağlı olarak farklı davranmasına izin verir.

JavaScript gelmez yerleşik fonksiyon aşırı yüklenme. Ancak, bu davranış birçok şekilde taklit edilebilir. İşte kullanışlı bir basit:

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

Kaç argüman alacağınızı bilmediğiniz senaryolarda , üç nokta olan dinlenme işlecini kullanabilirsiniz .... Argümanların geri kalanını bir diziye dönüştürür. Tarayıcı uyumluluğuna dikkat edin. İşte bir örnek:

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

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


2

Bu yazı zaten çok farklı çözümler içerdiğinden, başka bir tane yayınladığımı düşündüm.

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

}

bu aşağıda gösterildiği gibi kullanılabilir:

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

Bu çözüm mükemmel değil ama sadece nasıl yapılabileceğini göstermek istiyorum.


2

John Resig'den 'addMethod'u kullanabilirsiniz. Bu yöntemle, argüman sayısına dayalı olarak "aşırı yük" yöntemleri kullanabilirsiniz.

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

Ayrıca, fonksiyonun varyasyonlarını tutmak için önbellekleme kullanan bu yönteme bir alternatif oluşturduk. Farklılıklar burada açıklanmıştır

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

2

Yönlendirme Deseni => JS aşırı yüklemesi ile ilgili en iyi uygulama

3. ve 4. noktalardan oluşturulmuş başka bir işleve yönlendirme:

  1. Bağımsız değişken sayısını kullanma
  2. Argüman türlerini kontrol etme
window['foo_'+arguments.length+'_'+Array.from(arguments).map((arg)=>typeof arg).join('_')](...arguments)

Davanızda başvuru:

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

  }

Diğer karmaşık örnek:

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

2

100 JS hattında Dinamik Polimorfizm ile Fonksiyon Aşırı Yüklemesi

Buna, daha geniş bir kod gövdeden olup isFn, isArrvb tip kontrol fonksiyonları. Aşağıdaki VanillaJS sürümü, tüm harici bağımlılıkları kaldırmak için elden geçirildi, ancak .add()aramalarda kullanmak için kendi tür kontrol işlevlerinizi tanımlamanız gerekecek .

Not: Bu kendi kendini yürüten bir işlevdir (bu nedenle bir kapatma / kapalı kapsamımız olabilir), dolayısıyla atama window.overloadyerine 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;
}();

Kullanımı:

Arayan, aşırı yüklenmiş işlevlerini, dönüşüne bir değişken atayarak tanımlar overload(). Zincirleme sayesinde, ek aşırı yükler seri olarak tanımlanabilir:

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

overload()İmza tanımlanamazsa çağrılacak "varsayılan" işlevi tanımlamak için isteğe bağlı tek bağımsız değişken . Argümanları .add():

  1. fn: function aşırı yükün tanımlanması;
  2. a_vArgumentTests: ArrayAit functiontestleri tanımlayan s çalıştırmak için arguments. Her biri functiontek bir argümanı kabul eder ve geri dönertrue ve argümanın geçerli olup olmadığına bağlı olarak ;
  3. sAlias(İsteğe bağlı): stringtakma adın aşırı yük fonksiyonuna doğrudan erişmek için tanımlanması ( fn), örneğin myOverloadedFn.noArgs()argümanların dinamik polimorfizm testlerinden kaçınarak bu fonksiyonu doğrudan çağıracaktır.

Bu uygulama aslında ikinci a_vArgumentTestsargüman olarak geleneksel fonksiyon aşırı yüklemelerinden daha fazlasına izin verir ..add() özel türleri tanımladığından, . Böylece, argümanları yalnızca türe değil, aralıklara, değerlere veya değer koleksiyonlarına da dayanarak geçebilirsiniz!

145 kod satırına bakarsanız overload(), her imzanın kendisine argumentsgeçirilen sayıyla kategorize edildiğini görürsünüz . Bu, yaptığımız test sayısını sınırlamak için yapılır. Ayrıca bir çağrı sayısını takip ediyorum. Bazı ek kodlarda, aşırı yüklenmiş fonksiyonların dizileri yeniden sıralanabilir, böylece daha yaygın olarak adlandırılan fonksiyonlar ilk önce test edilir ve yine bir miktar performans artışı ölçüsü eklenir.

Şimdi, bazı uyarılar var ... Javascript gevşek yazıldığından, vArgumentTestsbir integerolarak doğrulanabileceğinden,float , vb

JSCompress.com sürümü (1114 bayt, 744 bayt g-sıkıştırılmış):

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

2

Artık çoklu doldurma olmadan ECMAScript 2018'de aşırı yükleme yapabilir, var uzunluk / tip vb. Kontrol edebilirsiniz, sadece forma sözdizimini kullanın .

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"

Yayılım sözdizimi nedir?

ECMAScript teklifi için Dinlenme / Yayılma Özellikleri (aşama 4), nesne değişmezlerine yayılma özellikleri ekler. Sağlanan bir nesneden kendi numaralandırılabilir özelliklerini yeni bir nesneye kopyalar. Mdn hakkında daha fazlası

Not: Nesne değişmezlerindeki yayılım sözdizimi Edge ve IE'de çalışmaz ve deneysel bir özelliktir. tarayıcı uyumluluğuna bakın


2

Fonksiyon aşırı yüklenmesi için böyle bir şey yapılabilir.

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

Kaynak


1

Bu eski bir soru ama bence başka bir girişe ihtiyacı olduğunu düşünüyorum (her ne kadar kimse okuyacak şüpheliyim). Hemen Başlatılan İşlev İfadelerinin (IIFE) kullanımı, aşırı işlev yüklemesine izin vermek için kapaklar ve satır içi işlevlerle birlikte kullanılabilir. Aşağıdaki (onaylanmış) örneği düşünün:

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

Kısacası, IIFE'nin kullanımı, özel değişkeni tanımlamamıza izin veren yerel bir kapsam oluşturur old , fonksiyonun ilk tanımına bir referans depolamakfoo . Bu işlev daha sonra newFootam olarak iki bağımsız değişkenden geçirilirse her iki bağımsız değişkenin içeriğini günlüğe kaydeden ave / bveya oldişlevi çağıran bir satır içi işlev döndürür arguments.length !== 2. Bu örüntü, bir değişkeni birkaç farklı fonksiyonel tanım ile donatmak için istenilen sayıda tekrarlanabilir.


1

Aşırı yüklenmiş benzeri yaklaşımın yararlı bir örneğini paylaşmak istiyorum.

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

Kullanım: temizle (); // Tüm dokümanı temizler

Açık (myDiv); // myDiv tarafından başvurulan paneli temizler


1

JavaScript türsüz bir dildir ve sadece parametre sayısı açısından bir yöntemi / işlevi aşırı yüklemenin mantıklı olduğunu düşünüyorum. Bu nedenle, parametrenin tanımlanmış olup olmadığını kontrol etmenizi tavsiye ederim:

myFunction = function(a, b, c) {
     if (b === undefined && c === undefined ){
          // do x...
     }
     else {
          // do y...
     }
};

1
Sadece tipsizin "tip yok" anlamına gelmediğini belirtmek isterim.
hamdiakoguz

1

Temmuz 2017 itibariyle, aşağıdaki yaygın teknik olmuştur. İşlev içinde tür kontrolü de yapabileceğimizi unutmayın.

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

1

Kullanım durumunuz için, bununla nasıl başa çıkacağım ES6(2017'nin sonu olduğu için):

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

Bunu herhangi bir miktarda parametreyle çalışacak şekilde uyarlayabilir ve koşullu ifadelerinizi buna göre değiştirebilirsiniz.


1

JS'de gerçek aşırı yükleme yoktur, yine de yöntem aşırı yüklemesini çeşitli şekillerde simüle edebiliriz:

yöntem # 1: nesneyi kullan

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

yöntem # 2: dinlenme (yayma) parametrelerini kullanma

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

yöntem # 3: tanımsız kullanın

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

yöntem # 4: tür denetimi

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

1

İken Varsayılan parametreler aşırı edilmez, geliştiriciler bu alanda karşılaştıkları sorunlardan bazılarını çözebilir. Giriş kesinlikle sipariş tarafından kararlaştırılır, klasik aşırı yüklemede olduğu gibi yeniden sipariş veremezsiniz:

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

Sonuçlar (2020 yılı için):

2020
16160
12
65
2021
2023

Daha fazla bilgi: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters


0

İlk seçenek gerçekten dikkatini hak ediyor çünkü oldukça karmaşık bir kod kurulumunda geldim. Cevabım

  1. İlk etapta farklı isimler kullanmak

Küçük ama önemli bir ipucu ile, isimler bilgisayar için farklı görünmeli, ancak sizin için değil. Aşırı yüklenmiş fonksiyonları adlandırın: func, func1, func2.


Aşırı yüklemeyi deneyecektim, ancak getDeviceInfoByID ve getDeviceInfoByType gibi farklı isimler kullanmaya karar verdim ...
CramerTV
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.