Birden çok argüman ve seçenek nesnesi


157

Birden fazla argümana sahip bir JavaScript işlevi oluştururken, her zaman bu seçenekle karşı karşıya kalırım: bir argüman listesi geçirin ve bir seçenekler nesnesini geçirin.

Örneğin bir diziye bir nodeList eşlemek için bir işlev yazıyorum:

function map(nodeList, callback, thisObject, fromIndex, toIndex){
    ...
}

Bunun yerine kullanabilirsiniz:

function map(options){
    ...
}

burada seçenekler bir nesnedir:

options={
    nodeList:...,
    callback:...,
    thisObject:...,
    fromIndex:...,
    toIndex:...
}

Hangisi önerilen yol? Birinin diğerine ne zaman kullanılacağına dair yönergeler var mı?

[Güncelleme] Seçenekler nesnesi lehine bir fikir birliği var gibi görünüyor, bu yüzden bir yorum eklemek istiyorum: benim durumumda argümanlar listesini kullanmak için cazip olmasının bir nedeni JavaScript ile tutarlı bir davranış vardı array.map yönteminde oluşturulmuştur.


2
İkinci seçenek, benim görüşüme göre güzel bir şey olan adlandırılmış argümanlar verir.
Werner Kvalem Vesterås

İsteğe bağlı mı yoksa gerekli argümanlar mı?
Tembel Nefret Ediyorum

@ user1689607 benim örneğimde son üç isteğe bağlıdır.
Christophe

Son iki argümanınız çok benzer olduğundan, kullanıcı yalnızca birini veya diğerini geçerse hangisinin amaçlandığını asla bilemezsiniz. Bu nedenle, neredeyse adlandırılmış argümanlara ihtiyacınız olacak. Ancak, yerel API'ye benzer bir API korumak istediğinizi takdir edebilirim.
Tembel Nefret Ediyorum

1
İşleviniz benzer bir şey yaparsa, yerel API'den sonra modelleme kötü bir şey değildir. Her şey "kodu en readablae yapan nedir?" Array.prototype.mapherhangi bir yarı deneyimli kodlayıcıyı şaşırtmayacak basit bir API'ye sahiptir.
Jeremy J Starcher

Yanıtlar:


160

Diğerlerinin çoğu gibi, genellikle options objectuzun bir parametre listesini geçmek yerine bir işleve geçmeyi tercih ederim , ancak gerçekten tam bağlama bağlıdır.

Turnusol testi olarak kod okunabilirliğini kullanıyorum.

Örneğin, bu işlev varsa, çağrı:

checkStringLength(inputStr, 10);

Ben kodu olduğu gibi oldukça okunabilir olduğunu düşünüyorum ve bireysel parametreleri geçirme gayet iyi.

Öte yandan, bunun gibi çağrıları olan işlevler vardır:

initiateTransferProtocol("http", false, 150, 90, null, true, 18);

Biraz araştırma yapmadığınız sürece tamamen okunamaz. Öte yandan, bu kod iyi okunur:

initiateTransferProtocol({
  "protocol": "http",
  "sync":      false,
  "delayBetweenRetries": 150,
  "randomVarianceBetweenRetries": 90,
  "retryCallback": null,
  "log": true,
  "maxRetries": 18
 });

Bu bir bilimden çok bir sanattır, ancak temel kuralları adlandırmam gerekirse:

Aşağıdaki durumlarda bir seçenekler parametresi kullanın:

  • Dörtten fazla parametreniz var
  • Parametrelerden herhangi biri isteğe bağlıdır
  • Hangi parametreleri aldığını anlamak için işlevi aramak zorunda kaldınız
  • Eğer biri "ARRRRRG!" Diye bağırırken sizi boğmaya çalışırsa

10
Mükemmel cevap. Değişir. Boole tuzaklarına dikkat edin ariya.ofilabs.com/2011/08/hall-of-api-shame-boolean-trap.html
Trevor Dixon

