Urutan fungsi JavaScript: mengapa itu penting?

106

Pertanyaan Asli:

JSHint mengeluh ketika JavaScript saya memanggil fungsi yang didefinisikan jauh di bagian bawah halaman daripada memanggilnya. Namun, halaman saya adalah untuk permainan, dan tidak ada fungsi yang dipanggil sampai semuanya diunduh. Jadi mengapa fungsi order muncul di kode saya penting?

EDIT: Saya pikir saya mungkin telah menemukan jawabannya.

http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting

Saya mengerang di dalam. Sepertinya saya perlu menghabiskan satu hari lagi untuk memesan enam ribu baris kode. Kurva belajar dengan javascript tidak curam sama sekali, tapi sangat luuuuuuuuuuuuuuuuuuuuu

Chris Tolworthy
sumber
1 untuk referensi luar biasa dalam pembaruan. Dan saya harap itu meyakinkan Anda bahwa Anda tidak perlu memesan ulang kode Anda. :)
awm

Jawaban:

294

tl; dr Jika Anda tidak menelepon apa pun sampai semuanya dimuat, Anda akan baik-baik saja.


Edit: Untuk gambaran umum yang juga mencakup beberapa deklarasi ES6 ( let, const): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Scope_Cheatsheet

Perilaku aneh ini bergantung pada

  1. Bagaimana Anda mendefinisikan fungsi dan
  2. Saat Anda menelepon mereka.

Berikut beberapa contohnya.

bar(); //This won't throw an error
function bar() {}

foo(); //This will throw an error
var foo = function() {}
bar();
function bar() {
    foo(); //This will throw an error
}
var foo = function() {}
bar();
function bar() {
    foo(); //This _won't_ throw an error
}
function foo() {}
function bar() {
    foo(); //no error
}
var foo = function() {}
bar();

Ini karena sesuatu yang disebut mengangkat !

Ada dua cara untuk mendefinisikan fungsi: Deklarasi fungsi dan ekspresi fungsi . Perbedaannya menjengkelkan dan menit, jadi anggap saja hal ini sedikit salah: Jika Anda menulisnya seperti function name() {}, itu adalah deklarasi , dan ketika Anda menulisnya seperti var name = function() {}(atau fungsi anonim yang ditugaskan untuk mengembalikan, hal-hal seperti itu), itu ekspresi fungsi .

Pertama, mari kita lihat bagaimana variabel ditangani:

var foo = 42;

//the interpreter turns it into this:
var foo;
foo = 42;

Sekarang, bagaimana deklarasi fungsi ditangani:

var foo = 42;
function bar() {}

//turns into
var foo; //Insanity! It's now at the top
function bar() {}
foo = 42;

The varpernyataan "melempar" yang penciptaan dari fooke puncak, tetapi tidak menetapkan nilai untuk itu belum. Deklarasi fungsi muncul di baris berikutnya, dan akhirnya sebuah nilai ditetapkan ke foo.

Dan bagaimana dengan ini?

bar();
var foo = 42;
function bar() {}
//=>
var foo;
function bar() {}
bar();
foo = 42;

Hanya deklarasi dari foodipindahkan ke atas. Penugasan dilakukan hanya setelah panggilan ke bardilakukan, di mana sebelum semua pengangkatan terjadi.

Dan akhirnya, untuk ringkasnya:

bar();
function bar() {}
//turns to
function bar() {}
bar();

Sekarang, bagaimana dengan ekspresi fungsi ?

var foo = function() {}
foo();
//=>
var foo;
foo = function() {}
foo();

Sama seperti variabel biasa, pertama fooadalah dideklarasikan pada titik tertinggi dari ruang lingkup, maka ditugaskan nilai.

Mari kita lihat mengapa contoh kedua memunculkan kesalahan.

bar();
function bar() {
    foo();
}
var foo = function() {}
//=>
var foo;
function bar() {
    foo();
}
bar();
foo = function() {}

