Neden arguments.callee.caller özelliği JavaScript'te kullanımdan kaldırıldı?


214

arguments.callee.callerMülk neden JavaScript'te kullanımdan kaldırıldı?

Eklendi ve ardından JavaScript'te kullanımdan kaldırıldı, ancak ECMAScript tarafından tamamen çıkarıldı. Bazı tarayıcılar (Mozilla, IE) bunu her zaman desteklemiştir ve haritada desteği kaldırmak için herhangi bir planınız yoktur. Diğerleri (Safari, Opera) bunun için destek kabul ettiler, ancak eski tarayıcılarda destek güvenilir değil.

Bu değerli işlevselliği limboya koymak için iyi bir neden var mı?

(Veya dönüşümlü olarak, çağrı işlevini ele almanın daha iyi bir yolu var mı?)


2
Yaygın kullanım sağlayan bir özellik diğer tarayıcılar için bir uyumluluk hatası haline geleceğinden diğer tarayıcılar tarafından desteklenir. Bir site yalnızca bir tarayıcıda bulunan bir özellik kullanıyorsa, site diğer tüm sitelerde bozulur ve genellikle kullanıcılar bunun bozuk tarayıcı olduğunu düşünür.
olliej

4
(Hemen hemen tüm tarayıcılar bunu bir seferde yapmışlardır, örneğin, bu özellik (ve JS'nin kendisi) Netscape, IE'den kaynaklanan XHR, Safari'de Canvas, vb. zamanla (js, tuval, xhr hepsi örnektir), bazıları (.callee) değildir.
olliej

@olliej Standart olduğu için (veya standartta kullanımdan kaldırılmasına rağmen) kullanıldığı için desteklenmesi hakkındaki yorumunuz çok doğru! Bu yüzden bana yardım etmediklerini hissettiğimde standartları çoğunlukla görmezden gelmeye başladım. Geliştiriciler olarak, spesifikasyonun yapmamız gerektiğini söylediklerini değil, neyin işe yaradığını kullanarak standartların yönünü şekillendirebiliriz. Sadece bu var nasıl <b>ve <i>(evet, o bir noktada kaldırılmış edildi) geri.
Stijn de Witt

Yanıtlar:


252

JavaScript'in önceki sürümleri adlandırılmış işlev ifadelerine izin vermedi ve bu nedenle yinelemeli işlev ifadesi yapamadık:

 // This snippet will work:
 function factorial(n) {
     return (!(n>1))? 1 : factorial(n-1)*n;
 }
 [1,2,3,4,5].map(factorial);


 // But this snippet will not:
 [1,2,3,4,5].map(function(n) {
     return (!(n>1))? 1 : /* what goes here? */ (n-1)*n;
 });

Bunu aşmak için ekleyebildik arguments.callee:

 [1,2,3,4,5].map(function(n) {
     return (!(n>1))? 1 : arguments.callee(n-1)*n;
 });

Bununla birlikte, bu gerçekten kötü bir çözümdü (bu, diğer argümanlar, callee ve arayan sorunları ile birlikte) genel durumda satır içi ve kuyruk yinelemeyi imkansız hale getiriyor (izleme vb. aksi takdirde gerekli olmayan kontroller nedeniyle alt optimaldir). Diğer önemli sorun, özyinelemeli çağrının farklı bir thisdeğer almasıdır, örneğin:

var global = this;
var sillyFunction = function (recursed) {
    if (!recursed)
        return arguments.callee(true);
    if (this !== global)
        alert("This is: " + this);
    else
        alert("This is the global");
}
sillyFunction();

Her neyse, EcmaScript 3, adlandırılmış işlev ifadelerine izin vererek bu sorunları çözdü, örneğin:

 [1,2,3,4,5].map(function factorial(n) {
     return (!(n>1))? 1 : factorial(n-1)*n;
 });

Bunun birçok faydası vardır:

  • Bu fonksiyon, kodunuzun içinden herhangi biri gibi çağrılabilir.

  • Ad alanını kirletmez.

  • Değeri thisdeğişmez.

  • Daha performanslıdır ( arguments nesnesine erişmek pahalıdır).

, Whoops

Sorunun diğer her şeye ek olarak arguments.callee.callerveya daha spesifik olarak olduğunu fark ettim Function.caller.

Herhangi bir zamanda, yığındaki herhangi bir işlevin en derin arayanını bulabilirsiniz ve yukarıda söylediğim gibi, çağrı yığınına bakmanın tek bir büyük etkisi vardır: Çok sayıda optimizasyonu imkansız veya çok daha zor hale getirir.

Örneğin. bir işlevin fbilinmeyen bir işlevi çağırmayacağını garanti edemezsek, satır içi yapmak mümkün değildir f. Temel olarak, önemsiz derecede inline edilmiş olabilecek herhangi bir çağrı sitesinin çok sayıda koruma biriktirdiği anlamına gelir:

 function f(a, b, c, d, e) { return a ? b * c : d * e; }

Js yorumlayıcısı, sağlanan tüm bağımsız değişkenlerin çağrının yapıldığı noktadaki sayılar olduğunu garanti edemezse, satır içi koddan önce tüm bağımsız değişkenler için denetimler eklemesi gerekir veya işlevi satır içinde yapamaz.

Şimdi bu özel durumda akıllı bir tercüman kontrolleri daha uygun olacak şekilde yeniden düzenleyebilmeli ve kullanılmayacak herhangi bir değeri kontrol etmemelidir. Bununla birlikte, birçok durumda bu mümkün değildir ve bu nedenle satır içi yapmak imkansız hale gelir.


12
Optimize edilmesi zor olduğu için bunun karmaşık olduğunu mu söylüyorsunuz? Bu biraz aptalca.
Thomas Eding

11
Hayır, i optimize etmeyi zorlaştırmasına ek olarak bir dizi nedeni de listeledim (genel tarihte optimize etmek zor olan şeylerin de insanların zorluk çektiği semantiklere sahip olduğunu göstermesine rağmen)
olliej

17
Bu argüman değeri Önemi var çağrısıyla ayarlanabilir, biraz sahte olduğunu. Genellikle kullanılmaz (en azından, özyinelemeli işlevlerde onunla hiç bir sorunum olmadı). Fonksiyonu isimle çağırmak aynı sorunlara sahiptir, thisbu yüzden callee'nin iyi mi kötü mü olduğu ile ilgili olmadığını düşünüyorum . Ayrıca, callee ve arayan sadece katı modda "kullanımdan kaldırılmıştır" (ECMAscript ed 5, Aralık 2009), ancak sanırım olliej yayınlandığında 2008'de bilinmiyordu.
RobG

8
) Hala mantığı göremiyorum. Birinci sınıf işlevlere sahip herhangi bir dilde, i bilmeden kendisine başvurabilen bir işlev gövdesi tanımlayabilmenin net bir değeri vardır
Mark Reed

8
RobG bu işaret, ama olduğunu sanmıyorum tüm açık olun: Yalnızca değerini koruyacaktır adlandırılmış fonksiyonu kullanılarak recursing thiseğer thisküresel kapsamı. Diğer tüm durumlarda, değeri this olacaktır I korunması ima Cevabınız bölümlerini düşünmek, böylece ilk özyinelemeli çağrıdan sonra değiştirmek thisgerçekten geçerli değildir.
JLRishe

89

arguments.callee.callerolduğu değil o make kullanımını yapar da, kullanımdan kaldırılmış özellik. ( sadece mevcut fonksiyona bir referans verecektir)Function.callerarguments.callee

  • Function.caller, ECMA3'e göre standart olmayan da olsa, mevcut tüm büyük tarayıcılarda uygulanır .
  • arguments.caller olduğu lehine önerilmemektedir ve bazı güncel önemli tarayıcılarda (örneğin Firefox 3) 'de uygulanmadı.Function.caller

Bu nedenle durum idealden daha azdır, ancak Javascript'teki arama işlevine tüm büyük tarayıcılarda erişmek istiyorsanız, doğrudan adlandırılmış işlev başvurusundan veya özellik aracılığıyla anonim bir işlevden erişilen özelliği kullanabilirsiniz.Function.callerarguments.callee


5
Bu, kullanımdan kaldırılan ve olmayanların en iyi açıklamasıdır, çok yararlıdır. Function.caller'ın yapamayacağına dair iyi bir örnek için (özyinelemeli işlevlerin yığın izlemesini alın), bkz. Developer.mozilla.org/en/JavaScript/Reference/Global_Objects/…
Juan Mendes

1
Yine de, arguments.calleekatı modda yasaktır. Beni de üzdü, ama artık kullanmamak daha iyi.
Gras Double

1
MDN'ye ihtiyacınız olan arguments.callee köprüsü, katı modda kaldırıldığını söylüyor. Kullanımdan kaldırılanla aynı değil mi?
styfle

1
arguments.callee.callerES5 katı modunda kullanımdan kaldırıldığını unutmayın : "Kullanımdan kaldırılan bir başka özellik arguments.callee.caller veya daha spesifik olarak Function.caller idi." ( Kaynak )
thdoan

29

Adlandırılmış işlevleri kullanmak arguments.callee'den daha iyidir:

 function foo () {
     ... foo() ...
 }

daha iyi

 function () {
     ... arguments.callee() ...
 }

Adlandırılmış işlev, caller özelliği aracılığıyla arayanına erişebilir :

 function foo () {
     alert(foo.caller);
 }

hangisinden daha iyi

 function foo () {
     alert(arguments.callee.caller);
 }

Kullanımdan kaldırma, mevcut ECMAScript tasarım ilkelerinden kaynaklanmaktadır .


2
Adlandırılmış işlevi kullanmanın neden daha iyi olduğunu açıklayabilir misiniz? Anonim bir işlevde callee kullanmaya hiç gerek yok mu?
AnthonyWJones

27
Anonim bir işlevde callee kullanıyorsanız, anonim olmaması gereken bir işleve sahipsiniz.
Prestaul

3
bazen hata ayıklamanın en kolay yolu .caller () yöntemidir. Bu gibi durumlarda adlandırılmış işlevler yardımcı olmaz - aramayı hangi işlevin yaptığını bulmaya çalışıyorsunuz.
SamGoody

6
Daha iyi tanımlayın. Örneğin arguments.callee çalışırken IE6-8 işlev tuhaflıklarını adlandırdı .
cmc

1
IE6-8 garipliklerinin yanı sıra, kodu sıkıca bağlar. Nesnelerin ve / veya işlevlerin adları sabit kodlanmışsa, ardsasd ve rsk82'nin belirttiği gibi, yalnızca kod tabanı boyutu arttıkça artan büyük yeniden düzenleme tehlikeleri vardır. Birim testleri bir savunma hattıdır ve bunları kullanıyorum, ancak yine de bu zor kodlama konusunda beni kişisel olarak tatmin eden bir cevap değiller.
Jasmine Hegman

0

Sadece bir uzantı. "This" değeri özyineleme sırasında değişir. Aşağıdaki (değiştirilmiş) örnekte, faktöryel {foo: true} nesnesini alır.

[1,2,3,4,5].map(function factorial(n) {
  console.log(this);
  return (!(n>1))? 1 : factorial(n-1)*n;
},     {foo:true}     );

ilk kez çağrılan faktöriyel nesne alır, ancak bu özyinelemeli çağrılar için doğru değildir.


1
Çünkü yanlış yapıyorsun. Bakılması thisgerekiyorsa, yazma factorial.call(this, n-1)aslında özyinelemeli kod yazarken buldum, genellikle yok thisveya thisbir ağaçtaki bazı düğümü ifade eder ve aslında değişmesi iyidir.
Stijn de Witt
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.