2
Ah evet ... Bu bağlantıyı unutmuştum. Gerçekten, API'lerin nasıl çalıştığını yeniden düşünmemi sağladı ve hatta aptalca şeyler yaptığımı öğrendikten sonra birkaç kod parçasını yeniden yazdım. Teşekkürler!
Jeremy J Starcher

1
'Hangi parametrelere ihtiyaç duyduğunu anlamak için işleve bakmak zorunda kaldınız' - Bunun yerine bir seçenekler nesnesi kullanarak, her zaman tuşların gerekli olduğunu ve ne adlandırıldığını anlamaya yönelik yöntemi aramak zorunda kalmayacaksınız . IDE'deki Intellisense bu bilgileri yansıtmazken, parametler de ortaya çıkıyor. Çoğu IDE'de, fareyi yöntemin üzerine getirebilirsiniz ve size parametrelerin ne olduğunu gösterecektir.
simbolo

1
Hepiniz 'iyi uygulamaları kodlama' bu bit performans sonuçları ilgilenen varsa, burada her iki yönde test bir jsPerf var: jsperf.com/function-boolean-arguments-vs-options-object/10 Not Ekle küçük büküm üçüncü alternatif, burada 'önceden ayarlanmış' (sabit) seçenekler nesnesini kullanıyorum, bu da geliştirme zamanında (kısaca bilinen) aynı ayarlarla birçok çağrınız (çalışma sürenizin ömrü boyunca, örneğin web sayfanız) olduğunda yapılabilir : seçenek değerleriniz kaynak kodunuzda sabit olarak kodlandığında).
Ger Hobbelt

2
@Dürüst, artık bu tarz bir kodlama kullanmıyorum. TypeScript'e geçtim ve adlandırılmış parametreleri kullandım.
Jeremy J Starcher

28

Çoklu argümanlar çoğunlukla zorunlu parametreler içindir. Onlarla ilgili yanlış bir şey yok.

İsteğe bağlı parametreleriniz varsa, karmaşık hale gelir. Bunlardan biri diğerlerine güveniyorsa, belirli bir düzenleri varsa (örneğin dördüncüsünün üçüncü olana ihtiyacı vardır), yine de birden fazla argüman kullanmalısınız. Neredeyse tüm yerel EcmaScript ve DOM yöntemleri bu şekilde çalışır. Bunun iyi bir örneği, son 3 argümanın isteğe bağlı olduğu openXMLHTTP isteklerinin yöntemidir - kural "kullanıcı olmadan şifre yok" gibidir (ayrıca bkz. MDN dokümanları . ).

Seçenek nesneleri iki durumda kullanışlıdır:

  • Kafanız karışacak çok fazla parametreniz var: "Adlandırma" size yardımcı olacak, bunların sırası hakkında endişelenmenize gerek yok (özellikle değişebilirlerse)
  • İsteğe bağlı parametreleriniz var. Nesneler çok esnektir ve herhangi bir sipariş vermeden sadece ihtiyacınız olan şeyleri ve başka şeyleri (veya undefinedsleri) geçirirsiniz .

Senin durumunda, ben tavsiye ederim map(nodeList, callback, options). nodelistve callbackgerekliyse, diğer üç argüman sadece ara sıra gelir ve makul temerrütlere sahiptir.

Başka bir örnek JSON.stringify . spaceParametreyi bir replacerişlevi geçmeden kullanmak isteyebilirsiniz - o zaman aramalısınız …, null, 4). Bir arguments nesnesi daha iyi olabilirdi, ancak yalnızca 2 parametre için gerçekten makul değil.


@ Trevor-dixon ile aynı soruyu + 1'leyin: Bu karışımın uygulamada, örneğin js kütüphanelerinde kullanıldığını gördünüz mü?
Christophe

Örnek olarak jQuery'nin ajax yöntemleri verilebilir . [Zorunlu] URL'yi ilk argüman olarak ve büyük seçenekler argümanını ikinci argüman olarak kabul ediyorlar.
Bergi

çok garip! Bunu daha önce hiç fark etmedim. Her zaman url ile bir seçenek özellik olarak kullanıldığını gördüm ...
Christophe

