JavaScript yang menyeronokkan: Tiada pendakap kerinting. Salah faham tentang cara julat dibahagikan. Variasi pada tema

Katakan saya menulis

If(someVal) alert("True");

Kemudian pembangun seterusnya datang dan berkata, "Oh, saya perlu melakukan sesuatu yang lain," jadi mereka menulis

If(someVal) alert("True"); alert("JugaBenar");

Sekarang seperti yang anda lihat "AndTrue" akan sentiasa benar kerana pembangun pertama tidak menggunakan pendakap gigi.

2018-12-04T00:00Z

Sudah Tentu Ya

Lupakan tentang "Ia adalah pilihan peribadi", "kod akan berfungsi dengan baik", "ia berfungsi dengan baik untuk saya", "ia lebih mudah dibaca" yada yada BS.

Hujah: "Ia adalah pilihan peribadi"

Tidak. Melainkan anda satu-satunya orang yang pergi ke Marikh, tidak. Selalunya akan ada orang lain yang membaca/mengubah suai kod anda. Dalam mana-mana pasukan pengekodan yang serius ini akan menjadi cara yang disyorkan, jadi ini bukan "keutamaan peribadi".

Hujah: "kod akan berfungsi dengan baik"

Begitu juga kod spageti! Adakah ini bermakna ia ok untuk menciptanya?

Hujah: "Ia berfungsi hebat untuk saya"

Dalam kerjaya saya, saya telah melihat begitu banyak pepijat yang dicipta kerana masalah ini. Anda mungkin tidak ingat berapa kali anda telah mengulas "DoSomething()" dan keliru mengapa "SomethingElse()" dipanggil:

Jika (syarat) DoSomething(); Sesuatu yang lain();

Jika (syarat) DoSomething(); Sesuatu yang lebih();

Berikut adalah contoh kehidupan sebenar. Seseorang ingin mendayakan semua pengelogan supaya mereka akan menjalankan find & replace "Console.println" => //"Console.println" :

Jika (syarat) Console.println("sesuatu"); Sesuatu yang lain();

Lihat Masalah?

Walaupun anda berfikir: "Ini sangat remeh sehingga saya tidak akan melakukannya"; ingat bahawa akan sentiasa ada ahli pasukan yang mempunyai kemahiran pengaturcaraan yang lebih rendah daripada anda (semoga anda bukan yang paling teruk dalam pasukan!)

Hujah: "ia lebih mudah dibaca"

Jika saya telah belajar apa-apa tentang pengaturcaraan, ia adalah sangat mudah. Ia adalah perkara biasa bahawa ini adalah:

Jika (syarat) DoSomething();

bertukar menjadi berikut selepas ia telah diuji dengan pelayar yang berbeza/ persekitaran / kes penggunaan atau ciri baharu ditambah:

Jika (a != null) jika (syarat) DoSomething(); else DoSomethingElse(); DoSomethingMore(); else if (b == null) alert("ralat b"); else alert("ralat a");

Dan bandingkan dengan ini:

Jika (a != null) ( if (condition) ( DoSomething(); ) else ( DoSomethingElse(); DoSomethingMore(); ) ) else if (b == null) ( alert("error b"); ) else ( makluman("ralat a"); )

PS: Mata Bonus pergi ke sesiapa yang perasan ralat dalam contoh di atas

11-12-2018T00:00Z

Sebagai tambahan kepada alasan yang disebut oleh @Josh K (yang juga terpakai untuk Java, C, dll.), salah satu daripada masalah khas dalam JavaScript ialah memasukkan koma bertitik secara automatik. Daripada contoh di Wikipedia:

Kembalikan a + b; // Mengembalikan tidak ditentukan. Dilayan sebagai: // return; // a + b;

Jadi ia juga mungkin memberikan hasil yang tidak dijangka jika digunakan seperti ini:

Jika (x) kembalikan a + b;

Ia sebenarnya tidak lebih baik untuk menulis

Jika (x) ( kembalikan a + b; )

tetapi mungkin ralat di sini lebih mudah untuk dikesan (?)

18-12-2018T00:00Z

Soalan bertanya tentang pernyataan pada satu baris. Walau bagaimanapun, banyak contoh memberi alasan untuk tidak meninggalkan pendakap kerinting merentas berbilang baris. Adalah agak selamat untuk tidak menggunakan kurungan pada baris yang sama jika itu gaya pengekodan yang anda suka.

Sebagai contoh, soalan bertanya sama ada ini adalah perkara biasa:

Jika (syarat) kenyataan;

Dia tidak bertanya sama ada semuanya baik-baik saja:

Jika (syarat) kenyataan;

Saya fikir meninggalkan kurungan adalah lebih baik kerana ia menjadikan kod lebih mudah dibaca dengan sintaks yang kurang berlebihan.

Gaya pengekodan saya ialah jangan sekali-kali menggunakan kurungan melainkan kod itu adalah blok. Dan jangan sekali-kali menggunakan berbilang penyataan pada baris yang sama (dipisahkan dengan koma bertitik). Saya mendapati ia mudah dibaca dan dibersihkan dan tidak pernah menghadapi masalah dengan pernyataan "jika". Akibatnya, menggunakan kurungan dalam satu jika keadaan memerlukan 3 baris. seperti ini:

Jika (syarat) ( pernyataan; )

Menggunakan satu talian jika pengendali lebih disukai kerana ia menggunakan kurang ruang menegak, dan kodnya lebih padat.

Saya tidak akan memaksa orang lain untuk menggunakan kaedah ini, tetapi ia berfungsi untuk saya dan saya tidak boleh tidak bersetuju lagi dengan contoh yang diberikan tentang cara untuk mengecualikan kurungan kerana ralat pengekodan/skop.

25-12-2018T00:00Z

Tidak

Ini benar sekali

