JavaScript'te bir işlev çağrısında adlandırılmış parametreleri sağlamanın bir yolu var mı?


207

C # adlandırılmış parametreleri özelliği bazı durumlarda oldukça yararlı buluyorum.

calculateBMI(70, height: 175);

JavaScript'te bunu istiyorsam ne kullanabilirim?


Ne istemiyorum bu:

myFunction({ param1: 70, param2: 175 });

function myFunction(params){
  // Check if params is an object
  // Check if the parameters I need are non-null
  // Blah blah
}

Bu yaklaşımı zaten kullandım. Başka bir yol var mı?

Bunu yapmak için herhangi bir kütüphane kullanmakta fayda var.


Bunun mümkün olduğunu düşünmüyorum, ancak tanımsız olanları boş yerlere koymayı deneyebilirsiniz. Bu çok kötü. Nesneyi kullanın, iyi.
Vladislav Qulin

14
Hayır, JavaScript / EcmaScript adlandırılmış parametreleri desteklemez. Afedersiniz.
smilly92

1
Bunu zaten biliyorum. Teşekkürler. FunctionJavascript mevcut ne yapabilirim tweaking içeren bir yol arıyordu .
Robin Maben

1
FunctionJavascript'te mevcut olan Javascript'in temel sözdizimini değiştiremez
Gareth

2
Javascript'in bu özelliği desteklediğini düşünmüyorum. Ben adlandırılmış parametrelere gelebilir en yakın (1) bir yorum eklemek calculateBMI(70, /*height:*/ 175);, (2) bir nesne sağlamak calculateBMI(70, {height: 175})veya (3) bir sabit kullanmak olduğunu düşünüyorum const height = 175; calculateBMI(70, height);.
tfmontague

Yanıtlar:


212

ES2015 ve üstü

ES2015'te, adlandırılmış parametreleri simüle etmek için parametre yok etme kullanılabilir. Arayanın bir nesneyi geçirmesini gerektirir, ancak varsayılan parametreleri de kullanırsanız işlevin içindeki tüm denetimleri önleyebilirsiniz:

myFunction({ param1 : 70, param2 : 175});

function myFunction({param1, param2}={}){
  // ...function body...
}

// Or with defaults, 
function myFunc({
  name = 'Default user',
  age = 'N/A'
}={}) {
  // ...function body...
}

ES5

İstediğinize yaklaşmanın bir yolu var, ancak Function.prototype.toString [ES5] dereceye kadar uygulamaya bağlı olan , bu nedenle tarayıcılar arası uyumlu olmayabilir.

Fikir, bir nesnenin özelliklerini karşılık gelen parametreyle ilişkilendirebilmeniz için işlevin dize temsilinden parametre adlarını ayrıştırmaktır.

Bir işlev çağrısı daha sonra

func(a, b, {someArg: ..., someOtherArg: ...});

Burada ave bkonum bağımsız değişkenleridir ve son bağımsız değişken, adlandırılmış bağımsız değişkenlere sahip bir nesnedir.

Örneğin:

var parameterfy = (function() {
    var pattern = /function[^(]*\(([^)]*)\)/;

    return function(func) {
        // fails horribly for parameterless functions ;)
        var args = func.toString().match(pattern)[1].split(/,\s*/);

        return function() {
            var named_params = arguments[arguments.length - 1];
            if (typeof named_params === 'object') {
                var params = [].slice.call(arguments, 0, -1);
                if (params.length < args.length) {
                    for (var i = params.length, l = args.length; i < l; i++) {
                        params.push(named_params[args[i]]);
                    }
                    return func.apply(this, params);
                }
            }
            return func.apply(null, arguments);
        };
    };
}());

Hangi olarak kullanırsınız:

var foo = parameterfy(function(a, b, c) {
    console.log('a is ' + a, ' | b is ' + b, ' | c is ' + c);     
});

foo(1, 2, 3); // a is 1  | b is 2  | c is 3
foo(1, {b:2, c:3}); // a is 1  | b is 2  | c is 3
foo(1, {c:3}); // a is 1  | b is undefined  | c is 3
foo({a: 1, c:3}); // a is 1  | b is undefined  | c is 3 

DEMO

Bu yaklaşımın bazı dezavantajları vardır (uyarıldınız!):

  • Son bağımsız değişken bir nesne ise, "adlandırılmış bağımsız değişken nesneleri" olarak değerlendirilir
  • Her zaman işlevde tanımladığınız kadar çok argüman alırsınız, ancak bazılarının değeri olabilir undefined(hiç değere sahip olmaktan farklıdır). Bu, arguments.lengthkaç bağımsız değişkenin geçtiğini test etmek için kullanamayacağınız anlamına gelir .

