Apa jenis kata kunci 'kembali' itu?

95

Kami menggunakan pernyataan kembali secara opsional dalam fungsi JavaScript. Itu kata kunci. Tapi apa tipe sebenarnya dari returndirinya sendiri. Sebenarnya saya sempat bingung melihat contoh:

function add(a, b) {
  return (
    console.log(a + b),
    console.log(arguments)
  );
}

add(2, 2);

Keluaran:

4
[2, 2]

Jadi, kita bisa meneruskan ekspresi yang dipisahkan koma ke dalam returnpernyataan. Apakah ini sebuah fungsi?

Dan mulai dengan ini, dapatkah kita menebak dengan liar bahwa setiap kata kunci di JavaScript pada akhirnya adalah sebuah fungsi?

Saya telah menulis blog kecil sebagai inti dari diskusi ini. Anda mungkin ingin memeriksanya di sini .

Arnab Das
sumber
106
Tepat ketika Anda mengira Anda memahami JavaScript, sesuatu seperti ini muncul ...
FP
12
Kurung tidak berarti bahwa Anda memanggil fungsi, misalnya: var a = (2 + 2).
alexmac
12
Contoh yang lebih tepat adalah var a = (1, 2)- ini akan menghasilkan anilai 2karena bagaimana operator koma beroperasi (ya, koma adalah operator matematika seperti +atau |dll, khususnya, dalam kalkulus lambada, operator koma mengimplementasikan kombinator K)
slebetman
6
Kemudian tindakan yang benar adalah menghilangkan prasangka kesalahpahaman, bukan suara negatif.
el.pescado
5
@FlorianPeschka tidak juga, ini adalah perilaku standar dalam bahasa C-flavor apa pun.
djechlin

Jawaban:

129

Tapi apa sebenarnya jenis 'pengembalian' itu sendiri.

Itu tidak memiliki tipe, itu bukan nilai.

Mencoba typeof return;akan memberi Anda Unexpected token return.

Jadi, kita bisa meneruskan ekspresi yang dipisahkan koma ke dalam pernyataan return. Apakah ini sebuah fungsi?

Tidak, meskipun tanda kurung dapat digunakan untuk memanggil suatu fungsi, di sini mereka adalah operator pengelompokan yang berisi beberapa ekspresi yang dipisahkan oleh operator koma .

Demonstrasi yang lebih berguna adalah:

function add(a, b) {
  return (
    (a + b),
    (a - b)
  );
}

console.log(add(2, 2));

Output mana 0karena hasil a + bdiabaikan (di sebelah kiri operator koma) dan a - bdikembalikan.

Quentin
sumber
Tapi, pada contoh di atas, setiap ekspresi dieksekusi, satu per satu. Saya pikir kasus yang Anda nyatakan berbeda dari apa yang disebutkan dalam pertanyaan awal. Yang ingin saya katakan adalah di mana prioritas operator Grup diterapkan di sana.
Arnab Das
3
@ArnabDas - Ya, mereka dieksekusi, tetapi returntidak bisa melihatnya, itulah yang Anda tanyakan.
Quentin
@ArnabDas - "Yang ingin saya katakan adalah di mana prioritas operator Grup diterapkan di sana." - Di dalam operator pengelompokan tentunya.
Quentin
11
@ArnabDas - Ya. a + bhanya tidak memiliki efek yang terlihat karena tidak memiliki efek samping dan nilainya akan dibuang (yah, mengingat tidak memiliki efek praktis, mungkin saja kompilator yang mengoptimalkan dapat menghapusnya, tetapi tidak ada perbedaan praktis).
Quentin
1
@ Roman itu benar. Satu-satunya cara untuk "melewati" ekspresi sehingga tidak dieksekusi (selain dari dalam kondisi seperti if) adalah evaluasi hubung singkat. Operator koma tidak mengalami korsleting, jadi akan mengevaluasi kedua sisi. Maka itu akan mengembalikan yang terakhir. Itu secara eksplisit adalah tujuan dari operator koma, untuk mengelompokkan ekspresi dalam satu pernyataan (biasanya nilai yang dihasilkan dari seluruh pernyataan diabaikan), paling sering digunakan untuk inisialisasi variabel:var i = 2, j = 3, k = 4;
Jason
32

Saya agak terkejut bahwa tidak ada orang di sini yang secara langsung mereferensikan spesifikasi :

12.9 Sintaks Pernyataan return ReturnStatement: return; kembali [tidak ada LineTerminator di sini] Ekspresi;

Semantik