Jika (syarat) makluman("Keadaan dipenuhi!") makluman lain("Keadaan tidak dipenuhi!")

Amalan yang sama ini diikuti dalam semua bahasa gaya sintaks C dengan mengikat. C, C++, Java, malah PHP semuanya menyokong operator satu baris tanpa kurungan. Anda mesti faham bahawa anda hanya menyimpan dua watak, dan dengan beberapa gaya, bagi sesetengah orang, anda tidak mengekalkan garisan. Saya lebih suka gaya pendakap penuh (seperti berikut), jadi ia cenderung lebih lama sedikit. Pertukaran itu dipenuhi dengan baik dengan fakta bahawa anda mempunyai kebolehbacaan kod yang sangat jelas.

Jika (syarat) ( makluman("Keadaan dipenuhi!") ) lain ( makluman("Keadaan tidak dipenuhi!") )

01-01-2019T00:00Z

Tahap pertama Lekukan pernyataan mestilah sama dengan bilangan pendakap kerinting terbuka di atasnya. (tidak termasuk tanda kurung atau simbol yang dipetik atau diulas dalam arahan prapemproses)

Jika tidak K&R akan lekukan yang baik. Untuk membetulkan gaya mereka, saya mengesyorkan menyiarkan pendek pengendali mudah jika pada satu baris.

JavaScript cantik dalam bahasa mudah bagi mereka yang ingin mula mempelajarinya, tetapi untuk mencapai penguasaan di dalamnya, anda perlu melakukan banyak usaha.

Pemula sering membuat beberapa kesilapan biasa yang kembali menghantui mereka apabila mereka tidak menjangkakannya. Untuk memahami apakah ralat ini, baca artikel ini.

1. Mengeluarkan pendakap kerinting

Salah satu kesilapan yang sering dilakukan oleh pemula JavaScript ialah melangkau pendakap kerinting selepas pernyataan seperti jika, lain, sementara, dan untuk . Walaupun ini tidak dilarang, anda mesti berhati-hati kerana ia boleh menyebabkan masalah tersembunyi dan membawa kepada ralat kemudian.

Lihat contoh di bawah:

JS

// Kod ini tidak melakukan apa yang sepatutnya dilakukan! if(name === undefined) console.log("Sila masukkan nama pengguna!"); gagal(); // Pelaksanaan tidak akan sampai ke baris ini: success(name); ) function success(name)( console.log("Hello, " + name + "!"); ) function fail())( throw new Error("Nama tiada. Tidak boleh bertanya khabar!"); )

Walaupun panggilan fail() diinden dan nampaknya tergolong dalam pernyataan if, ia tidak. Ia sentiasa dipanggil. Jadi apa itu amalan yang bermanfaat kelilingi semua blok kod dengan pendakap kerinting, walaupun ia mengandungi hanya satu pernyataan.

2. Tiada titik bertitik

Apabila JavaScript dihuraikan, proses yang dikenali sebagai koma bertitik automatik dilakukan. Seperti namanya, penganalisis mengisi aksara yang hilang untuk anda.

Tujuan ciri ini adalah untuk menjadikan JavaScript lebih mudah diakses dan lebih mudah untuk ditulis untuk pemula. Walau bagaimanapun, anda harus sentiasa menambah koma bertitik itu sendiri kerana terdapat risiko kehilangannya.

Berikut ialah contoh:

JS

// Hasil pemprosesan kod ini akan menjadi mesej ralat. Menambah koma bertitik akan menyelesaikan masalah. console.log("Selamat datang persekutuan!") ["Frodo", "Gandalf", "Legolas", "Gimli"].forEach(fungsi(nama)( hello(nama) )) function hello(nama)( console. log("Hello, " + nama + "!") )