Sarıcıyı oluşturan bir işleve sahip olmak yerine, bir işlevi ve bağımsız değişkenler gibi çeşitli değerleri kabul eden bir işleve sahip olabilirsiniz.

call(func, a, b, {posArg: ... });

hatta genişletebilirsiniz Function.prototype:

foo.execute(a, b, {posArg: ...});

Evet ... işte bunun için bir örnek: jsfiddle.net/9U328/1 (yine de kullanmanız Object.definePropertyve ayarlamanız enumerablegerekir false). Yerel nesneleri genişletirken daima dikkatli olunmalıdır. Bütün yaklaşım biraz acayip hissediyor, bu yüzden şimdi ve sonsuza dek çalışmasını beklemezdim;)
Felix Kling

Kayıt edilmiş. Bunu kullanmaya başlayacağım. Cevap olarak işaretleme! ... Şimdilik;)
Robin Maben

1
Çok küçük nitpick: Bu yaklaşımın EcmaScript 6 Ok İşlevlerini yakalayacağını sanmıyorum . Şu anda büyük bir endişe değil, ancak cevabınızın uyarılar bölümünde belirtmeye değer olabilir.
NobodyMan

3
@ Kimse: Doğru. Bu cevabı ok fonksiyonları bir şey olmadan yazdım. ES6'da, aslında parametre yıkımına başvururum.
Felix Kling

1
Konusunda undefinedtedavi - "hiçbir değeri" vs, JS işlevlerin varsayılan değerler olarak gösterilebilir olduğunu eklenebilir undefinedeksik değer olarak.
Dmitri Zaitsev

71

Hayır - nesne yaklaşımı JavaScript'in buna cevabıdır. Fonksiyonunuzun ayrı parametreler yerine bir nesne beklemesi şartıyla bu konuda herhangi bir sorun yoktur.


35
@RobertMaben - Sorulan belirli sorunun cevabı, belirli bir ad alanında yaşadıklarını bilmeden bildirilen değişkenleri veya işlevleri toplamanın yerel bir yolu olmadığıdır. Cevap kısa olduğu için, cevap olarak uygunluğunu reddetmez - kabul etmez misiniz? Orada "hayır, mümkün değil" çizgisinde çok daha kısa cevaplar var. Kısa olabilirler, ama aynı zamanda sorunun cevabıdır.
Mitya


1
Bu kesinlikle adil bir cevaptır - 'adlandırılmış parametreler' bir dil özelliğidir. Nesne yaklaşımı, böyle bir özelliğin yokluğunda bir sonraki en iyi şeydir.
fatuhoku

32

Bu konu bir süredir evcil hayvanım. Kemerimin altında birçok dilde deneyimli bir programcıyım. Kullanmaktan zevk aldığım en sevdiğim dillerden biri Python. Python herhangi bir hile olmadan adlandırılmış parametreleri destekler .... Python (bir süre önce) kullanmaya başladığımdan beri her şey daha kolay oldu. Her dilin adlandırılmış parametreleri desteklemesi gerektiğine inanıyorum, ancak durum böyle değil.

Birçok kişi, parametreleri adlandırmanız için yalnızca "Bir nesneyi geçir" hilesi kullanmayı söylüyor.

/**
 * My Function
 *
 * @param {Object} arg1 Named arguments
 */
function myFunc(arg1) { }

myFunc({ param1 : 70, param2 : 175});

Ve bu harika çalışıyor, ancak ..... çoğu IDE söz konusu olduğunda, birçok geliştiricimiz IDE'mizdeki tür / argüman ipuçlarına güveniyoruz. Şahsen PHP Storm kullanıyorum (PyCharm için PyCharm ve Objective C için AppCode gibi diğer JetBrains IDE'leri ile birlikte)

Ve "Bir nesneyi geçir" hilesi kullanmanın en büyük sorunu, fonksiyonu çağırdığınızda IDE'nin size tek bir tip ipucu vermesidir ve işte bu ... Hangi parametrelerin ve tiplerin arg1 nesnesi?

Arg1 içinde hangi parametrelerin gitmesi gerektiği hakkında hiçbir fikrim yok

Yani ... "Bir nesneyi geç" numarası benim için işe yaramıyor ... Aslında, işlevin hangi parametreleri beklediğini bilmeden önce her işlevin docblock'una bakmakla daha fazla baş ağrısına neden oluyor .... Elbette, mevcut kodu korurken, yeni kod yazmak korkunç bir durumdur.

