Saya dapat membuat fungsi rekursif dalam variabel seperti:
/* Count down to 0 recursively.
*/
var functionHolder = function (counter) {
output(counter);
if (counter > 0) {
functionHolder(counter-1);
}
}
Dengan ini, functionHolder(3);
akan menghasilkan 3
2
1
0
. Katakanlah saya melakukan yang berikut:
var copyFunction = functionHolder;
copyFunction(3);
akan menghasilkan 3
2
1
0
seperti di atas. Jika saya kemudian berubah functionHolder
sebagai berikut:
functionHolder = function(whatever) {
output("Stop counting!");
Kemudian functionHolder(3);
akan memberi Stop counting!
, seperti yang diharapkan.
copyFunction(3);
sekarang memberikan 3
Stop counting!
apa yang dirujuknya functionHolder
, bukan fungsi (yang ditunjukkannya sendiri). Ini mungkin diinginkan dalam beberapa keadaan, tetapi adakah cara untuk menulis fungsi sehingga ia memanggil dirinya sendiri daripada variabel yang menampungnya?
Artinya, apakah mungkin untuk mengubah hanya garis functionHolder(counter-1);
sehingga melalui semua langkah ini masih memberikan 3
2
1
0
saat kita menelepon copyFunction(3);
? Saya mencoba this(counter-1);
tetapi itu memberi saya kesalahan this is not a function
.
sumber
Jawaban:
Menggunakan Ekspresi Fungsi Bernama:
Anda dapat memberi ekspresi fungsi nama yang sebenarnya pribadi dan hanya terlihat dari dalam fungsi itu sendiri:
var factorial = function myself (n) { if (n <= 1) { return 1; } return n * myself(n-1); } typeof myself === 'undefined'
Berikut
myself
adalah hanya terlihat dalam fungsi itu sendiri.Anda dapat menggunakan nama pribadi ini untuk memanggil fungsi secara rekursif.
Lihat
13. Function Definition
spesifikasi ECMAScript 5:Harap dicatat bahwa Internet Explorer hingga versi 8 tidak berperilaku dengan benar karena nama sebenarnya terlihat di lingkungan variabel yang melingkupinya, dan ini merujuk pada duplikat dari fungsi sebenarnya (lihat komentar patrick dw di bawah).
Menggunakan arguments.callee:
Atau Anda dapat menggunakan
arguments.callee
untuk merujuk ke fungsi saat ini:var factorial = function (n) { if (n <= 1) { return 1; } return n * arguments.callee(n-1); }
Edisi ke-5 ECMAScript melarang penggunaan arguments.callee () dalam mode ketat , namun:
sumber
myself
sebenarnya terlihat di lingkungan variabel yang melingkupi, dan ini mereferensikan duplikat darimyself
fungsi sebenarnya . Anda harus bisa mengatur referensi luarnyanull
.return n * myself(n-1);
?Anda dapat mengakses fungsi itu sendiri menggunakan
arguments.callee
[MDN] :if (counter>0) { arguments.callee(counter-1); }
Namun, ini akan rusak dalam mode ketat.
sumber
TypeError
, tetapi saya belum menemukan apa pun yang secara resmi menyatakan bahwaarguments.callee
(atau pelanggaran mode ketat) tidak berlaku lagi di luar "mode ketat".Anda dapat menggunakan Y-combinator: ( Wikipedia )
// ES5 syntax var Y = function Y(a) { return (function (a) { return a(a); })(function (b) { return a(function (a) { return b(b)(a); }); }); }; // ES6 syntax const Y = a=>(a=>a(a))(b=>a(a=>b(b)(a))); // If the function accepts more than one parameter: const Y = a=>(a=>a(a))(b=>a((...a)=>b(b)(...a)));
Dan Anda bisa menggunakannya seperti ini:
// ES5 var fn = Y(function(fn) { return function(counter) { console.log(counter); if (counter > 0) { fn(counter - 1); } } }); // ES6 const fn = Y(fn => counter => { console.log(counter); if (counter > 0) { fn(counter - 1); } });
sumber
Saya tahu ini adalah pertanyaan lama, tetapi saya pikir saya akan menyajikan satu solusi lagi yang dapat digunakan jika Anda ingin menghindari penggunaan ekspresi fungsi bernama. (Tidak mengatakan Anda harus atau tidak harus menghindarinya, hanya memberikan solusi lain)
var fn = (function() { var innerFn = function(counter) { console.log(counter); if(counter > 0) { innerFn(counter-1); } }; return innerFn; })(); console.log("running fn"); fn(3); var copyFn = fn; console.log("running copyFn"); copyFn(3); fn = function() { console.log("done"); }; console.log("fn after reassignment"); fn(3); console.log("copyFn after reassignment of fn"); copyFn(3);
sumber
Inilah satu contoh yang sangat sederhana:
var counter = 0; function getSlug(tokens) { var slug = ''; if (!!tokens.length) { slug = tokens.shift(); slug = slug.toLowerCase(); slug += getSlug(tokens); counter += 1; console.log('THE SLUG ELEMENT IS: %s, counter is: %s', slug, counter); } return slug; } var mySlug = getSlug(['This', 'Is', 'My', 'Slug']); console.log('THE SLUG IS: %s', mySlug);
Perhatikan bahwa
counter
menghitung "mundur" sehubungan dengan apaslug
nilainya. Ini karena posisi di mana kita mencatat nilai-nilai ini, karena fungsinya berulang sebelum masuk - jadi, pada dasarnya kita terus melakukan penumpukan lebih dalam dan lebih dalam ke tumpukan panggilan sebelum logging dilakukan.Setelah rekursi memenuhi item stack panggilan terakhir, ia akan melakukan trampolin "keluar" dari pemanggilan fungsi, sedangkan, kenaikan pertama
counter
terjadi di dalam panggilan bertingkat terakhir.Saya tahu ini bukan "perbaikan" pada kode Penanya, tetapi dengan judul yang saya pikir secara umum saya akan mencontohkan Rekursi untuk pemahaman yang lebih baik tentang rekursi, langsung.
sumber