Oleh kerana tiada koma bernoktah pada baris 3, penghurai menganggap bahawa kurungan pembukaan pada baris 5 ialah percubaan untuk mengakses harta menggunakan sintaks pengakses tatasusunan (lihat pepijat #8), dan bukannya tatasusunan berasingan, yang bukan seperti yang sepatutnya. akan menjadi.

Ini mengakibatkan ralat. Penyelesaiannya adalah mudah - sentiasa masukkan koma bertitik.

Ada yang berpengalaman pembangun JavaScript memilih untuk tidak menggunakan koma bertitik, tetapi mereka sedar kemungkinan kesilapan, dan cara mencegahnya.

3. Salah faham jenis cast

JavaScript disokong jenis dinamik. Ini bermakna anda tidak perlu menentukan jenis apabila mengisytiharkan pembolehubah baharu, anda boleh menetapkan semula atau menukar nilainya secara bebas.

Ini menjadikan JavaScript lebih mudah daripada, katakan, C# atau Java. Tetapi ini penuh dengan potensi bahaya kesilapan, yang dalam bahasa lain dikesan pada peringkat penyusunan.

Berikut ialah contoh:

JS

// Tunggu acara input daripada kotak teks var textBox = document.querySelector("input"); textBox.addEventListener("input", function())( // textBox.value mengandungi rentetan. Menambah 10 mengandungi // rentetan "10" tetapi tidak menambahnya. console.log(textBox.value + " + 10 = " + (textBox.value + 10)); ));

HTML

Masalahnya boleh diselesaikan dengan mudah dengan menggunakan parseInt(textBox.value, 10) untuk menukar rentetan kepada nombor sebelum menambah 10 padanya.

Bergantung pada cara anda menggunakan pembolehubah, masa jalan mungkin memutuskan bahawa ia harus ditukar kepada satu jenis atau yang lain. Ini dipanggil pelakon jenis.

Untuk mengelakkan penukaran jenis apabila membandingkan pembolehubah dalam pernyataan if, anda boleh gunakan pemeriksaan kesaksamaan yang ketat (=== ).

4. Terlupa var

Satu lagi kesilapan newbies ialah mereka terlupa menggunakan kata kunci var apabila mengisytiharkan pembolehubah. JavaScript ialah enjin yang sangat liberal.

Kali pertama ia melihat bahawa anda telah menggunakan pembolehubah tanpa operator var, ia akan mengisytiharkannya secara global secara automatik. Ini mungkin membawa kepada beberapa ralat.

Berikut ialah contoh yang turut menggambarkan ralat lain - koma yang hilang apabila mengisytiharkan beberapa pembolehubah sekaligus:

JS

var a = 1, b = 2, c = 3; function alphabet(str)( var a = "A", b = "B" // Oops, tiada ","! c = "C", d = "D"; return str + " " + a + b + c + "…"; ) console.log(alphabet("Mari"sebut abjad!")); // Oh, tidak! Ada yang tidak kena! c mempunyai nilai baharu! console.log(a, b, c);

Apabila penghurai mencapai baris 4, ia akan menambah koma bernoktah secara automatik dan kemudian mentafsirkan pengisytiharan c dan d pada baris 5 sebagai global.

Ini akan mengubah nilai pembolehubah lain c. Lebih lanjut mengenai Perangkap JavaScript di sini.

5. Aritmetik titik terapung

Ralat ini adalah tipikal untuk hampir semua bahasa pengaturcaraan, termasuk JavaScript. Kerana cara itu nombor titik terapung diwakili dalam ingatan, operasi aritmetik jangan bekerja dengan cara yang anda fikirkan.

Sebagai contoh:

JS

var a = 0.1, b = 0.2; // Kejutan! Ini salah: console.log(a + b == 0.3); // Kerana 0.1 + 0.2 tidak menambah apa yang anda jangkakan: console.log("0.1 + 0.2 = ", a + b);

Untuk mengatasi masalah ini, anda tidak seharusnya menggunakan nombor perpuluhan, jika anda memerlukan ketepatan mutlak, gunakan integer atau jika anda perlu bekerja dengan unit monetari, gunakan perpustakaan seperti bignumber.js.

6. Menggunakan pembina dan bukannya tatatanda asal

Bila pengaturcara Java dan C# mula menulis JavaScript, mereka sering memilih untuk mencipta objek menggunakan pembina: new Array(), new Object(), new String().

JS

var elem4 = new Array(1,2,3,4); console.log("Empat elemen tatasusunan: " + elem4.length); // Buat tatasusunan satu elemen. Ini tidak berfungsi seperti yang anda fikirkan: var elem1 = new Array(23); console.log("Satu tatasusunan elemen? " + elem1.length); /* Objek rentetan juga mempunyai ciri tersendiri */ var str1 = new String("JavaScript"), str2 = "JavaScript"; // Kesamaan yang ketat tidak dipatuhi: console.log("Adakah str1 sama dengan str2?", str1 === str2);

Penyelesaian kepada masalah ini adalah mudah: cuba untuk sentiasa menggunakan asal literal
jawatan. Juga, dalam JS tidak perlu menentukan saiz tatasusunan terlebih dahulu.

7. Salah faham cara julat dibahagikan

Salah satu perkara yang paling sukar difahami oleh pemula JS ialah peraturan untuk membatasi dan menutup julat. Dan ia benar-benar tidak mudah:

JS

untuk(var i = 0; i< 10; i++){ setTimeout(function(){ console.log(i+1); }, 100*i); } /* Чтобы исправить проблему, заключите код в выражение самовыполняющейся функции: for(var i = 0; i < 10; i++){ (function(i){ setTimeout(function(){ console.log(i+1); }, 100*i); })(i); } */

Fungsi kekal dikaitkan dengan pembolehubah dalam julat induknya. Tetapi oleh kerana kita menangguhkan pelaksanaan melalui setTimeout , apabila tiba masanya untuk menjalankan fungsi, gelung sebenarnya akan selesai dan pembolehubah i akan dinaikkan kepada 11.

Fungsi laksana sendiri dalam ulasan berfungsi kerana ia menyalin nilai pembolehubah i dan menyimpan salinannya untuk setiap kelewatan dalam pelaksanaan fungsi. Anda boleh mengetahui lebih lanjut tentang julat di sini dan di sini.

8. Menggunakan Eval

Eval jahat. Ini dianggap amalan buruk dan dalam kebanyakan kes apabila anda berfikir untuk melakukannya, terdapat cara yang lebih baik dan lebih pantas.

JS

// Ini adalah amalan buruk. Tolong jangan lakukan ini: console.log(eval("obj.name + " is a " + obj." + access)); // Sebaliknya, gunakan tatasusunan anotasi untuk mengakses sifat secara dinamik: console.log(obj.name + " is a " + obj); /* Menggunakan eval dalam setTimout */ // Ini juga merupakan amalan buruk. Ia lambat dan sukar untuk diuji dan nyahpepijat: setTimeout(" if(obj.age == 30) console.log("Ini ialah kod eval-ed, " + obj + "!");", 100); // Ia akan menjadi lebih baik dengan cara ini: setTimeout(function())( if(obj.age == 30)( console.log("Kod ini bukan eval-ed, " + obj + "!"); ) ) , 100);

Kod di dalam eval ialah rentetan. Mesej penyahpepijatan yang dikaitkan dengan blok Eval mengelirukan, dan anda perlu memerah otak anda untuk mendapatkan petikan tunggal dan berganda dengan betul.

Belum lagi bahawa ia akan menjadi lebih perlahan daripada JavaScript biasa. Jangan gunakan Eval melainkan anda tahu dengan tepat apa yang anda lakukan.

9. Salah faham kod tak segerak

Apa yang benar-benar menjadikan JavaScript menonjol dan menjadikannya unik ialah hampir semuanya berfungsi secara tidak segerak dan anda perlu lulus fungsi panggil balik untuk dimaklumkan tentang acara.

Masih sukar bagi pemula untuk memahami perkara ini secara intuitif, dan mereka cepat menemui jalan buntu apabila berhadapan dengan ralat yang sukar difahami.

Berikut ialah contoh di mana saya menggunakan perkhidmatan FreeGeoIP untuk menentukan lokasi anda melalui alamat IP:

JS

// Tentukan data lokasi pengguna semasa. beban(); // Paparkan lokasi pengguna. Oops, itu tidak berfungsi! kenapa? console.log("Hello! Alamat IP anda ialah " + userData.ip + " dan negara anda ialah " + userData.country_name); // Fungsi yang dimuatkan akan menentukan ip pengguna semasa dan lokasinya // melalui ajax, menggunakan perkhidmatan freegeoip. Apabila ini dilakukan, ia akan meletakkan data // yang dikembalikan ke dalam pembolehubah userData. function load())( $.getJSON("http://freegeoip.net/json/?callback=?", function(response)( userData = response; // Keluarkan baris berikut dari komen untuk melihat // keputusan dikembalikan: // console.log(respons); )); )

Walaupun console.log terletak selepas panggilan fungsi beban() , ia sebenarnya dilaksanakan sebelum data ditakrifkan.

10. Penyalahgunaan pengesanan acara

Katakan anda ingin menjejak klik butang, tetapi hanya jika penyemak dipasang.

Bahagian ketiga pelajaran tentang kod JavaScript. Cadangan mudah untuk mencipta projek yang mudah diselenggara.

Elakkan penukaran jenis tersirat

JavaScript melibatkan penukaran jenis pembolehubah apabila membandingkannya. Oleh itu, perbandingan seperti false == 0 atau "" == 0 kembali benar .

Untuk mengelakkan masalah yang disebabkan oleh penukaran jenis tersirat, sentiasa gunakan operator === dan !== untuk menyemak kedua-dua nilai dan jenis ungkapan yang dibandingkan:

Terdapat satu lagi sekolah pengaturcaraan yang menganggapnya terlalu banyak menggunakan operator === apabila operator == mencukupi. Sebagai contoh, apabila menggunakan typeof, yang mengembalikan rentetan, tidak ada sebab untuk memerlukan padanan yang ketat. Tetapi JSLint memerlukan pematuhan yang ketat. Dan, sebagai tambahan, kod akan kelihatan lebih koheren dan mengurangkan jumlah pemikiran semasa membaca (" pengendali ini== digunakan secara sengaja atau secara tidak sengaja?").

Elakkan menggunakan eval()

Fungsi eval() mengambil rentetan arbitrari dan melaksanakannya sebagai kod JavaScript. Jika kod itu diketahui (dan tidak ditentukan semasa pelaksanaan proses), tiada sebab untuk menggunakan eval() sama sekali. Jika kod dijana secara dinamik pada masa jalan, maka selalunya mungkin untuk mencapai matlamat kaedah terbaik daripada menggunakan eval() . Contohnya, menggunakan notasi kurungan segi empat sama untuk mengakses sifat secara dinamik adalah lebih baik dan mudah:

// bad var property = "nama"; alert(eval("obj." + property)); // preferable to do this var property = "name"; makluman(obj);

Menggunakan eval() juga boleh mempunyai implikasi keselamatan, kerana anda mungkin melaksanakan kod (contohnya, diperoleh daripada rangkaian) yang berbahaya. Amalan buruk yang agak biasa apabila bekerja dengan respons JSON kepada permintaan AJAX. DALAM dalam kes ini Lebih baik menggunakan kaedah terbina dalam penyemak imbas untuk menghuraikan respons JSON untuk menyelesaikan masalah kaedah selamat dan memang betul. Untuk penyemak imbas yang tidak menyokong JSON.parse(), anda boleh menggunakan pustaka daripada JSON.org.

Ia juga penting untuk diingat bahawa menghantar rentetan kepada setInterval() , setTimeout() , dan Function() pembina adalah serupa dengan menggunakan eval() dalam kebanyakan kes. Oleh itu, tindakan sedemikian harus dielakkan. Di latar belakang JavaScript menilai dan melaksanakan rentetan yang anda masukkan seperti kod program:

// bad setTimeout("myFunc()", 1000); setTimeout("myFunc(1, 2, 3)", 1000); //sebaik-baiknya setTimeout(myFunc, 1000); setTimeout(fungsi () ( myFunc(1, 2, 3); ), 1000);

Menggunakan pembina Function() baharu adalah serupa dengan eval() dan harus digunakan dengan berhati-hati. ini alat yang berkuasa, tetapi sering digunakan secara salah. Jika anda benar-benar mesti menggunakan eval() , pertimbangkan untuk menggunakan Function() baru. Terdapat potensi manfaat kecil kerana kod yang ditakrifkan dalam Function() baharu akan dijalankan dalam ruang tempatan fungsi, jadi pembolehubah yang ditakrifkan dengan arahan var dalam kod yang ditakrifkan tidak akan menjadi global secara automatik. Satu lagi cara untuk mengelak pengesanan automatik pembolehubah global - bungkus panggilan eval() dalam fungsi.

Pertimbangkan contoh berikut. Di sini hanya un kekal sebagai pembolehubah global, mencemarkan ruang nama:

Console.log(jenis un); // "undefined" console.log(typeof deux); // "undefined" console.log(typeof trois); // "undefined" var jsstring = "var un = 1; console.log(un);"; eval(jsstring); // Log "1" jsstring = "var deux = 2; console.log(deux);"; Fungsi baru(jsstring)(); // Log "2" jsstring = "var trois = 3; console.log(trois);"; (fungsi () ( eval(jsstring); )()); // Log "3" console.log(typeof un); // number console.log(typeof deux); // undefined console.log(typeof trois); // tidak ditentukan

Satu lagi perbezaan antara eval() dan pembina Function() baharu ialah eval() boleh bertindih rantaian ruang nama, manakala pelaksanaan Fungsi berlaku dalam kotak pasir. Tidak kira di mana anda melaksanakan Function , ia hanya menggunakan ruang global nama Oleh itu ia kurang mencemarkan ruang tempatan nama Dalam contoh berikut, eval() boleh mengakses dan mengubah suai pembolehubah dalam ruang nama luarannya, tetapi Fungsi tidak boleh (penggunaan Fungsi dan Fungsi baharu adalah sama):

(fungsi () ( var local = 1; eval("local = 3; console.log(local)"); // Log "3" console.log(local); // Log "3" )()); (function () ( var local = 1; Function("console.log(typeof local);")(); // Log "undefined" )());

Menukar nombor menggunakan parseInt()

Menggunakan parseInt() anda boleh mendapatkan nombor daripada rentetan. Fungsi ini mengambil parameter kedua - asas sistem nombor, yang sering ditinggalkan. Tetapi sia-sia. Masalah berlaku apabila anda perlu menghuraikan rentetan yang bermula dengan 0: contohnya, sebahagian daripada tarikh yang dimasukkan ke dalam medan borang. Rentetan yang bermula dengan 0 dianggap sebagai nombor oktal(radix 8), yang ditakrifkan dalam ECMAScript 3 (tetapi diubah dalam ECMAScript 5). Untuk mengelakkan ketidakserasian dan hasil yang tidak dijangka, anda harus sentiasa menggunakan parameter radix:

Var bulan = "06", tahun = "09"; bulan = parseInt(bulan, 10); tahun = parseInt(tahun, 10);

Dalam contoh ini, jika anda meninggalkan parameter radix (panggil fungsi sebagai parseInt(tahun)), anda akan mendapat nilai 0 kerana "09" diandaikan sebagai nombor perlapanan (seolah-olah anda telah memanggil parseInt(tahun, 8) ), dan 09 ialah nombor yang salah dalam asas 8.

Kaedah Alternatif menukar rentetan kepada nombor:

+"08" // hasil 8 Nombor("08") // 8

Kaedah ini selalunya lebih pantas daripada parseInt() kerana parseInt() melakukan penghuraian rentetan dan bukannya penukaran mudah. Tetapi jika anda menganggap input mungkin dalam bentuk "08 hello", maka parseInt() akan mengembalikan nombor dan kaedah lain akan gagal dan mengembalikan NaN .

Keperluan kod

Adalah penting untuk menyediakan keperluan kod dan mengikutinya - langkah sedemikian akan menjadikan kod itu konsisten, boleh diramal dan lebih mudah dan difahami dengan ketara. Pembangun baharu dalam pasukan anda yang mengikut keperluan kod akan memasuki rentak kerja dengan lebih pantas, menerima kod yang ditulis oleh peserta projek lain.

Pertembungan dan pertengkaran serius timbul apabila berbincang pelbagai aspek keperluan kod (contohnya, sama ada untuk mengesot dengan ruang atau tab). Oleh itu, apabila memperkenalkan keperluan kod, anda perlu serius bersedia untuk perbincangan. Tetapi keperluan kod dan pematuhan ketat kepada mereka adalah sangat penting untuk kewujudan dan kejayaan pembangunan projek.

Lekukan

Sesetengah pembangun lebih suka menggunakan tab untuk lekukan, kerana semua orang boleh mengkonfigurasi editor mereka untuk mengeluarkan bilangan ruang tertentu dan bukannya tab mengikut keutamaan mereka. Yang lain lebih suka menggunakan ruang. Untuk intipati isu ini, ini tidak penting, perkara utama ialah lekukan ditakrifkan dalam keperluan kod semasa.

Di manakah lekukan harus dibuat? Peraturannya mudah - di mana sahaja terdapat pendakap kerinting. Iaitu, dalam badan fungsi, gelung (do, while, for, for-in), jika dan suis pernyataan, dan sifat objek. Kod berikut menunjukkan contoh penggunaan lekukan:

Fungsi luar(a, b) ( var c = 1, d = 2, dalam; jika (a > b) ( dalam = fungsi () ( kembali ( r: c - d ); ) ) lain ( dalam = fungsi ( ) ( kembali ( r: c + d ); ) kembali dalam; )

Pendakap gigi

Pendakap kerinting harus sentiasa digunakan, walaupun dalam kes di mana ia adalah pilihan. Secara teknikal, apabila anda hanya mempunyai satu ungkapan dalam pernyataan if atau for, pendakap kerinting tidak diperlukan, tetapi ia harus digunakan juga. Mereka menjadikan kod lebih konsisten dan lebih mudah untuk dilanjutkan.

Mari bayangkan anda mempunyai gelung dengan satu ungkapan. Anda boleh meninggalkan kurungan, yang tidak akan berlaku ralat sintaks:

// buruk untuk (var i = 0; i< 10; i += 1) alert(i);

Tetapi bagaimana jika anda kemudiannya perlu menambah baris lain pada badan gelung?

// buruk untuk (var i = 0; i< 10; i += 1) alert(i); alert(i + " is " + (i % 2 ? "odd" : "even"));

Kedua fungsi amaran berada di luar gelung, dan lekukan boleh memainkan jenaka buruk kepada anda. Adalah lebih baik untuk masa hadapan untuk sentiasa menggunakan kurungan, walaupun untuk blok satu baris:

// sebaiknya untuk (var i = 0; i< 10; i += 1) { alert(i); }

Juga untuk syarat:

// bad if (true) alert(1); else alert(2); // sebaik-baiknya jika (benar) ( ​​alert(1); ) else ( alert(2); )

Buka kedudukan kurungan

Persoalannya sering timbul: di manakah kurungan terbuka harus diletakkan - pada baris yang sama atau pada baris seterusnya?

Jika (benar) ( ​​alert("Mesej!"); )

Jika (benar) ( ​​alert("Mesej"); )

Dalam contoh ini, kedudukan kurungan adalah perkara keutamaan. Tetapi terdapat kes di mana program akan berkelakuan berbeza bergantung pada kedudukan kurungan. Keadaan ini berlaku kerana mekanisme penyisipan koma bertitik - JavaScript tidak faham apabila anda memilih untuk tidak menamatkan baris dengan betul dan akan menambah koma bertitik untuk anda. Tingkah laku ini boleh menyebabkan masalah apabila fungsi mengembalikan objek literal dan kurungan terbuka berada di baris seterusnya:

// amaran: unexpected return function func() ( return // mengikut blok kod yang tidak akan dilaksanakan ( nama: "Batman") )

Jika anda menjangkakan itu fungsi ini mengembalikan objek dengan harta nama, anda akan terkejut dengan tidak menyenangkan. Oleh kerana koma bertitik tersirat, fungsi akan kembali undefined . Kod sebelumnya adalah bersamaan dengan blok berikut:

// warning: unexpected return function func() ( return undefined; // mengikut blok kod yang tidak akan dilaksanakan ( nama: "Batman") )

Function function() ( return ( nama: "Batman" ); )

Perhatikan titik bertitik. Sama seperti pendakap kerinting, anda harus sentiasa menggunakan koma bertitik, walaupun ia tersirat oleh binaan kod JavaScript. Ini bukan sahaja mewujudkan disiplin dan selaras dengan pendekatan pengekodan yang lebih ketat, tetapi ia juga membantu mengelakkan situasi samar-samar seperti contoh di atas.

Saya selalu terkejut JavaScript Pertama sekali, ia mungkin tidak seperti bahasa lain yang digunakan secara meluas yang menyokong kedua-dua paradigma secara serentak: pengaturcaraan biasa dan tidak normal. Dan jika hampir segala-galanya telah dibaca tentang amalan terbaik dan templat yang mencukupi, maka dunia yang indah tentang bagaimana anda tidak perlu menulis kod, tetapi boleh, kekal sedikit terbuka.


Dalam artikel ini, kami akan menganalisis satu lagi masalah yang mengada-ada yang memerlukan pelanggaran penyelesaian biasa yang tidak boleh dimaafkan.


Tugasan sebelumnya:

Formulasi

Laksanakan fungsi penghias yang mengira bilangan panggilan ke fungsi yang diluluskan dan menyediakan keupayaan untuk mendapatkan semula nombor tersebut atas permintaan. Dalam keputusan dilarang gunakan pendakap kerinting dan pembolehubah global.

Kaunter panggilan hanyalah alasan, kerana terdapat console.count() . Intinya ialah fungsi kami mengumpul beberapa data apabila memanggil fungsi yang dibalut dan menyediakan beberapa jenis antara muka untuk mengaksesnya. Ini mungkin menyimpan semua hasil panggilan, mengumpul log, atau sejenis hafalan. Cuma kaunter itu primitif dan boleh difahami oleh semua orang.


Keseluruhan kesukaran terletak pada had yang tidak normal. Anda tidak boleh menggunakan pendakap kerinting, yang bermaksud anda perlu mempertimbangkan semula amalan harian dan sintaks biasa.

Penyelesaian biasa

Mula-mula anda perlu memilih titik permulaan. Biasanya, jika bahasa atau sambungannya tidak menyediakan fungsi yang diperlukan hiasan, kami melaksanakan sendiri bekas tertentu: fungsi yang dibungkus, data terkumpul dan antara muka untuk mengaksesnya. Selalunya ini adalah kelas:


kelas CountFunction ( constructor(f) ( this.calls = 0; this.f = f; ) invoke() ( this.calls += 1; return this.f(...arguments); ) ) const csum = new CountFunction ((x, y) => x + y); csum.invoke(3, 7); // 10 csum.invoke(9, 6); // 15 csum.calls; // 2

Ini tidak segera sesuai untuk kami, kerana:

  1. Dalam JavaScript, anda tidak boleh melaksanakan harta peribadi dengan cara ini: kita boleh membaca sama ada panggilan contoh (yang kita perlukan), dan tulis nilai ke dalamnya dari luar (yang TIDAK kita perlukan). Sudah tentu, kita boleh menggunakan penutupan dalam pembina, tetapi kemudian apa gunanya kelas? Saya masih akan berhati-hati menggunakan ladang persendirian yang segar tanpa babel 7.
  2. Bahasa ini menyokong paradigma berfungsi, dan instantiasi melalui baru nampaknya tiada di sini penyelesaian terbaik. Lebih baik menulis fungsi yang mengembalikan fungsi lain. Ya!
  3. Akhirnya, sintaks Pengisytiharan Kelas Dan KaedahDefinisi tidak akan membenarkan kita, tidak kira berapa banyak yang kita mahu, untuk menghilangkan semua pendakap kerinting.

Tetapi kami mempunyai yang hebat yang melaksanakan privasi menggunakan penutupan:


function count(f) ( biarkan panggilan = 0; return ( invoke: function() ( calls += 1; return f(...arguments); ), getCalls: function() ( return calls; ) ); ) const csum = count((x, y) => x + y); csum.invoke(3, 7); // 10 csum.invoke(9, 6); // 15 csum.getCalls(); // 2

Anda sudah boleh bekerja dengan ini.

Penyelesaian yang menarik

Untuk apa pendakap kerinting digunakan di sini? Ini adalah 4 kes berbeza:

  1. Mentakrifkan badan kiraan fungsi ( Pengisytiharan Fungsi)
  2. Memulakan objek yang dikembalikan
  3. Mentakrifkan badan panggilan fungsi ( FunctionExpression) dengan dua ungkapan
  4. Mentakrifkan badan fungsi getCalls( FunctionExpression) dengan satu ungkapan

Mari kita mulakan dengan kedua titik. Malah, kita tidak perlu kembali objek baru, sambil menjadikannya lebih sukar untuk memanggil fungsi akhir melalui menyeru. Kita boleh mengambil kesempatan daripada fakta bahawa fungsi dalam JavaScript ialah objek, yang bermaksud ia boleh mengandungi medan dan kaedahnya sendiri. Mari buat fungsi pulangan kami df dan tambah kaedah kepadanya getCalls, yang melalui penutupan akan mempunyai akses kepada panggilan seperti dahulu:


function count(f) ( biarkan panggilan = 0; function df() ( calls += 1; return f(...arguments); ) df.getCalls = function() ( return calls; ) return df; )

Lebih menyenangkan untuk bekerja dengan ini:


const csum = count((x, y) => x + y); csum(3, 7); // 10 csum(9, 6); // 15 csum.getCalls(); // 2

C keempat semuanya jelas: kami hanya akan menggantikannya FunctionExpression pada ArrowFunction. Ketiadaan pendakap kerinting akan memastikannya nota ringkas fungsi anak panah dalam kes ungkapan tunggal dalam badannya:


kiraan fungsi(f) ( biarkan panggilan = 0; fungsi df() ( panggilan += 1; kembalikan f(...argumen); ) df.getCalls = () => panggilan; kembalikan df; )

DENGAN ketiga- semuanya lebih rumit. Kami ingat bahawa perkara pertama yang kami lakukan ialah menggantikan FunctionExpression fungsi menyeru pada Perisytiharan Fungsi df. Untuk menulis semula ini kepada ArrowFunction anda perlu menyelesaikan dua masalah: tidak kehilangan akses kepada hujah (pada masa ini ini adalah susunan pseudo hujah) dan elakkan badan fungsi dua ungkapan.


Parameter yang dinyatakan secara eksplisit untuk fungsi tersebut akan membantu kami menangani masalah pertama args dengan pengendali penyebaran. Dan untuk menggabungkan dua ungkapan menjadi satu, anda boleh gunakan logik DAN. Berbeza dengan klasik pengendali logik daripada kata hubung yang mengembalikan Boolean, ia menilai operan dari kiri ke kanan sehingga "palsu" pertama dan mengembalikannya, dan jika semuanya "benar" maka nilai terakhir. Kenaikan pertama kaunter akan memberi kita 1, yang bermaksud sub-ungkapan ini akan sentiasa dikurangkan kepada benar. Kami tidak berminat untuk mengurangkan hasil panggilan fungsi dalam sub-ungkapan kedua kepada "kebenaran": penilai akan berhenti padanya dalam apa jua keadaan. Sekarang kita boleh gunakan ArrowFunction:


function count(f) ( biarkan panggilan = 0; biarkan df = (...args) => (panggilan += 1) && f(...args);df.getCalls = () => panggilan; return df; )

Anda boleh menghiasi rakaman sedikit menggunakan kenaikan awalan:


kiraan fungsi(f) ( biarkan panggilan = 0; biarkan df = (...args) => ++panggilan && f(...args); df.getCalls = () => panggilan; kembalikan df; )

Penyelesaian pertama dan mari kita mulakan dengan titik yang paling sukar dengan penggantian Pengisytiharan Fungsi pada ArrowFunction. Tetapi buat masa ini kita masih akan mempunyai badan dalam pendakap kerinting:


const count = f => ( biarkan panggilan = 0; biarkan df = (...args) => ++panggilan && f(...args); df.getCalls = () => panggilan; return df; );

Jika kita ingin menyingkirkan pendakap kerinting yang mengelilingi badan fungsi, kita perlu mengelak daripada mengisytiharkan dan memulakan pembolehubah melalui biarkan. Dan kami mempunyai dua pembolehubah: panggilan Dan df.


Pertama, mari kita berurusan dengan kaunter. Kita boleh mencipta pembolehubah tempatan dengan mentakrifkannya dalam senarai parameter fungsi, dan lulus nilai awal dengan memanggilnya menggunakan IIFE (Ungkapan Fungsi Segera Diminta):


const count = f => (panggilan => ( biarkan df = (...args) => ++panggilan && f(...args); df.getCalls = () => panggilan; return df; ))( 0);

Yang tinggal hanyalah menggabungkan tiga ungkapan menjadi satu. Oleh kerana ketiga-tiga ungkapan adalah fungsi yang sentiasa menilai kepada benar, kita juga boleh menggunakan logik DAN:


const count = f => (panggilan => (df = (...args) => ++panggilan && f(...args)) && (df.getCalls = () => panggilan) && df)(0 );

Tetapi terdapat satu lagi pilihan untuk menggabungkan ungkapan: menggunakan pengendali koma. Ia lebih baik kerana ia tidak melibatkan transformasi logik yang tidak perlu dan memerlukan lebih sedikit tanda kurungan. Operan dinilai dari kiri ke kanan, dan hasilnya ialah nilai yang terakhir:


const count = f => (panggilan => (df = (...args) => ++panggilan && f(...args), df.getCalls = () => panggilan, df))(0);

Mungkin saya berjaya menipu awak? Kami selamat menyingkirkan pengisytiharan berubah-ubah df dan hanya meninggalkan tugasan kepada fungsi anak panah kami. Dalam kes ini, pembolehubah ini akan diisytiharkan secara global, yang tidak boleh diterima! Mari kita ulangi untuk df memulakan pembolehubah tempatan dalam parameter fungsi IIFE kami, tetapi kami tidak akan melepasi sebarang nilai awal:


const count = f => ((panggilan, df) => (df = (...args) => ++panggilan && f(...args), df.getCalls = () => panggilan, df)) (0);

Dengan itu matlamat telah tercapai.

Variasi pada tema

Menariknya, kami dapat mengelak daripada mencipta dan memulakan pembolehubah setempat, berbilang ungkapan dalam blok fungsi dan mencipta objek literal. Pada masa yang sama, ketulenan penyelesaian asal dipelihara: ketiadaan pembolehubah global, privasi kaunter, akses kepada hujah fungsi yang dibungkus.


Secara umum, anda boleh mengambil sebarang pelaksanaan dan cuba melakukan sesuatu yang serupa. Sebagai contoh, polyfill untuk fungsi mengikat dalam hal ini ia agak mudah:


const bind = (f, ctx, ...a) => (...args) => f.apply(ctx, a.concat(args));

Namun, jika hujah f bukan fungsi, dengan cara yang baik kita harus membuang pengecualian. Dan pengecualian melontar tidak boleh dibuang dalam konteks sesuatu ungkapan. Anda boleh menunggu untuk ekspresi lontaran (peringkat 2) dan cuba lagi. Atau adakah sesiapa sudah mempunyai fikiran?


Atau pertimbangkan kelas yang menerangkan koordinat titik tertentu:


kelas Point ( constructor(x, y) ( this.x = x; this.y = y; ) toString() ( return `($(this.x), $(this.y))`; ) )

Yang boleh diwakili oleh fungsi:


titik const = (x, y) => (p => (p.x = x, p.y = y, p.toString = () => ["(", x, ", ", y, ")"].sertai (""), p))(Objek baharu);

Hanya di sini kita telah kehilangan warisan prototaip: menjalin adalah sifat objek prototaip titik, bukannya objek yang dibuat secara berasingan. Bolehkah perkara ini dielakkan jika kita berusaha dengan gigih?


Hasil transformasi menghasilkan campuran yang tidak sihat pengaturcaraan berfungsi dengan penggodaman penting dan beberapa ciri bahasa itu sendiri. Jika anda memikirkannya, ini boleh menjadikan penyapu yang menarik (tetapi tidak praktikal). kod sumber. Anda boleh membuat versi anda sendiri bagi masalah "penghilang kurungan" dan menghiburkan rakan sekerja dan rakan JavaScripters pada masa lapang anda. kerja yang berguna masa.

Kesimpulan

Persoalannya, untuk siapa ini berguna dan mengapa ia diperlukan? Ini benar-benar berbahaya untuk pemula, kerana ia menimbulkan tanggapan palsu tentang kerumitan dan penyelewengan bahasa yang berlebihan. Tetapi ia boleh berguna untuk pengamal, kerana ia membolehkan anda melihat ciri bahasa dari sisi lain: panggilan bukan untuk mengelak, tetapi panggilan untuk mencuba untuk mengelak pada masa hadapan.

Tag: Tambah tag

Ralat dalam program agak sukar dicari. Dan untuk memudahkan tugas anda, anda perlu menulis kod dengan betul dan cantik (boleh dibaca). Kod yang bagus mestilah: boleh dibaca, konsisten, boleh diramal, diformat dengan betul, dan ia juga mesti mempunyai dokumentasi.

Perkara penting mengenai pemformatan kod

Lekukan

Jika anda tidak memasukkan kod, ia adalah mustahil untuk dibaca. Biasanya, lekukan dibuat dengan aksara tab, tetapi ada juga yang mengesot menggunakan ruang, biasanya 3-4 ruang. Di bawah adalah contoh betul dan salah penempatan yang betul lekukan:

/* Lekukan yang betul */

< max; i++) {
jika (arr[i] % 2 === 0) (
jawapan = (
jumlah: jumlah += arr[i]
};
}
}
/* Dan di sini semuanya buruk */
var jawapan, arr = , jumlah = 0;
untuk (var i = 0, maks = arr.panjang; i< max; i++) {
jika (arr[i] % 2 === 0) (
jawapan = (
jumlah: jumlah += arr[i]
};
}
}

Pendakap gigi

Sentiasa gunakan pendakap kerinting, walaupun ia tidak diperlukan. Dari sudut pandangan teknikal, jika badan konstruk jika, sementara atau untuk terdiri daripada satu pernyataan, maka kurungan kerinting boleh diabaikan. Mempunyai pendakap kerinting sentiasa ditulis memudahkan anda membuat perubahan kemudian. Di bawah ialah contoh cara melakukannya dengan betul dan apa yang tidak:

/* Entri yang betul */
untuk (var i = 0; i< 5; i++) {
makluman(i);
}
/* Tetapi tiada satu */
untuk (var i = 0; i< 5; i++)
makluman(i);

Lokasi pendakap pembukaan

Terdapat dua jenis pengaturcara:

Jika (a > b) (
...
}
jika (a > b)
{
...
}

Cara menulis pendakap kerinting pembukaan terpulang kepada anda. Kita sudah tahu bahawa JavaScript itu sendiri memasukkan koma bertitik dan di sini masalah timbul dalam kaedah kedua. Katakan anda menulis sekeping kod ini:

Kembali
{
id: 1
};

Dan apa yang akan dilakukan oleh jurubahasa? Dia memasukkan koma bertitik selepas pulangan dan ternyata fungsi itu akan kembali tidak ditentukan . Iaitu, jurubahasa melihat sekeping kod ini seperti ini:

Pulangan tidak ditentukan;
{
id: 1
};

ruang

Ruang mesti digunakan dengan betul. Apabila anda menulis di atas kertas, adakah anda meninggalkan ruang kosong selepas koma? Saya rasa ya. Dan apabila menyenaraikan apa-apa dalam JavaScript, anda perlu meletakkan ruang selepas koma. Di manakah saya harus meletakkan ruang untuk membuat kod jelas?

1. Selepas koma bertitik dalam pernyataan for

Untuk (var i = 0; i< 100; i++) {...}

2. Apabila mengisytiharkan berbilang pembolehubah dalam pernyataan for

Untuk (var i = 0, maks = arr.length; i< max; i++) {...}

3. Selepas koma, antara elemen tatasusunan