Bu benim kullandığım teknik ... Şimdi, bazı sorunlar olabilir ve bazı geliştiriciler bana yanlış yaptığımı söyleyebilir ve bu şeyler söz konusu olduğunda açık bir fikrim var ... Her zaman bir görevi yerine getirmenin daha iyi yollarına bakmaya hazırım ... Yani, bu teknikle ilgili bir sorun varsa, yorumlarınızı bekliyoruz.

/**
 * My Function
 *
 * @param {string} arg1 Argument 1
 * @param {string} arg2 Argument 2
 */
function myFunc(arg1, arg2) { }

var arg1, arg2;
myFunc(arg1='Param1', arg2='Param2');

Bu şekilde, her iki dünyanın en iyisine sahibim ... IDE'm bana tüm uygun argüman ipuçlarını verdiği için yeni kod yazmak kolaydır ... Ve, daha sonra kodu korurken, sadece bir bakışta değil, işleve iletilen değer değil, aynı zamanda bağımsız değişkenin adı. Gördüğüm tek yük, argüman adlarınızı global ad alanını kirletmemek için yerel değişkenler olarak bildirmektir. Tabii, biraz fazladan yazım, ancak yeni kod yazarken veya mevcut kodu korurken docblock'ları aramak için geçen süreye kıyasla önemsiz.

Şimdi, yeni kod oluştururken tüm parametreleri ve türleri var


2
Bu teknikle ilgili tek şey, parametrelerin sırasını değiştirememeniz ... Ama ben şahsen iyiyim.
Ray Perea

13
Görünüşe göre bu, gelecekteki bir bakıcı ortaya çıktığında ve argüman sırasını değiştirebileceklerini düşündüklerinde (ancak tabii ki yapamazlar) sorun istiyor.
kimse

1
@AndrewMedico Kabul ediyorum ... Görünüşe göre sadece Python'daki gibi argüman sırasını değiştirebilirsiniz. Bununla ilgili söyleyebileceğim tek şey, argüman sırasını değiştirirken programı gerçekten hızlı bir şekilde öğrenecekler.
Ray Perea

7
Bunun myFunc(/*arg1*/ 'Param1', /*arg2*/ 'Param2');daha iyi olduğunu iddia ediyorum myFunc(arg1='Param1', arg2='Param2');, çünkü bu, okuyucuyu kandırmak için gerçek isimlendirilmiş herhangi bir argümandan daha fazla şansı yok
Eric

2
Önerilen kalıbın adlandırılmış argümanlarla ilgisi yoktur, düzen hala önemlidir, isimlerin gerçek parametrelerle senkronize kalması gerekmez, ad alanı gereksiz değişkenlerle kirlenir ve orada olmayan ilişkiler ima edilir.
Dmitri Zaitsev

25

Sadece parametreleri çağırmak yerine, parametrelerin her birinin ne olduğunu netleştirmek istiyorsanız

someFunction(70, 115);

neden aşağıdakileri yapmıyorsun

var width = 70, height = 115;
someFunction(width, height);

emin, bu ekstra bir kod satırı, ama okunabilirlik kazanır.