Evet, jQuery, geriye dönük olarak uyumlu kalırken isteğe bağlı parametreleriyle garip bir şey yapıyor :-)
Bergi

1
Bence buradaki tek aklı başında cevap bu.
Benjamin Gruenbaum

11

'Seçenekler bir nesne olarak' yaklaşımını kullanmak en iyisi olacaktır. Özelliklerin sırası hakkında endişelenmenize gerek yoktur ve verilerin aktarılmasında daha fazla esneklik vardır (örneğin isteğe bağlı parametreler)

Nesne oluşturmak, seçeneklerin birden fazla işlevde kolayca kullanılabileceği anlamına gelir:

options={
    nodeList:...,
    callback:...,
    thisObject:...,
    fromIndex:...,
    toIndex:...
}

function1(options){
    alert(options.nodeList);
}

function2(options){
    alert(options.fromIndex);
}

Buradaki (makul) varsayım, nesnenin her zaman aynı anahtar çiftlerine sahip olacağıdır. Eğer bir bok / tutarsız API ile çalışıyorsanız, ellerinizde farklı bir sorun var.
backdesk

9

Bir şey başlatıyorsanız veya bir nesnenin yöntemini çağırıyorsanız, bir seçenekler nesnesi kullanmak istediğinizi düşünüyorum. Yalnızca bir veya iki parametre üzerinde çalışan ve bir değer döndüren bir işlevse, bağımsız değişken listesi tercih edilir.

Bazı durumlarda, her ikisini de kullanmak iyidir. İşlevinizde bir veya iki gerekli parametre ve bir grup isteğe bağlı parametre varsa, gereken ilk iki parametreyi ve üçüncüsünü isteğe bağlı bir seçenek karma yapın.

Örneğinizde yapardım map(nodeList, callback, options). Nodelist ve geri arama gereklidir, sadece bir çağrıyı okuyarak neler olduğunu anlatmak oldukça kolaydır ve mevcut harita işlevleri gibi. Diğer seçenekler isteğe bağlı üçüncü parametre olarak geçirilebilir.


+1 ilginç. Uygulamada, örneğin js kütüphanelerinde kullanıldığını gördünüz mü?
Christophe

7

Soru hakkındaki yorumunuz:

benim örneğimde son üç isteğe bağlıdır.

Peki bunu neden yapmıyorsunuz? (Not: Bu oldukça ham Javascript. Normalde bir defaultkarma kullanır ve Object.extend veya JQuery.extend veya benzeri kullanarak geçirilen seçeneklerle güncellerim .)

function map(nodeList, callback, options) {
   options = options || {};
   var thisObject = options.thisObject || {};
   var fromIndex = options.fromIndex || 0;
   var toIndex = options.toIndex || 0;
}

Yani, şimdi neyin isteğe bağlı ve neyin olmadığını çok daha açık olduğu için, bunların hepsi fonksiyonun geçerli kullanımlarıdır:

map(nodeList, callback);
map(nodeList, callback, {});
map(nodeList, callback, null);
map(nodeList, callback, {
   thisObject: {some: 'object'},
});
map(nodeList, callback, {
   toIndex: 100,
});
map(nodeList, callback, {
   thisObject: {some: 'object'},
   fromIndex: 0,
   toIndex: 100,
});

Bu @ trevor-dixon'un cevabına benzer.
Christophe

5

Bu yanıta biraz geç kalmış olabilirim, ama diğer geliştiricilerin bu konu hakkındaki görüşlerini arıyordum ve bu konuya rastladım.

Yanıt verenlerin çoğuna katılmıyorum ve 'çoklu argümanlar' yaklaşımının yanındayım. Benim ana argümanım "param nesnesini mutasyona uğratmak ve geri döndürmek" veya "aynı param nesnesini diğer işlevlere aktarmak" gibi diğer anti-kalıpları caydırmaktır. Bu anti-desen kötüye olan kod tabanlarında çalıştım ve bu hızlı bir şekilde imkansız hale getirir kod hata ayıklama. Javascript güçlü bir şekilde yazılmadığından ve bu tür keyfi olarak yapılandırılmış nesnelere izin verdiğinden, bu çok Javascript'e özel bir başparmak kuralı olduğunu düşünüyorum.