Seperti yang telah kita lihat sebelumnya, hanya pembuatan dari fooyang diangkat, tugas datang di tempat ia muncul di kode "asli" (tidak dikerek). Ketika bardipanggil, itu sebelum foodiberi nilai, jadi foo === undefined. Sekarang di dalam function-body dari bar, seolah-olah Anda sedang melakukan undefined(), yang membuat kesalahan.

Zirak
sumber
Maaf untuk menggali ini, tetapi apakah kelebihan beban seperti Array.prototype.someMethod = function () {} diangkat? Sepertinya saya mendapatkan kesalahan jika hal-hal semacam ini berada di akhir skrip saya.
Tepi
Bagaimana Anda memastikan semuanya dimuat ? Apakah ada praktik umum?
zwcloud
6

Alasan utamanya mungkin karena JSLint hanya melakukan satu penyampaian pada file sehingga tidak tahu Anda akan mendefinisikan fungsi seperti itu.

Jika Anda menggunakan sintaks pernyataan fungsi

function foo(){ ... }

Sebenarnya tidak ada perbedaan sama sekali di mana Anda mendeklarasikan fungsi (itu selalu berperilaku seolah-olah deklarasi ada di awal).

Di sisi lain, jika fungsi Anda disetel seperti variabel biasa

var foo = function() { ... };

Anda harus menjamin Anda tidak akan memanggilnya sebelum inisialisasi (ini sebenarnya bisa menjadi sumber bug).


Karena menyusun ulang banyak kode itu rumit dan bisa menjadi sumber bug itu sendiri, saya sarankan Anda mencari solusinya. Saya cukup yakin Anda bisa memberi tahu JSLint nama variabel global sebelumnya sehingga tidak mengeluh tentang hal-hal yang tidak dideklarasikan.

Beri komentar di awal file

/*globals foo1 foo2 foo3*/

Atau Anda dapat menggunakan kotak teks di sana untuk itu. (Saya juga berpikir Anda bisa meneruskan ini dalam argumen ke fungsi jslint batin jika Anda bisa ikut campur dengannya.)

hugomg
sumber
Terima kasih. Jadi baris / * globals * / akan berfungsi? Bagus - apa saja agar JsHint menyukai saya. Saya masih baru mengenal JavaScript dan mendapatkan jeda yang tidak bisa dijelaskan saat menyegarkan halaman, namun tidak ada bug yang dilaporkan. Jadi saya pikir solusinya adalah bermain dengan semua aturan dan kemudian melihat apakah itu masih terjadi.
Chris Tolworthy
4

Ada terlalu banyak orang yang mendorong aturan sewenang-wenang tentang bagaimana JavaScript harus ditulis. Kebanyakan peraturan adalah omong kosong.

Pengangkatan fungsi adalah fitur dalam JavaScript karena ini adalah ide yang bagus.

Jika Anda memiliki fungsi internal yang sering kali merupakan kegunaan fungsi dalam, menambahkannya ke awal fungsi luar adalah gaya penulisan kode yang dapat diterima, tetapi ada kekurangannya yaitu Anda harus membaca detailnya untuk mendapatkan apa fungsi luar tidak.

Anda harus tetap berpegang pada satu prinsip di seluruh basis kode Anda, baik menempatkan fungsi pribadi terlebih dahulu atau terakhir dalam modul atau fungsi Anda. JSHint bagus untuk menegakkan konsistensi, tetapi Anda harus SEBENARNYA menyesuaikan .jshintrc agar sesuai dengan kebutuhan Anda, TIDAK menyesuaikan kode sumber Anda dengan konsep pengkodean aneh orang lain.

Salah satu gaya pengkodean yang mungkin Anda lihat di alam liar harus Anda hindari karena tidak memberi Anda keuntungan dan hanya kemungkinan kesulitan refactoring:

function bigProcess() {
    var step1,step2;
    step1();
    step2();

    step1 = function() {...};
    step2 = function() {...};
}

Inilah fungsi hoisting yang harus dihindari. Pelajari saja bahasanya dan manfaatkan kekuatannya.

Henrik Vendelbo
sumber
2

Hanya deklarasi fungsi yang diangkat, bukan ekspresi fungsi (penugasan).

Abhay Srivastav
sumber