Program ECMAScript dianggap tidak benar secara sintaksis jika program tersebut berisi pernyataan pengembalian yang tidak berada dalam FunctionBody. Pernyataan kembali menyebabkan fungsi menghentikan eksekusi dan mengembalikan nilai ke pemanggil. Jika Ekspresi dihilangkan, nilai yang dikembalikan tidak ditentukan. Jika tidak, nilai yang dikembalikan adalah nilai Ekspresi.

ReturnStatement dievaluasi sebagai berikut:

Jika Ekspresi tidak ada, kembali (return, undefined, empty). Membiarkan exprRefmenjadi hasil evaluasi Ekspresi. Kembali (return, GetValue(exprRef), empty).

Jadi, karena spesifikasinya, contoh Anda berbunyi:

return ( GetValue(exprRef) )

dimana exprRef = console.log(a + b), console.log(arguments)

Yang menurut spek pada operator koma ...

Semantik

Ekspresi produksi: Ekspresi, Ekspresi Tugas dievaluasi sebagai berikut:

Let lref be the result of evaluating Expression.
Call GetValue(lref).
Let rref be the result of evaluating AssignmentExpression.
Return GetValue(rref).

... berarti bahwa setiap ekspresi akan dievaluasi hingga item terakhir dalam daftar koma, yang menjadi ekspresi tugas. Jadi kode Anda return (console.log(a + b) , console.log(arguments))akan

1.) mencetak hasil a + b

2.) Tidak ada yang tersisa untuk dieksekusi, jadi jalankan ekspresi berikutnya yaitu

3.) mencetak arguments, dan karena console.log()tidak menentukan pernyataan pengembalian

4.) Mengevaluasi menjadi tidak terdefinisi

5.) Yang kemudian dikembalikan ke pemanggil.

Jadi jawaban yang benar adalah, returntidak memiliki tipe, ini hanya mengembalikan hasil dari beberapa ekspresi.

Untuk pertanyaan selanjutnya:

Jadi, kita bisa meneruskan ekspresi yang dipisahkan koma ke dalam pernyataan return. Apakah ini sebuah fungsi?

Tidak. Koma dalam JavaScript adalah operator, yang ditentukan untuk memungkinkan Anda menggabungkan beberapa ekspresi menjadi satu baris, dan ditentukan oleh spesifikasi untuk mengembalikan ekspresi yang dievaluasi dari apa pun yang terakhir dalam daftar Anda.

Kamu masih tidak percaya padaku?

<script>
alert(foo());
function foo(){
    var foo = undefined + undefined;
    console.log(foo);
    return undefined, console.log(1), 4;
}
</script>

Mainkan kode itu di sini dan ubah dengan nilai terakhir dalam daftar. Itu akan selalu mengembalikan nilai terakhir dalam daftar, dalam kasus Anda itu terjadi begitu sajaundefined.

Untuk pertanyaan terakhir Anda,

Dan mulai dengan ini, dapatkah kita menebak dengan liar bahwa setiap kata kunci di JavaScript pada akhirnya adalah sebuah fungsi?

Sekali lagi tidak. Fungsi memiliki definisi yang sangat spesifik dalam bahasanya. Saya tidak akan mencetak ulang di sini karena jawaban ini sudah menjadi sangat panjang.

avgvstvs
sumber
15

Menguji apa yang terjadi ketika Anda mengembalikan nilai dalam tanda kurung:

function foo() {
    return (1, 2);
}

console.log(foo());

Memberikan jawabannya 2, sehingga tampak bahwa daftar nilai yang dipisahkan koma mengevaluasi ke elemen terakhir dalam daftar.

Sungguh, tanda kurung tidak relevan di sini, mereka mengelompokkan operasi alih-alih menandai panggilan fungsi. Namun, yang mungkin mengejutkan adalah bahwa koma legal di sini. Saya menemukan entri blog yang menarik tentang cara menangani koma di sini:

https://javascriptweblog.wordpress.com/2011/04/04/the-javascript-comma-operator/

SpoonMeiser
sumber
Saya kira kemudian, sesuatu seperti return ([1, 2])harus mencetak kedua nilai?
cst1992
1
Itu akan mengembalikan array.
Kami Stan Uji Cakupan
console.log((1, 2))
kembali
9

returnbukanlah sebuah fungsi. Ini adalah kelanjutan dari fungsi tempat terjadinya.

Pikirkan tentang pernyataan di alert (2 * foo(bar));mana foonama suatu fungsi. Saat Anda mengevaluasinya, Anda melihat bahwa Anda harus mengesampingkan pernyataan lainnya sejenak untuk berkonsentrasi pada evaluasi foo(bar). Anda dapat memvisualisasikan bagian yang Anda sisihkan sebagai sesuatu seperti alert (2 * _), dengan kosong untuk diisi. Ketika Anda tahu apa nilainya foo(bar), Anda mengambilnya lagi.