Kişisel fikrim, geliştiricilerin işlevleri çağırırken açık olması, gereksiz verilerin etrafından geçmemesi ve referansla değiştirilmesinden kaçınmaları gerektiğidir. Bu kalıpların kısa ve doğru kod yazmayı engellememesi değil. Sadece projenizin kötü geliştirme uygulamalarına girmesini çok daha kolay hale getirdiğini hissediyorum.

Aşağıdaki korkunç kodu düşünün:

function main() {
    const x = foo({
        param1: "something",
        param2: "something else",
        param3: "more variables"
    });

    return x;
}

function foo(params) {
    params.param1 = "Something new";
    bar(params);
    return params;
}


function bar(params) {
    params.param2 = "Something else entirely";
    const y = baz(params);
    return params.param2;
}

function baz(params) {
    params.params3 = "Changed my mind";
    return params;
}

Bu tür bir amaç sadece belirtmek için daha açık belgeler gerektirmekle kalmaz, aynı zamanda belirsiz hatalara da yer açar. Bir geliştirici param1,bar() ? Bunu yakalamak için yeterli büyüklükte bir kod tabanına bakmanın ne kadar süreceğini düşünüyorsunuz? Kuşkusuz, bu örnek biraz tuhaftır, çünkü geliştiricilerin bu noktaya kadar birkaç anti-desen uyguladıklarını varsayar. Ancak, parametreler içeren nesnelerin iletilmesinin, hata ve belirsizliğe nasıl daha fazla alan sağladığını, daha fazla vicdanlılık ve sabit doğruluk gözlemi gerektirdiğini gösterir.

Bu konuda sadece iki sentim!


3

Değişir.

Bu popüler kütüphane tasarımına ilişkin gözlemime dayanarak, seçenek nesnesini kullanmamız gereken senaryolar şunlardır:

  • Parametre listesi uzundur (> 4).
  • Bazı veya tüm parametreler isteğe bağlıdır ve belirli bir sıraya bağlı değildir.
  • Parametre listesi gelecekteki API güncellemesinde büyüyebilir.
  • API diğer kodlardan çağrılır ve API adı parametrelerin anlamını anlatacak kadar açık değildir. Bu nedenle, okunabilirlik için güçlü parametre adına ihtiyaç duyulabilir.

Ve parametre listesini kullanmak için senaryolar:

  • Parametre listesi kısadır (<= 4).
  • Parametrelerin çoğu veya tümü gereklidir.
  • İsteğe bağlı parametreler belirli bir sıradadır. (örneğin: $ .get)
  • API adına göre parametrelerin anlamını söylemek kolaydır.

2

Nesne daha çok tercih edilir, çünkü eğer bir nesneyi iletirseniz, o nesnelerdeki özellik sayısını genişletmek kolaydır ve argümanlarınızın geçtiği sırayı izlemek zorunda kalmazsınız.


1

Genellikle önceden tanımlanmış bazı argümanlar kullanan bir işlev için seçenek nesnesini daha iyi kullanırsınız. Bunun tersi örnek, setCSS ({height: 100}, {width: 200}, {background: "# 000"}) gibi sonsuz sayıda argüman alan bir işlev gibi bir şey olacaktır.


0

Büyük javascript projelerine bakardım.

Google haritası gibi şeyler sık ​​sık örneklenen nesnelerin bir nesne gerektirdiğini, ancak işlevlerin parametre gerektirdiğini göreceksiniz. Bunun OPTION argumemnts ile ilgisi olduğunu düşünürüm.

Varsayılan bağımsız değişkenlere veya isteğe bağlı bağımsız değişkenlere ihtiyacınız varsa, nesne daha esnek olduğu için muhtemelen daha iyi olur. Ama eğer normal değilseniz, işlevsel argümanlar daha açıktır.

Javascript'in de bir argumentsnesnesi var. https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Functions_and_function_scope/arguments

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.