5
KISS ilkesini izlemek için +1 ve ayrıca hata ayıklamaya da yardımcı olur. Bununla birlikte, her varlığın hafif bir performans isabetiyle de olsa kendi hattında olması gerektiğini düşünüyorum ( http://stackoverflow.com/questions/9672635/javascript-var-statement-and-performance ).
clairestreb

21
Bu sadece fazladan kod satırı değil, aynı zamanda argümanların sırası ve isteğe bağlı hale getirilmesi ile de ilgilidir. Yani bunu adlandırılmış parametrelerle yazabilirsiniz: someFunction(height: 115);ancak yazarsanız someFunction(height);genişliği gerçekten ayarlarsınız.
Francisco Presencia

Bu durumda, CoffeeScript adlandırılmış bağımsız değişkenleri destekler. Sadece yazmana izin verecek someFunction(width = 70, height = 115);. Değişkenler, oluşturulan JavaScript kodunda geçerli kapsamın en üstünde bildirilir.
Audun Olsen

6

Başka bir yol, örneğin aşağıdaki gibi uygun bir nesnenin niteliklerini kullanmak olacaktır:

function plus(a,b) { return a+b; };

Plus = { a: function(x) { return { b: function(y) { return plus(x,y) }}}, 
         b: function(y) { return { a: function(x) { return plus(x,y) }}}};

sum = Plus.a(3).b(5);

Tabii ki bu uydurulmuş örnek için biraz anlamsız. Ancak işlevin benzediği durumlarda

do_something(some_connection_handle, some_context_parameter, some_value)

daha faydalı olabilir. Ayrıca, böyle bir nesneyi varolan bir işlevden genel bir şekilde oluşturmak için "parametrefy" fikriyle birleştirilebilir. Yani her parametre için fonksiyonun kısmi olarak değerlendirilmiş bir versiyonunu değerlendirebilen bir üye oluşturacaktır.

Bu fikir elbette Schönfinkeling aka Currying ile ilgilidir.


Bu düzgün bir fikirdir ve bunu tartışma iç hileleriyle birleştirirseniz daha da iyi olur. Ne yazık ki, isteğe bağlı argümanlar ile çalışma
Eric

2

Başka bir yol var. Bir nesneyi başvuru ile geçiriyorsanız, o nesnenin özellikleri işlevin yerel kapsamında görünür. Bunun Safari için çalıştığını biliyorum (diğer tarayıcıları kontrol etmedim) ve bu özelliğin bir adı olup olmadığını bilmiyorum, ancak aşağıdaki örnek kullanımını gösteriyor.

Her ne kadar pratikte bunun zaten kullandığınız tekniğin ötesinde herhangi bir fonksiyonel değer sunduğunu düşünmese de, anlamsal olarak biraz daha temiz. Ve yine de bir nesne başvurusu veya bir nesne değişmezi iletilmesini gerektirir.

function sum({ a:a, b:b}) {
    console.log(a+'+'+b);
    if(a==undefined) a=0;
    if(b==undefined) b=0;
    return (a+b);
}

// will work (returns 9 and 3 respectively)
console.log(sum({a:4,b:5}));
console.log(sum({a:3}));

// will not work (returns 0)
console.log(sum(4,5));
console.log(sum(4));

2

Node-6.4.0 (process.versions.v8 = '5.0.71.60') ve Node Chakracore-v7.0.0-pre8 ve ardından Chrome-52'yi (V8 = 5.2.361.49) denerken, adlandırılan parametrelerin neredeyse ancak bu düzen hala önceliğe sahiptir. ECMA standardının söylediklerini bulamıyorum.

>function f(a=1, b=2){ console.log(`a=${a} + b=${b} = ${a+b}`) }

> f()
a=1 + b=2 = 3
> f(a=5)
a=5 + b=2 = 7
> f(a=7, b=10)
a=7 + b=10 = 17

Ama sipariş gereklidir !! Standart davranış mı?

> f(b=10)
a=10 + b=2 = 12

10
Bu düşündüğünüzü yapmıyor. b=10İfadenin sonucu şudur 10ve işleve aktarılan şey budur. f(bla=10)de çalışır (değişkene 10 atar blave değeri işleve daha sonra iletir).
Matthias Kestenholz

1
Bu aynı zamanda bir yan etki olarak küresel kapsamda ave bdeğişkenler yaratmaktadır .
Gershom

2

fNesne olarak adlandırılmış parametrelerle çağrı fonksiyonu

o = {height: 1, width: 5, ...}

temelde kompozisyonunu çağırıyor ve f(...g(o))burada yayılım sözdizimini kullanıyorum veg ve nesne değerlerini parametre konumlarıyla birleştiren bir "bağlayıcı" harita.

Bağlanma haritası, anahtar dizisi ile temsil edilebilen tam olarak eksik bileşendir:

// map 'height' to the first and 'width' to the second param
binding = ['height', 'width']

// take binding and arg object and return aray of args
withNamed = (bnd, o) => bnd.map(param => o[param])

// call f with named args via binding
f(...withNamed(binding, {hight: 1, width: 5}))

Not üç ayrılmış maddeler: işlevi adlandırılmış bağımsız değişkenleri ve bağlayıcı ile bir nesne. Bu ayırma, bağlamanın fonksiyon tanımında keyfi olarak özelleştirilebildiği ve fonksiyon çağrısı zamanında keyfi olarak uzatılabildiği bu yapıyı kullanmada çok fazla esnekliğe izin verir.

Örneğin, kısaltmak isteyebilirsiniz heightve widtholarak hve whala netlik için tam isimlerle aramak isterken, senin işlevin tanımı içinde, bu daha kısa ve daha temiz hale getirmek için:

// use short params
f = (h, w) => ...

// modify f to be called with named args
ff = o => f(...withNamed(['height', 'width'], o))

// now call with real more descriptive names
ff({height: 1, width: 5})

Bu esneklik, fonksiyonların orijinal param isimleri kaybolarak keyfi olarak dönüştürülebildiği fonksiyonel programlama için de daha kullanışlıdır.


0

Python'dan gelen bu beni çok etkiledi. Hem konumsal hem de anahtar kelime nesnelerini kabul edecek düğüm için basit bir sarmalayıcı / Proxy yazdım.

https://github.com/vinces1979/node-def/blob/master/README.md


Doğru anlarsam, çözümünüz işlevin tanımındaki konumsal ve adlandırılmış parametreleri ayırt etmeyi gerektirir, bu da çağrı sırasında bu seçimi yapma özgürlüğüm olmadığı anlamına gelir.
Dmitri Zaitsev

@DmitriZaitsev: Python topluluğunda (anahtar kelime argümanlarının günlük bir özellik olduğu), kullanıcıları anahtar kelimeye göre isteğe bağlı argümanlar belirlemeye zorlamanın çok iyi bir fikir olduğunu düşünüyoruz ; bu hataların önlenmesine yardımcı olur.
Tobias

@ Tobias Kullanıcıları JS'de zorlamak çözülmüş bir sorundur: f(x, opt)nerede optbir nesne. Hatalardan kaçınmaya veya yaratmaya yardımcı olup olmadığı (yanlış yazmanın ve anahtar kelime adlarını hatırlamanın acısının neden olduğu gibi) bir soru olmaya devam etmektedir.
Dmitri Zaitsev

@DmitriZaitsev: Python'da bu kesinlikle hataları önler , çünkü (elbette) anahtar kelime argümanları orada temel bir dil özelliğidir. Yalnızca anahtar kelime bağımsız değişkenleri için, Python 3'ün özel sözdizimi vardır (Python 2'de, kwargs dikteki anahtarları teker teker açar ve son olarak birTypeError bilinmeyen anahtarlar bırakılırsa ). Sizin f(x, opt)çözüm verirf Python 2'de böyle bir şey yapmak işlevini, ancak tüm varsayılan değerler kendini kontrol etmesi gerekir.
Tobias

@Tobias Bu teklif JS ile ilgili mi? Bunun f(x, opt)zaten nasıl çalıştığını açıklıyor gibi görünüyor , ancak bunun soruyu cevaplamaya nasıl yardımcı olduğunu görmüyorum, örneğin her ikisini de yapmak istiyorsunuz request(url)ve sadece anahtar kelime parametresi request({url: url})yaparak mümkün olmaz url.
Dmitri Zaitsev

-1

Genel olarak inanılanın aksine, adlandırılmış parametreler, aşağıda gösterildiği gibi basit, temiz bir kodlama kuralıyla standart, eski okul JavaScript'inde (yalnızca boole parametreleri için) uygulanabilir.

function f(p1=true, p2=false) {
    ...
}

f(!!"p1"==false, !!"p2"==true); // call f(p1=false, p2=true)

Uyarılar:

  • Bağımsız değişkenlerin sıralaması korunmalıdır - ancak kalıp hala yararlıdır, çünkü işlev imzası için grep ya da IDE kullanmak zorunda kalmadan hangi gerçek bağımsız değişkenin hangi resmi parametrenin kastedildiğini açıklığa kavuşturur.

  • Bu sadece booleanlar için geçerlidir. Ancak, benzer bir kalıbın JavaScript'in benzersiz tür zorlama semantiği kullanılarak diğer türler için geliştirilebileceğinden eminim.


Hala isme göre değil, yere göre atıfta bulunuyorsunuz.
Dmitri Zaitsev

@DmitriZaitsev evet, yukarıda bile söyledim. Ancak, isimlendirilmiş argümanların amacı okuyucuya her argümanın ne anlama geldiğini açıklamaktır; bu bir tür dokümantasyon. Çözümüm, düzensiz görünen yorumlara başvurmadan belgelerin işlev çağrısına gömülmesini sağlar.
user234461

Bu farklı bir sorunu çözer. Soru, heightparam düzenine bakılmaksızın isimle geçmekle ilgiliydi .
Dmitri Zaitsev

@DmitriZaitsev aslında, soru parametre sırası veya OP'nin neden params kullanmak istediğini söylemedi.
user234461

Herhangi bir sırayla params adının geçilmesinin anahtarı olduğu C # 'a atıfta bulunur.
Dmitri Zaitsev
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.