Versi awal JavaScript tidak mengizinkan ekspresi fungsi bernama, dan karena itu kami tidak dapat membuat ekspresi fungsi rekursif:
// 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;
});
Untuk menyiasatinya, arguments.callee
ditambahkan agar kami dapat melakukan:
[1,2,3,4,5].map(function(n) {
return (!(n>1))? 1 : arguments.callee(n-1)*n;
});
Namun ini sebenarnya solusi yang sangat buruk karena hal ini (dalam hubungannya dengan argumen lain, masalah callee, dan penelepon) membuat inlining dan rekursi ekor menjadi tidak mungkin dalam kasus umum (Anda dapat mencapainya dalam kasus tertentu melalui penelusuran dll, tetapi bahkan kode terbaik tidak optimal karena cek yang tidak diperlukan). Masalah utama lainnya adalah bahwa panggilan rekursif akan mendapatkan this
nilai yang berbeda , misalnya:
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();
Bagaimanapun, EcmaScript 3 menyelesaikan masalah ini dengan mengizinkan ekspresi fungsi bernama, misalnya:
[1,2,3,4,5].map(function factorial(n) {
return (!(n>1))? 1 : factorial(n-1)*n;
});
Ini memiliki banyak manfaat:
Fungsi ini dapat dipanggil seperti yang lainnya dari dalam kode Anda.
Itu tidak mencemari namespace.
Nilai this
tidak berubah.
Ini lebih performant (mengakses objek argumen mahal).
Aduh,
Baru menyadari bahwa selain segala sesuatu yang lain pertanyaannya adalah tentang arguments.callee.caller
, atau lebih khusus Function.caller
.
Kapan saja Anda dapat menemukan penelepon terdalam dari fungsi apa pun di stack, dan seperti yang saya katakan di atas, melihat stack panggilan memiliki satu efek utama: Membuat sejumlah besar optimasi tidak mungkin, atau jauh lebih sulit.
Misalnya. jika kami tidak dapat menjamin bahwa suatu fungsi f
tidak akan memanggil fungsi yang tidak dikenal, maka itu tidak mungkin untuk sebaris f
. Pada dasarnya itu berarti bahwa setiap situs panggilan yang mungkin tidak dapat ditelusuri secara sepele mengakumulasi sejumlah besar penjaga, ambil:
function f(a, b, c, d, e) { return a ? b * c : d * e; }
Jika juru bahasa js tidak dapat menjamin bahwa semua argumen yang disediakan adalah angka pada saat panggilan dilakukan, maka perlu memasukkan cek untuk semua argumen sebelum kode yang diuraikan, atau tidak dapat menguraikan fungsi.
Sekarang dalam kasus khusus ini penerjemah pintar harus dapat mengatur ulang cek agar lebih optimal dan tidak memeriksa nilai apa pun yang tidak akan digunakan. Namun dalam banyak kasus itu tidak mungkin dan karena itu menjadi tidak mungkin untuk sebaris.