Hal yang Anda sisihkan adalah kelanjutan panggilan foo(bar).

Memanggil returnmemberi nilai pada kelanjutan itu.

Saat Anda mengevaluasi suatu fungsi di dalam foo, sisa waktu foomenunggu fungsi tersebut berkurang menjadi nilai, lalu foomengambilnya kembali. Anda masih punya tujuan untuk mengevaluasi foo(bar), itu hanya dijeda.

Ketika Anda mengevaluasi returndi dalam foo, tidak ada bagian dari foomenunggu nilai. returntidak mengurangi nilai di tempat di dalam footempat Anda menggunakannya. Alih-alih, hal itu menyebabkan seluruh panggilan foo(bar) berkurang menjadi suatu nilai, dan tujuan "evaluasi foo(bar)" dianggap selesai dan dilenyapkan.

Orang biasanya tidak memberi tahu Anda tentang kelanjutan ketika Anda baru mengenal pemrograman. Mereka menganggapnya sebagai topik maju, hanya karena ada yang beberapa hal yang sangat canggih bahwa orang akhirnya melakukan dengan keberlanjutan. Tapi kenyataannya, Anda menggunakannya selama ini, setiap kali Anda memanggil suatu fungsi.

Nathan Ellis Rasmussen
sumber
Pemikiran semacam ini biasa terjadi dengan bahasa fungsional, seperti LISP dan Haskell, tetapi saya belum pernah melihatnya digunakan dalam bahasa prosedural seperti javascript. Orang biasanya tidak menganggapnya sebagai kelanjutan karena kebanyakan bahasa prosedural memperlakukannya sebagai kasus khusus. Misalnya, Anda tidak dapat menyimpan kelanjutan alert(2 * _)untuk digunakan nanti dalam Javascript (ada notasi berbeda untuk melakukan hal semacam itu).
Cort Ammon
1
@CortAmmon Meskipun saya agak setuju dengan Anda, perhatikan bahwa Javascript memang memiliki latar belakang fungsional yang cukup kuat (awalnya dimaksudkan sebagai implementasi subset Skema dengan sintaks mirip C), dan meskipun tidak memiliki fungsi standar apa pun untuk memanipulasi kontinuitas (misalnya call / cc) adalah praktik umum untuk menggunakan gaya penerusan lanjutan di banyak pustaka javascript, jadi memahami konsep pada tahap awal cukup berguna bagi pemrogram JS.
Jules
Memang benar bahwa Javascript tidak melakukan kelanjutan kelas satu . Dari returndalam foobukanlah sebuah nilai; Anda tidak dapat mengemasnya dalam struktur data, menetapkannya ke variabel, menunggu beberapa saat dan kemudian memberi makan sesuatu nanti - dan Anda terutama tidak dapat memberinya makan lebih dari sekali. Tapi, seperti yang saya katakan, topik lanjutan.
Nathan Ellis Rasmussen
Demikian juga, menerapkan struktur kontrol baru menggunakan lanjutan: lanjutan. Tetapi mencoba mengimplementasikan my_iffungsi yang berperilaku seperti built-in if()then{}else{}, dan gagal melakukannya bahkan jika Anda membiarkan diri Anda menggunakan built-in di dalamnya - tidak terlalu maju, dan sangat instruktif, terutama jika Anda akan mengakhirinya up dalam kode yang melewati callback.
Nathan Ellis Rasmussen
6

di returnsini adalah ikan haring merah. Mungkin yang menarik adalah variasi berikut:

function add(a, b) {
  return (
    console.log(a + b),
    console.log(arguments)
  );
}

console.log(add(2, 2));

yang dihasilkan sebagai baris terakhir

undefined

karena fungsinya tidak benar-benar mengembalikan apa pun. (Ini akan mengembalikan nilai pengembalian dari detik console.log, jika ada).

Karena itu, kodenya persis sama dengan

function add(a, b) {
    console.log(a + b);
    console.log(arguments);
}

console.log(add(2, 2));
ths
sumber
1

Cara yang menarik untuk memahami pernyataan return adalah melalui operator void. Lihatlah kode ini

var console = {
    log: function(s) {
      document.getElementById("console").innerHTML += s + "<br/>"
    }
}

function funReturnUndefined(x,y) {
   return ( void(x+y) );
}

function funReturnResult(x,y) {
   return ( (x+y) );
}

console.log( funReturnUndefined(2,3) );
console.log( funReturnResult(2,3) );
<div id="console" />

Karena returnpernyataan tersebut mengambil satu argumen [[expression]]dan mengembalikan ini ke pemanggil pada stack yaitu arguments.callee.callerkemudian akan dieksekusi void(expression)dan kemudian kembali undefineditulah evaluasi dari operator void.

loretoparisi
sumber