JavaScript

Jelaskan delegasi event

Delegasi event adalah teknik yang melibatkan penambahan pendengar event ke elemen induk daripada menambahkannya ke elemen turunan. Pendengar akan aktif setiap kali event dipicu pada elemen turunan karena event menggelembung ke atas DOM. Manfaat dari teknik ini adalah:

  • Jejak memori berkurang karena hanya satu handler yang diperlukan pada elemen induk, daripada harus melampirkan handler event pada setiap turunan.
  • Tidak perlu melepas ikatan handler dari elemen yang dihapus dan untuk mengikat event untuk elemen baru.

Jelaskan cara kerja `this` di JavaScript

Tidak ada penjelasan sederhana untuk this; ini adalah salah satu konsep yang paling membingungkan di JavaScript. Penjelasan singkatnya adalah nilai this tergantung pada bagaimana fungsi dipanggil. Saya telah membaca banyak penjelasan tentang this secara online, dan saya menemukan penjelasan [Arnav Aggrawal] adalah yang paling jelas. Aturan-aturan berikut diterapkan:

  1. Jika kata kunci new digunakan saat memanggil fungsi, this di dalam fungsi adalah objek baru.
  2. Jika apply, call, atau bind digunakan untuk memanggil/membuat fungsi, this di dalam fungsi adalah objek yang diteruskan sebagai argumen.
  3. Jika fungsi dipanggil sebagai metode, seperti obj.method()this adalah objek yang menjadi properti fungsi tersebut.
  4. Jika fungsi dipanggil sebagai pemanggilan fungsi bebas, yang berarti dipanggil tanpa kondisi apa pun di atas, this adalah objek global. Di browser, itu adalah objek window. Jika dalam mode ketat ('use strict'), this akan menjadi undefined alih-alih objek global.
  5. Jika beberapa aturan di atas berlaku, aturan yang lebih tinggi akan menang dan akan mengatur nilai this.
  6. Jika fungsi adalah fungsi panah ES2015, ia mengabaikan semua aturan di atas dan menerima nilai this dari cakupan sekitarnya pada saat dibuat.

Untuk penjelasan mendalam, silakan lihat [artikelnya di Medium].

Bisakah Anda memberikan contoh salah satu cara kerja this telah berubah di ES6?

ES6 memungkinkan Anda menggunakan [fungsi panah] yang menggunakan [lingkup leksikal yang melingkupi]. Ini biasanya nyaman, tetapi mencegah pemanggil mengontrol konteks melalui .call atau .apply — konsekuensinya adalah pustaka seperti jQuery tidak akan mengikat this dengan benar di fungsi penanganan event Anda. Jadi, penting untuk mengingat hal ini saat memfaktorkan ulang aplikasi lama yang besar.

Jelaskan cara kerja pewarisan prototipe

Semua objek JavaScript memiliki properti __proto__ dengan pengecualian objek yang dibuat dengan Object.create(null), yang merupakan referensi ke objek lain, yang disebut "prototipe" objek. Ketika properti diakses pada suatu objek dan jika properti tidak ditemukan pada objek tersebut, mesin JavaScript mencari __proto__ objek, dan __proto__ dari __proto__ dan seterusnya, sampai menemukan properti yang didefinisikan pada salah satu __proto__ atau sampai mencapai akhir rantai prototipe. Perilaku ini mensimulasikan pewarisan klasik, tetapi sebenarnya lebih merupakan [delegasi daripada pewarisan].

Contoh Pewarisan Prototipe

// Konstruktor objek induk. function Animal(name) { this.name = name; } // Tambahkan metode ke prototipe objek induk. Animal.prototype.makeSound = function () { console.log('The ' + this.constructor.name + ' makes a sound.'); }; // Konstruktor objek anak. function Dog(name) { Animal.call(this, name); // Panggil konstruktor induk. } // Atur prototipe objek anak menjadi prototipe induk. Object.setPrototypeOf(Dog.prototype, Animal.prototype); // Tambahkan metode ke prototipe objek anak. Dog.prototype.bark = function () { console.log('Woof!'); }; // Buat instance baru dari Dog. const bolt = new Dog('Bolt'); // Panggil metode pada objek anak. console.log(bolt.name); // "Bolt" bolt.makeSound(); // "The Dog makes a sound." bolt.bark(); // "Woof!"

Hal-hal yang perlu diperhatikan adalah:

  • .makeSound tidak didefinisikan pada Dog, jadi mesin naik rantai prototipe dan menemukan .makeSound dari Animal yang diwarisi.
  • Menggunakan Object.create untuk membangun rantai pewarisan tidak lagi direkomendasikan. Gunakan Object.setPrototypeOf sebagai gantinya.

Apa pendapat Anda tentang AMD vs CommonJS?

Keduanya adalah cara untuk mengimplementasikan sistem modul, yang tidak secara native ada di JavaScript sampai ES2015 muncul. CommonJS adalah sinkron sedangkan AMD (Asynchronous Module Definition) jelas asinkron. CommonJS dirancang dengan mempertimbangkan pengembangan sisi server sementara AMD, dengan dukungannya untuk pemuatan modul secara asinkron, lebih ditujukan untuk browser.

Saya menemukan sintaks AMD cukup verbose dan CommonJS lebih dekat ke gaya Anda akan menulis pernyataan import dalam bahasa lain. Sebagian besar waktu, saya merasa AMD tidak perlu, karena jika Anda menyajikan semua JavaScript Anda ke dalam satu file bundel yang digabungkan, Anda tidak akan mendapatkan keuntungan dari properti pemuatan asinkron. Juga, sintaks CommonJS lebih dekat ke gaya Node dalam menulis modul dan ada lebih sedikit overhead peralihan konteks saat beralih antara pengembangan JavaScript sisi klien dan sisi server.

Saya senang bahwa dengan modul ES2015, yang memiliki dukungan untuk pemuatan sinkron dan asinkron, kita akhirnya bisa tetap berpegang pada satu pendekatan. Meskipun belum sepenuhnya diluncurkan di browser dan di Node, kita selalu dapat menggunakan transpiler untuk mengubah kode kita.

Jelaskan mengapa hal berikut tidak berfungsi sebagai IIFE: `function foo(){ }();`. Apa yang perlu diubah agar berfungsi sebagai IIFE yang benar?

IIFE adalah singkatan dari Immediately Invoked Function Expressions (Ekspresi Fungsi yang Segera Dipanggil). Parser JavaScript membaca function foo(){ }(); sebagai function foo(){ } dan ();, di mana yang pertama adalah deklarasi fungsi dan yang kedua (sepasang tanda kurung) adalah upaya memanggil fungsi tetapi tidak ada nama yang ditentukan, oleh karena itu ia memunculkan Uncaught SyntaxError: Unexpected token ).

Berikut adalah dua cara untuk memperbaikinya yang melibatkan penambahan lebih banyak tanda kurung: (function foo(){ })() dan (function foo(){ }()). Pernyataan yang dimulai dengan function dianggap sebagai deklarasi fungsi; dengan membungkus fungsi ini dalam (), itu menjadi ekspresi fungsi yang kemudian dapat dieksekusi dengan () berikutnya. Fungsi-fungsi ini tidak terekspos dalam cakupan global dan Anda bahkan dapat menghilangkan namanya jika Anda tidak perlu mereferensikan dirinya sendiri dalam tubuh fungsi.

Anda juga dapat menggunakan operator void: void function foo(){ }();. Sayangnya, ada satu masalah dengan pendekatan tersebut. Evaluasi ekspresi yang diberikan selalu undefined, jadi jika fungsi IIFE Anda mengembalikan sesuatu, Anda tidak dapat menggunakannya. Contoh:

const foo = void (function bar() { return 'foo'; })(); console.log(foo); // undefined

Apa perbedaan antara variabel yang: `null`, `undefined`, atau undeclared? Bagaimana cara memeriksa salah satu status ini?

Variabel Undeclared (tidak dideklarasikan) dibuat ketika Anda menetapkan nilai ke pengenal yang sebelumnya tidak dibuat menggunakan var, let, atau const. Variabel yang tidak dideklarasikan akan didefinisikan secara global, di luar cakupan saat ini. Dalam mode ketat, ReferenceError akan dilemparkan ketika Anda mencoba menetapkan ke variabel yang tidak dideklarasikan. Variabel yang tidak dideklarasikan sama buruknya dengan variabel global. Hindari mereka dengan segala cara! Untuk memeriksanya, bungkus penggunaannya dalam blok try/catch.

function foo() { x = 1; // Melemparkan ReferenceError dalam mode ketat } foo(); console.log(x); // 1

Variabel yang undefined (tidak terdefinisi) adalah variabel yang telah dideklarasikan, tetapi tidak diberi nilai. Jenisnya adalah undefined. Jika fungsi tidak mengembalikan nilai apa pun sebagai hasil dari eksekusinya yang ditetapkan ke variabel, variabel tersebut juga memiliki nilai undefined. Untuk memeriksanya, bandingkan menggunakan operator kesetaraan ketat (===) atau typeof yang akan memberikan string 'undefined'. Perhatikan bahwa Anda tidak boleh menggunakan operator kesetaraan abstrak untuk memeriksa, karena itu juga akan mengembalikan true jika nilainya null.

var foo; console.log(foo); // undefined console.log(foo === undefined); // true console.log(typeof foo === 'undefined'); // true console.log(foo == null); // true. Salah, jangan gunakan ini untuk memeriksa! function bar() {} var baz = bar(); console.log(baz); // undefined

Variabel yang null akan secara eksplisit diberi nilai null. Ini mewakili tidak ada nilai dan berbeda dari undefined dalam arti bahwa itu telah secara eksplisit ditetapkan. Untuk memeriksa null, cukup bandingkan menggunakan operator kesetaraan ketat. Perhatikan bahwa seperti di atas, Anda tidak boleh menggunakan operator kesetaraan abstrak (==) untuk memeriksa, karena itu juga akan mengembalikan true jika nilainya undefined.

var foo = null; console.log(foo === null); // true console.log(typeof foo === 'object'); // true console.log(foo == undefined); // true. Salah, jangan gunakan ini untuk memeriksa!

Sebagai kebiasaan pribadi, saya tidak pernah meninggalkan variabel saya tidak dideklarasikan atau tidak ditetapkan. Saya akan secara eksplisit menetapkan null kepada mereka setelah mendeklarasikan jika saya belum berniat menggunakannya. Jika Anda menggunakan linter dalam alur kerja Anda, biasanya juga dapat memeriksa bahwa Anda tidak mereferensikan variabel yang tidak dideklarasikan.

Apa itu closure, dan bagaimana/mengapa Anda menggunakannya?

Closure adalah kombinasi fungsi dan lingkungan leksikal di mana fungsi tersebut dideklarasikan. Kata "leksikal" mengacu pada fakta bahwa cakupan leksikal menggunakan lokasi di mana variabel dideklarasikan dalam kode sumber untuk menentukan di mana variabel tersebut tersedia. Closure adalah fungsi yang memiliki akses ke variabel fungsi luar (penutup) — rantai cakupan bahkan setelah fungsi luar telah dikembalikan.

Mengapa Anda menggunakannya?

  • Privasi data / meniru metode privat dengan closure. Umumnya digunakan dalam [pola modul].
  • [Aplikasi parsial atau currying].

Bisakah Anda menjelaskan perbedaan utama antara loop `.forEach` dan loop `.map()` dan mengapa Anda akan memilih yang satu daripada yang lain?

Untuk memahami perbedaan antara keduanya, mari kita lihat apa yang dilakukan setiap fungsi.

forEach

  • Melintasi elemen-elemen dalam sebuah array.
  • Menjalankan callback untuk setiap elemen.
  • Tidak mengembalikan nilai.
const a = [1, 2, 3]; const doubled = a.forEach((num, index) => { // Lakukan sesuatu dengan num dan/atau index. }); // doubled = undefined

map

  • Melintasi elemen-elemen dalam sebuah array.
  • "Memetakan" setiap elemen ke elemen baru dengan memanggil fungsi pada setiap elemen, menciptakan array baru sebagai hasilnya.
const a = [1, 2, 3]; const doubled = a.map((num) => { return num * 2; }); // doubled = [2, 4, 6]

Perbedaan utama antara .forEach dan .map() adalah bahwa .map() mengembalikan array baru. Jika Anda membutuhkan hasilnya, tetapi tidak ingin mengubah array asli, .map() adalah pilihan yang jelas. Jika Anda hanya perlu mengulang array, forEach adalah pilihan yang baik.

Apa kasus penggunaan umum untuk fungsi anonim?

Mereka dapat digunakan dalam IIFE untuk mengenkapsulasi beberapa kode dalam cakupan lokal sehingga variabel yang dideklarasikan di dalamnya tidak bocor ke cakupan global.

(function () { // Beberapa kode di sini. })();

Sebagai callback yang digunakan sekali dan tidak perlu digunakan di tempat lain. Kode akan tampak lebih mandiri dan mudah dibaca ketika handler didefinisikan langsung di dalam kode yang memanggilnya, daripada harus mencari di tempat lain untuk menemukan badan fungsi.

setTimeout(function () { console.log('Halo dunia!'); }, 1000);

Argumen untuk konstruksi pemrograman fungsional atau Lodash (mirip dengan callback).

const arr = [1, 2, 3]; const double = arr.map(function (el) { return el * 2; }); console.log(double); // [2, 4, 6]

Bagaimana Anda mengatur kode Anda? (pola modul, pewarisan klasik?)

Dulu, saya menggunakan Backbone untuk model-model saya yang mendorong pendekatan OOP, membuat model Backbone dan melampirkan metode-metode padanya.

Pola modul masih bagus, tetapi saat ini, saya menggunakan React/Redux yang memanfaatkan aliran data satu arah berdasarkan arsitektur Flux. Saya akan merepresentasikan model aplikasi saya menggunakan objek biasa dan menulis fungsi murni utilitas untuk memanipulasi objek-objek ini. Keadaan dimanipulasi menggunakan tindakan dan reducer seperti di aplikasi Redux lainnya.

Saya menghindari penggunaan pewarisan klasik jika memungkinkan. Jika dan ketika saya melakukannya, saya berpegang pada [aturan-aturan ini].

Apa perbedaan antara objek host dan objek native?

Objek native adalah objek yang merupakan bagian dari bahasa JavaScript yang didefinisikan oleh spesifikasi ECMAScript, seperti String, Math, RegExp, Object, Function, dll.

Objek host disediakan oleh lingkungan runtime (browser atau Node), seperti window, XMLHTTPRequest, dll.

Perbedaan antara: `function Person(){}`, `var person = Person()`, dan `var person = new Person()`?

Pertanyaan ini cukup samar. Dugaan terbaik saya tentang niatnya adalah bahwa ini menanyakan tentang konstruktor dalam JavaScript. Secara teknis, function Person(){} hanyalah deklarasi fungsi biasa. Konvensinya adalah menggunakan PascalCase untuk fungsi yang dimaksudkan untuk digunakan sebagai konstruktor.

var person = Person() memanggil Person sebagai fungsi, dan bukan sebagai konstruktor. Memanggil seperti itu adalah kesalahan umum jika fungsi dimaksudkan untuk digunakan sebagai konstruktor. Biasanya, konstruktor tidak mengembalikan apa pun, oleh karena itu memanggil konstruktor seperti fungsi normal akan mengembalikan undefined dan itu ditetapkan ke variabel yang dimaksudkan sebagai instance.

var person = new Person() membuat instance objek Person menggunakan operator new, yang mewarisi dari Person.prototype. Alternatifnya adalah menggunakan Object.create, seperti: Object.create(Person.prototype).

function Person(name) { this.name = name; } var person = Person('John'); console.log(person); // undefined console.log(person.name); // Uncaught TypeError: Cannot read property 'name' of undefined var person = new Person('John'); console.log(person); // Person { name: "John" } console.log(person.name); // "john"

Apa perbedaan antara `.call` dan `.apply`?

Keduanya .call dan .apply digunakan untuk memanggil fungsi dan parameter pertama akan digunakan sebagai nilai this dalam fungsi. Namun, .call menerima argumen yang dipisahkan koma sebagai argumen berikutnya sementara .apply menerima array argumen sebagai argumen berikutnya. Cara mudah untuk mengingat ini adalah C untuk call dan dipisahkan koma dan A untuk apply dan array argumen.

function add(a, b) { return a + b; } console.log(add.call(null, 1, 2)); // 3 console.log(add.apply(null, [1, 2])); // 3

Jelaskan `Function.prototype.bind`.

Dikutip kata demi kata dari [MDN]:

Metode bind() membuat fungsi baru yang, ketika dipanggil, memiliki kata kunci this yang diatur ke nilai yang diberikan, dengan urutan argumen yang diberikan mendahului argumen apa pun yang diberikan saat fungsi baru dipanggil.

Menurut pengalaman saya, ini paling berguna untuk mengikat nilai this dalam metode kelas yang ingin Anda lewatkan ke fungsi lain. Ini sering dilakukan di komponen React.

Kapan Anda akan menggunakan `document.write()`?

document.write() menulis string teks ke aliran dokumen yang dibuka oleh document.open(). Ketika document.write() dieksekusi setelah halaman dimuat, itu akan memanggil document.open yang akan menghapus seluruh dokumen (<head> dan <body> dihapus!) dan mengganti konten dengan nilai parameter yang diberikan. Oleh karena itu, ini biasanya dianggap berbahaya dan rentan terhadap penyalahgunaan.

Ada beberapa jawaban online yang menjelaskan bahwa document.write() digunakan dalam kode analitik atau [ketika Anda ingin menyertakan gaya yang seharusnya hanya berfungsi jika JavaScript diaktifkan]. Bahkan digunakan dalam HTML5 boilerplate untuk [memuat skrip secara paralel dan mempertahankan urutan eksekusi]! Namun, saya menduga alasan-alasan tersebut mungkin sudah ketinggalan zaman dan di zaman modern, mereka dapat dicapai tanpa menggunakan document.write(). Tolong koreksi saya jika saya salah tentang hal ini.

Apa perbedaan antara deteksi fitur, inferensi fitur, dan penggunaan string UA?

Deteksi Fitur (Feature Detection)

Deteksi fitur melibatkan mencari tahu apakah browser mendukung blok kode tertentu, dan menjalankan kode yang berbeda tergantung apakah didukung (atau tidak), sehingga browser selalu dapat memberikan pengalaman kerja daripada crash/error di beberapa browser. Misalnya:

if ('geolocation' in navigator) { // Bisa menggunakan navigator.geolocation } else { // Tangani kekurangan fitur }

[Modernizr] adalah pustaka yang bagus untuk menangani deteksi fitur.

Inferensi Fitur (Feature Inference)

Inferensi fitur memeriksa fitur sama seperti deteksi fitur, tetapi menggunakan fungsi lain karena mengasumsikan itu juga akan ada, misalnya:

if (document.getElementsByTagName) { element = document.getElementById(id); }

Ini tidak terlalu direkomendasikan. Deteksi fitur lebih terjamin kebenarannya.

String UA (UA String)

Ini adalah string yang dilaporkan browser yang memungkinkan rekan protokol jaringan untuk mengidentifikasi jenis aplikasi, sistem operasi, vendor perangkat lunak, atau versi perangkat lunak dari agen pengguna perangkat lunak yang meminta. Ini dapat diakses melalui navigator.userAgent. Namun, string ini sulit diurai dan dapat dipalsukan. Misalnya, Chrome melaporkan dirinya sebagai Chrome dan Safari. Jadi untuk mendeteksi Safari Anda harus memeriksa string Safari dan ketiadaan string Chrome. Hindari metode ini.

Jelaskan Ajax sedetail mungkin.

Ajax (asynchronous JavaScript and XML) adalah seperangkat teknik pengembangan web yang menggunakan banyak teknologi web di sisi klien untuk membuat aplikasi web asinkron. Dengan Ajax, aplikasi web dapat mengirim dan mengambil data dari server secara asinkron (di latar belakang) tanpa mengganggu tampilan dan perilaku halaman yang ada. Dengan memisahkan lapisan pertukaran data dari lapisan presentasi, Ajax memungkinkan halaman web, dan dengan demikian aplikasi web, untuk mengubah konten secara dinamis tanpa perlu memuat ulang seluruh halaman. Dalam praktiknya, implementasi modern umumnya menggunakan JSON alih-alih XML, karena keuntungan JSON yang native dengan JavaScript.

API XMLHttpRequest sering digunakan untuk komunikasi asinkron atau saat ini, API fetch().

Apa keuntungan dan kerugian menggunakan Ajax?

Keuntungan

  • Interaktivitas yang lebih baik. Konten baru dari server dapat diubah secara dinamis tanpa perlu memuat ulang seluruh halaman.
  • Mengurangi koneksi ke server karena skrip dan stylesheet hanya perlu diminta sekali.
  • Status dapat dipertahankan di halaman. Variabel JavaScript dan status DOM akan bertahan karena halaman kontainer utama tidak dimuat ulang.
  • Pada dasarnya sebagian besar keuntungan SPA.

Kerugian

  • Halaman web dinamis lebih sulit untuk di-bookmark.
  • Tidak berfungsi jika JavaScript dinonaktifkan di browser.
  • Beberapa perayap web tidak menjalankan JavaScript dan tidak akan melihat konten yang telah dimuat oleh JavaScript.
  • Halaman web yang menggunakan Ajax untuk mengambil data kemungkinan harus menggabungkan data jarak jauh yang diambil dengan template sisi klien untuk memperbarui DOM. Agar ini terjadi, JavaScript harus diurai dan dieksekusi di browser, dan perangkat seluler kelas bawah mungkin kesulitan dengan ini.
  • Pada dasarnya sebagian besar kerugian SPA.

Jelaskan cara kerja JSONP (dan bagaimana itu sebenarnya bukan Ajax).

JSONP (JSON with Padding) adalah metode yang umum digunakan untuk melewati kebijakan lintas domain di browser web karena permintaan Ajax dari halaman saat ini ke domain lintas asal tidak diizinkan.

JSONP bekerja dengan membuat permintaan ke domain lintas asal melalui tag <script> dan biasanya dengan parameter kueri callback, misalnya: https://example.com?callback=printData. Server kemudian akan membungkus data dalam fungsi bernama printData dan mengembalikannya ke klien.

<script> function printData(data) { console.log(`Nama saya ${data.name}!`); } </script> <script src="[https://example.com?callback=printData](https://example.com?callback=printData)"></script>
printData({ name: 'Yang Shun' });

Klien harus memiliki fungsi printData dalam cakupan globalnya dan fungsi tersebut akan dieksekusi oleh klien ketika respons dari domain lintas asal diterima.

JSONP bisa tidak aman dan memiliki beberapa implikasi keamanan. Karena JSONP sebenarnya adalah JavaScript, ia dapat melakukan segala sesuatu yang dapat dilakukan JavaScript, jadi Anda perlu memercayai penyedia data JSONP.

Saat ini, [CORS] adalah pendekatan yang direkomendasikan dan JSONP dianggap sebagai hack.

Pernahkah Anda menggunakan templating JavaScript? Jika ya, pustaka apa yang telah Anda gunakan?

Ya. Handlebars, Underscore, Lodash, AngularJS, dan JSX. Saya tidak menyukai templating di AngularJS karena banyak menggunakan string dalam direktif dan kesalahan penulisan akan tidak terdeteksi. JSX adalah favorit baru saya karena lebih dekat ke JavaScript dan hampir tidak ada sintaks yang perlu dipelajari. Saat ini, Anda bahkan dapat menggunakan literal string template ES2015 sebagai cara cepat untuk membuat template tanpa bergantung pada kode pihak ketiga.

const template = `<div>Nama saya: ${name}</div>`;

Namun, waspadai potensi XSS dalam pendekatan di atas karena konten tidak diloloskan untuk Anda, tidak seperti di pustaka templating.

Jelaskan "hoisting".

Hoisting adalah istilah yang digunakan untuk menjelaskan perilaku deklarasi variabel dalam kode Anda. Variabel yang dideklarasikan atau diinisialisasi dengan kata kunci var akan memiliki deklarasinya "dipindahkan" ke atas cakupan modul/fungsi mereka, yang kami sebut sebagai hoisting. Namun, hanya deklarasinya yang di-hoist, penugasan (jika ada), akan tetap di tempatnya.

Perhatikan bahwa deklarasi sebenarnya tidak dipindahkan - mesin JavaScript mengurai deklarasi selama kompilasi dan menjadi sadar akan deklarasi dan cakupannya. Lebih mudah memahami perilaku ini dengan memvisualisasikan deklarasi sebagai di-hoist ke bagian atas cakupannya. Mari kita jelaskan dengan beberapa contoh.

console.log(foo); // undefined var foo = 1; console.log(foo); // 1

Deklarasi fungsi memiliki badan yang di-hoist sedangkan ekspresi fungsi (ditulis dalam bentuk deklarasi variabel) hanya memiliki deklarasi variabel yang di-hoist.

// Deklarasi Fungsi console.log(foo); // [Fungsi: foo] foo(); // 'FOOOOO' function foo() { console.log('FOOOOO'); } console.log(foo); // [Fungsi: foo] // Ekspresi Fungsi console.log(bar); // undefined bar(); // Uncaught TypeError: bar is not a function var bar = function () { console.log('BARRRR'); }; console.log(bar); // [Fungsi: bar]

Variabel yang dideklarasikan melalui let dan const juga di-hoist. Namun, tidak seperti var dan function, mereka tidak diinisialisasi dan mengaksesnya sebelum deklarasi akan mengakibatkan pengecualian ReferenceError. Variabel berada dalam "zona mati temporal" dari awal blok hingga deklarasi diproses.

x; // undefined y; // Reference error: y is not defined var x = 'local'; let y = 'local';

Jelaskan event bubbling.

Ketika sebuah event terpicu pada elemen DOM, ia akan mencoba menangani event tersebut jika ada listener yang terpasang, kemudian event tersebut menggelembung ke induknya dan hal yang sama terjadi. Penggelembungan ini terjadi hingga leluhur elemen sampai ke document. Event bubbling adalah mekanisme di balik delegasi event.

Apa perbedaan antara "atribut" dan "properti"?

Atribut didefinisikan pada markup HTML tetapi properti didefinisikan pada DOM. Untuk mengilustrasikan perbedaannya, bayangkan kita memiliki bidang teks ini di HTML kita: <input type="text" value="Hello">.

const input = document.querySelector('input'); console.log(input.getAttribute('value')); // Hello console.log(input.value); // Hello

Tetapi setelah Anda mengubah nilai bidang teks dengan menambahkan "World!" ke dalamnya, ini menjadi:

console.log(input.getAttribute('value')); // Hello console.log(input.value); // Hello World!

Mengapa memperluas objek JavaScript bawaan bukanlah ide yang bagus?

Memperluas objek JavaScript bawaan/native berarti menambahkan properti/fungsi ke prototype-nya. Meskipun ini mungkin terlihat seperti ide yang bagus pada awalnya, ini berbahaya dalam praktiknya. Bayangkan kode Anda menggunakan beberapa pustaka yang keduanya memperluas Array.prototype dengan menambahkan metode contains yang sama, implementasi akan saling menimpa dan kode Anda akan rusak jika perilaku kedua metode ini tidak sama.

Satu-satunya saat Anda mungkin ingin memperluas objek native adalah ketika Anda ingin membuat polyfill, pada dasarnya menyediakan implementasi Anda sendiri untuk metode yang merupakan bagian dari spesifikasi JavaScript tetapi mungkin tidak ada di browser pengguna karena browser tersebut lebih tua.

Perbedaan antara event `load` dokumen dan event `DOMContentLoaded` dokumen?

Event DOMContentLoaded terpicu ketika dokumen HTML awal telah sepenuhnya dimuat dan diurai, tanpa menunggu stylesheet, gambar, dan subframe selesai dimuat.

Event load window hanya terpicu setelah DOM dan semua sumber daya serta aset yang bergantung telah dimuat.

Apa perbedaan antara `==` dan `===`?

== adalah operator kesetaraan abstrak sedangkan === adalah operator kesetaraan ketat. Operator == akan membandingkan kesetaraan setelah melakukan konversi tipe yang diperlukan. Operator === tidak akan melakukan konversi tipe, jadi jika dua nilai bukan tipe yang sama, === akan langsung mengembalikan false. Saat menggunakan ==, hal-hal aneh bisa terjadi, seperti:

1 == '1'; // true 1 == [1]; // true 1 == true; // true 0 == ''; // true 0 == '0'; // true 0 == false; // true

Saran saya adalah jangan pernah menggunakan operator ==, kecuali untuk kenyamanan saat membandingkan dengan null atau undefined, di mana a == null akan mengembalikan true jika a adalah null atau undefined.

var a = null; console.log(a == null); // true console.log(a == undefined); // true

Jelaskan kebijakan same-origin terkait JavaScript.

Kebijakan same-origin mencegah JavaScript membuat permintaan melintasi batas domain. Sebuah asal didefinisikan sebagai kombinasi skema URI, hostname, dan nomor port. Kebijakan ini mencegah skrip berbahaya di satu halaman mendapatkan akses ke data sensitif di halaman web lain melalui Document Object Model halaman tersebut.

Buat ini berfungsi:

duplicate([1, 2, 3, 4, 5]); // [1,2,3,4,5,1,2,3,4,5]
function duplicate(arr) { return arr.concat(arr); } duplicate([1, 2, 3, 4, 5]); // [1,2,3,4,5,1,2,3,4,5]

Atau dengan ES6:

const duplicate = (arr) => [...arr, ...arr]; duplicate([1, 2, 3, 4, 5]); // [1,2,3,4,5,1,2,3,4,5]

Mengapa disebut ekspresi Ternary, apa yang ditunjukkan oleh kata "Ternary"?

"Ternary" berarti tiga, dan ekspresi ternary menerima tiga operan, kondisi uji, ekspresi "then" dan ekspresi "else". Ekspresi ternary tidak spesifik untuk JavaScript dan saya tidak yakin mengapa itu bahkan ada dalam daftar ini.

Apa itu `"use strict";`? Apa keuntungan dan kerugian menggunakannya?

'use strict' adalah pernyataan yang digunakan untuk mengaktifkan mode ketat untuk seluruh skrip atau fungsi individual. Mode ketat adalah cara untuk memilih varian JavaScript yang dibatasi.

Keuntungan:

  • Membuatnya tidak mungkin secara tidak sengaja membuat variabel global.
  • Membuat penugasan yang jika tidak akan gagal secara diam-diam untuk melempar pengecualian.
  • Membuat upaya untuk menghapus properti yang tidak dapat dihapus melempar pengecualian (yang sebelumnya upaya tersebut tidak akan berpengaruh).
  • Memerlukan nama parameter fungsi menjadi unik.
  • this tidak terdefinisi dalam konteks global.
  • Ini menangkap beberapa kesalahan pengkodean umum, melempar pengecualian.
  • Ini menonaktifkan fitur-fitur yang membingungkan atau kurang dipikirkan.

Kerugian:

  • Banyak fitur yang hilang yang mungkin biasa digunakan beberapa pengembang.
  • Tidak ada lagi akses ke function.caller dan function.arguments.
  • Penggabungan skrip yang ditulis dalam mode ketat yang berbeda mungkin menyebabkan masalah.

Secara keseluruhan, saya pikir keuntungannya lebih besar daripada kerugiannya, dan saya tidak pernah harus bergantung pada fitur-fitur yang diblokir oleh mode ketat. Saya akan merekomendasikan penggunaan mode ketat.

Buatlah perulangan for yang berulang hingga `100` sambil menghasilkan **"fizz"** pada kelipatan `3`, **"buzz"** pada kelipatan `5`, dan **"fizzbuzz"** pada kelipatan `3` dan `5`.

Lihat versi FizzBuzz oleh [Paul Irish] ini.

for (let i = 1; i <= 100; i++) { let f = i % 3 == 0, b = i % 5 == 0; console.log(f ? (b ? 'FizzBuzz' : 'Fizz') : b ? 'Buzz' : i); }

Namun, saya tidak menyarankan Anda untuk menulis yang di atas selama wawancara. Tetaplah pada pendekatan yang panjang namun jelas. Untuk versi FizzBuzz yang lebih aneh, lihat tautan referensi di bawah ini.

Mengapa, secara umum, merupakan ide yang baik untuk membiarkan cakupan global situs web apa adanya dan tidak pernah menyentuhnya?

Setiap skrip memiliki akses ke cakupan global, dan jika setiap orang menggunakan namespace global untuk mendefinisikan variabel mereka, kemungkinan besar akan terjadi tabrakan. Gunakan pola modul (IIFE) untuk mengenkapsulasi variabel Anda dalam namespace lokal.

Mengapa Anda menggunakan sesuatu seperti event `load`? Apakah event ini memiliki kekurangan? Apakah Anda tahu alternatif lain, dan mengapa Anda menggunakannya?

Event load aktif di akhir proses pemuatan dokumen. Pada titik ini, semua objek dalam dokumen berada di DOM, dan semua gambar, skrip, tautan, dan sub-frame telah selesai dimuat.

Event DOM DOMContentLoaded akan aktif setelah DOM untuk halaman telah dibangun, tetapi tidak menunggu sumber daya lain selesai dimuat. Ini lebih disukai dalam kasus tertentu ketika Anda tidak membutuhkan seluruh halaman untuk dimuat sebelum menginisialisasi.

Jelaskan apa itu aplikasi satu halaman (single page app) dan bagaimana membuatnya ramah SEO.

Di bawah ini diambil dari [Grab Front End Guide] yang luar biasa, yang secara kebetulan, ditulis oleh saya!

Pengembang web saat ini menyebut produk yang mereka bangun sebagai aplikasi web, bukan situs web. Meskipun tidak ada perbedaan ketat antara kedua istilah tersebut, aplikasi web cenderung sangat interaktif dan dinamis, memungkinkan pengguna untuk melakukan tindakan dan menerima respons terhadap tindakan mereka. Secara tradisional, browser menerima HTML dari server dan merendernya. Ketika pengguna menavigasi ke URL lain, pemuatan ulang halaman penuh diperlukan dan server mengirimkan HTML baru yang segar ke halaman baru. Ini disebut rendering sisi server.

Namun, dalam SPA modern, rendering sisi klien digunakan sebagai gantinya. Browser memuat halaman awal dari server, bersama dengan skrip (kerangka kerja, pustaka, kode aplikasi) dan stylesheet yang diperlukan untuk seluruh aplikasi. Ketika pengguna menavigasi ke halaman lain, pemuatan ulang halaman tidak dipicu. URL halaman diperbarui melalui [HTML5 History API]. Data baru yang diperlukan untuk halaman baru, biasanya dalam format JSON, diambil oleh browser melalui permintaan [AJAX] ke server. SPA kemudian secara dinamis memperbarui halaman dengan data melalui JavaScript, yang telah diunduhnya dalam pemuatan halaman awal. Model ini mirip dengan cara kerja aplikasi seluler native.

Manfaatnya:

  • Aplikasi terasa lebih responsif dan pengguna tidak melihat kedipan antara navigasi halaman karena pemuatan ulang halaman penuh.
  • Lebih sedikit permintaan HTTP yang dibuat ke server, karena aset yang sama tidak perlu diunduh lagi untuk setiap pemuatan halaman.
  • Pemisahan yang jelas antara klien dan server; Anda dapat dengan mudah membangun klien baru untuk platform yang berbeda (misalnya, seluler, chatbot, jam tangan pintar) tanpa harus memodifikasi kode server. Anda juga dapat memodifikasi tumpukan teknologi pada klien dan server secara independen, selama kontrak API tidak dilanggar.

Kekurangan:

  • Pemuatan halaman awal yang lebih berat karena pemuatan kerangka kerja, kode aplikasi, dan aset yang diperlukan untuk beberapa halaman.
  • Ada langkah tambahan yang harus dilakukan di server Anda yaitu mengkonfigurasinya untuk mengarahkan semua permintaan ke satu titik masuk dan memungkinkan perutean sisi klien mengambil alih dari sana.
  • SPA bergantung pada JavaScript untuk merender konten, tetapi tidak semua mesin pencari mengeksekusi JavaScript selama perayapan, dan mereka mungkin melihat konten kosong di halaman Anda. Ini secara tidak sengaja merugikan Optimasi Mesin Pencari (SEO) aplikasi Anda. Namun, sebagian besar waktu, ketika Anda membangun aplikasi, SEO bukanlah faktor terpenting, karena tidak semua konten perlu diindeks oleh mesin pencari. Untuk mengatasi ini, Anda dapat merender aplikasi Anda di sisi server atau menggunakan layanan seperti [Prerender] untuk "merender JavaScript Anda di browser, menyimpan HTML statis, dan mengembalikannya ke perayap".

Seberapa jauh pengalaman Anda dengan Promises dan/atau polyfill-nya?

Memiliki pengetahuan kerja tentangnya. Promise adalah objek yang dapat menghasilkan satu nilai di masa depan: baik nilai yang diselesaikan atau alasan mengapa tidak diselesaikan (misalnya, terjadi kesalahan jaringan). Promise dapat berada dalam salah satu dari 3 status yang mungkin: terpenuhi, ditolak, atau tertunda. Pengguna Promise dapat melampirkan callback untuk menangani nilai yang terpenuhi atau alasan penolakan.

Beberapa polyfill umum adalah $.deferred, Q, dan Bluebird, tetapi tidak semuanya sesuai dengan spesifikasi. ES2015 mendukung Promises di luar kotak dan polyfill biasanya tidak diperlukan saat ini.

Apa pro dan kontra menggunakan Promises daripada callbacks?

Kelebihan

  • Menghindari callback hell yang bisa tidak terbaca.
  • Memudahkan penulisan kode asinkron sekuensial yang mudah dibaca dengan .then().
  • Memudahkan penulisan kode asinkron paralel dengan Promise.all().
  • Dengan promise, skenario-skenario yang ada dalam pengkodean hanya callback ini tidak akan terjadi:
    • Memanggil callback terlalu cepat
    • Memanggil callback terlalu lambat (atau tidak pernah)
    • Memanggil callback terlalu sedikit atau terlalu banyak
    • Gagal meneruskan lingkungan/parameter yang diperlukan
    • Menelan kesalahan/pengecualian yang mungkin terjadi

Kekurangan

  • Kode sedikit lebih kompleks (dapat diperdebatkan).
  • Di browser lama tempat ES2015 tidak didukung, Anda perlu memuat polyfill untuk menggunakannya.

Apa saja keuntungan/kerugian menulis kode JavaScript dalam bahasa yang dikompilasi ke JavaScript?

Beberapa contoh bahasa yang dikompilasi ke JavaScript termasuk CoffeeScript, Elm, ClojureScript, PureScript, dan TypeScript.

Keuntungan:

  • Memperbaiki beberapa masalah lama di JavaScript dan mencegah anti-pola JavaScript.
  • Memungkinkan Anda menulis kode yang lebih pendek, dengan menyediakan gula sintaksis di atas JavaScript, yang menurut saya kurang dimiliki ES5, tetapi ES2015 luar biasa.
  • Tipe statis sangat bagus (dalam kasus TypeScript) untuk proyek-proyek besar yang perlu dipertahankan seiring waktu.

Kekurangan:

  • Memerlukan proses build/compile karena browser hanya menjalankan JavaScript dan kode Anda perlu dikompilasi menjadi JavaScript sebelum disajikan ke browser.
  • Debugging bisa menjadi masalah jika peta sumber Anda tidak memetakan dengan baik ke sumber pra-kompilasi Anda.
  • Sebagian besar pengembang tidak terbiasa dengan bahasa-bahasa ini dan perlu mempelajarinya. Ada biaya peningkatan yang terlibat untuk tim Anda jika Anda menggunakannya untuk proyek Anda.
  • Komunitas yang lebih kecil (tergantung bahasanya), yang berarti sumber daya, tutorial, pustaka, dan alat akan lebih sulit ditemukan.
  • Dukungan IDE/editor mungkin kurang.
  • Bahasa-bahasa ini akan selalu tertinggal dari standar JavaScript terbaru.
  • Pengembang harus menyadari apa yang dikompilasi kode mereka — karena itulah yang sebenarnya akan berjalan, dan itulah yang penting pada akhirnya.

Secara praktis, ES2015 telah sangat meningkatkan JavaScript dan membuatnya jauh lebih baik untuk ditulis. Saya tidak melihat kebutuhan akan CoffeeScript saat ini.

Alat dan teknik apa yang Anda gunakan untuk men-debug kode JavaScript?

  • React dan Redux
    • [React Devtools]
    • [Redux Devtools]
  • Vue
    • [Vue Devtools]
  • JavaScript
    • [Chrome Devtools]
    • Pernyataan debugger
    • Debugging console.log yang baik

Konstruksi bahasa apa yang Anda gunakan untuk mengulang properti objek dan item array?

Untuk objek:

  • Perulangan for-in - for (var property in obj) { console.log(property); }. Namun, ini juga akan mengulang properti yang diwarisinya, dan Anda akan menambahkan pemeriksaan obj.hasOwnProperty(property) sebelum menggunakannya.
  • Object.keys() - Object.keys(obj).forEach(function (property) { ... }). Object.keys() adalah metode statis yang akan mencantumkan semua properti yang dapat dihitung dari objek yang Anda lewatinya.
  • Object.getOwnPropertyNames() - Object.getOwnPropertyNames(obj).forEach(function (property) { ... }). Object.getOwnPropertyNames() adalah metode statis yang akan mencantumkan semua properti yang dapat dihitung dan tidak dapat dihitung dari objek yang Anda lewatinya.

Untuk array:

  • Perulangan for - for (var i = 0; i < arr.length; i++). Perangkap umum di sini adalah bahwa var berada dalam cakupan fungsi dan bukan cakupan blok dan sebagian besar waktu Anda menginginkan variabel iterator cakupan blok. ES2015 memperkenalkan let yang memiliki cakupan blok dan disarankan untuk menggunakan itu sebagai gantinya. Jadi ini menjadi: for (let i = 0; i < arr.length; i++).
  • forEach - arr.forEach(function (el, index) { ... }). Konstruksi ini terkadang bisa lebih nyaman karena Anda tidak perlu menggunakan index jika yang Anda butuhkan hanyalah elemen array. Ada juga metode every dan some yang akan memungkinkan Anda mengakhiri iterasi lebih awal.
  • Perulangan for-of - for (let elem of arr) { ... }. ES6 memperkenalkan perulangan baru, perulangan for-of, yang memungkinkan Anda mengulang objek yang sesuai dengan protokol iterable seperti String, Array, Map, Set, dll. Ini menggabungkan keuntungan dari perulangan for dan metode forEach(). Keuntungan dari perulangan for adalah Anda dapat memutuskannya, dan keuntungan dari forEach() adalah lebih ringkas daripada perulangan for karena Anda tidak memerlukan variabel penghitung. Dengan perulangan for-of, Anda mendapatkan kemampuan untuk keluar dari perulangan dan sintaks yang lebih ringkas.

Sebagian besar waktu, saya akan lebih memilih metode .forEach, tetapi itu sangat tergantung pada apa yang ingin Anda lakukan. Sebelum ES6, kami menggunakan perulangan for ketika kami perlu mengakhiri perulangan secara prematur menggunakan break. Tetapi sekarang dengan ES6, kita dapat melakukannya dengan perulangan for-of. Saya akan menggunakan perulangan for ketika saya membutuhkan fleksibilitas yang lebih besar, seperti menaikkan iterator lebih dari sekali per perulangan.

Juga, saat menggunakan perulangan for-of, jika Anda perlu mengakses indeks dan nilai setiap elemen array, Anda dapat melakukannya dengan metode entries() Array ES6 dan destructuring:

const arr = ['a', 'b', 'c']; for (let [index, elem] of arr.entries()) { console.log(index, ': ', elem); }

Jelaskan perbedaan antara objek mutable dan immutable.

Immutabilitas adalah prinsip inti dalam pemrograman fungsional, dan memiliki banyak hal untuk ditawarkan pada program berorientasi objek juga. Objek mutable adalah objek yang keadaannya dapat dimodifikasi setelah dibuat. Objek immutable adalah objek yang keadaannya tidak dapat dimodifikasi setelah dibuat.

Apa contoh objek immutable di JavaScript?

Di JavaScript, beberapa tipe bawaan (angka, string) adalah immutable, tetapi objek kustom umumnya mutable.

Beberapa objek JavaScript immutable bawaan adalah Math, Date.

Berikut adalah beberapa cara untuk menambahkan/mensimulasikan immutabilitas pada objek JavaScript biasa.

Properti Konstanta Objek

Dengan menggabungkan writable: false dan configurable: false, Anda pada dasarnya dapat membuat konstanta (tidak dapat diubah, didefinisikan ulang, atau dihapus) sebagai properti objek, seperti:

let myObject = {}; Object.defineProperty(myObject, 'number', { value: 42, writable: false, configurable: false, }); console.log(myObject.number); // 42 myObject.number = 43; console.log(myObject.number); // 42

Mencegah Ekstensi

Jika Anda ingin mencegah objek memiliki properti baru yang ditambahkan padanya, tetapi membiarkan properti objek lainnya tidak berubah, panggil Object.preventExtensions(...):

var myObject = { a: 2, }; Object.preventExtensions(myObject); myObject.b = 3; myObject.b; // undefined

Dalam mode non-strict, pembuatan b gagal secara diam-diam. Dalam mode strict, ia membuang TypeError.

Segel (Seal)

Object.seal() membuat objek "tersegel", yang berarti ia mengambil objek yang ada dan pada dasarnya memanggil Object.preventExtensions() padanya, tetapi juga menandai semua properti yang ada sebagai configurable: false.

Jadi, Anda tidak hanya tidak dapat menambahkan properti lagi, tetapi Anda juga tidak dapat mengkonfigurasi ulang atau menghapus properti yang ada (meskipun Anda masih dapat memodifikasi nilainya).

Bekukan (Freeze)

Object.freeze() membuat objek beku, yang berarti ia mengambil objek yang ada dan pada dasarnya memanggil Object.seal() padanya, tetapi juga menandai semua properti "data accessor" sebagai writable:false, sehingga nilainya tidak dapat diubah.

Pendekatan ini adalah tingkat imutabilitas tertinggi yang dapat Anda capai untuk objek itu sendiri, karena mencegah perubahan apa pun pada objek atau pada properti langsungnya (meskipun, seperti yang disebutkan di atas, isi objek lain yang direferensikan tidak terpengaruh).

var immutable = Object.freeze({});

Membekukan objek tidak memungkinkan properti baru ditambahkan ke objek dan mencegah penghapusan atau pengubahan properti yang ada. Object.freeze() mempertahankan enumerability, configurability, writability dan prototipe objek. Ini mengembalikan objek yang dilewatkan dan tidak membuat salinan beku.

Apa pro dan kontra dari imutabilitas?

Kelebihan

  • Deteksi perubahan lebih mudah - Kesetaraan objek dapat ditentukan secara performa dan mudah melalui kesetaraan referensial. Ini berguna untuk membandingkan perbedaan objek di React dan Redux.
  • Program dengan objek immutable lebih mudah dipikirkan, karena Anda tidak perlu khawatir tentang bagaimana objek dapat berkembang seiring waktu.
  • Salinan defensif tidak lagi diperlukan ketika objek immutable dikembalikan dari atau dilewatkan ke fungsi, karena tidak ada kemungkinan objek immutable akan dimodifikasi olehnya.
  • Berbagi mudah melalui referensi - Satu salinan objek sama baiknya dengan yang lain, sehingga Anda dapat menyimpan objek dalam cache atau menggunakan kembali objek yang sama beberapa kali.
  • Thread-safe - Objek immutable dapat digunakan dengan aman antara thread dalam lingkungan multi-threaded karena tidak ada risiko mereka dimodifikasi dalam thread lain yang berjalan secara bersamaan.
  • Menggunakan pustaka seperti ImmutableJS, objek dimodifikasi menggunakan pembagian struktural dan lebih sedikit memori yang dibutuhkan untuk memiliki banyak objek dengan struktur yang serupa.

Kekurangan

  • Implementasi naif dari struktur data immutable dan operasinya dapat mengakibatkan kinerja yang sangat buruk karena objek baru dibuat setiap saat. Disarankan untuk menggunakan pustaka untuk struktur data immutable yang efisien dan operasi yang memanfaatkan pembagian struktural.
  • Alokasi (dan dealokasi) banyak objek kecil daripada memodifikasi yang sudah ada dapat menyebabkan dampak kinerja. Kompleksitas alokator atau pengumpul sampah biasanya tergantung pada jumlah objek di heap.
  • Struktur data siklik seperti grafik sulit dibangun. Jika Anda memiliki dua objek yang tidak dapat dimodifikasi setelah inisialisasi, bagaimana Anda bisa membuatnya saling menunjuk?

Jelaskan perbedaan antara fungsi sinkron dan asinkron.

Fungsi sinkron bersifat memblokir sedangkan fungsi asinkron tidak. Dalam fungsi sinkron, pernyataan selesai sebelum pernyataan berikutnya dijalankan. Dalam kasus ini, program dievaluasi persis sesuai urutan pernyataan dan eksekusi program dijeda jika salah satu pernyataan membutuhkan waktu yang sangat lama.

Fungsi asinkron biasanya menerima callback sebagai parameter dan eksekusi berlanjut pada baris berikutnya segera setelah fungsi asinkron dipanggil. Callback hanya dipanggil ketika operasi asinkron selesai dan tumpukan panggilan kosong. Operasi berat seperti memuat data dari server web atau mengkueri database harus dilakukan secara asinkron sehingga thread utama dapat terus menjalankan operasi lain daripada memblokir hingga operasi panjang itu selesai (dalam kasus browser, UI akan membeku).

Apa itu event loop? Apa perbedaan antara call stack dan task queue?

Event loop adalah perulangan single-threaded yang memantau call stack dan memeriksa apakah ada pekerjaan yang harus dilakukan dalam task queue. Jika call stack kosong dan ada fungsi callback dalam task queue, sebuah fungsi akan dikeluarkan dari antrean dan didorong ke call stack untuk dieksekusi.

Jika Anda belum melihat ceramah Philip Robert tentang Event Loop, Anda harus melihatnya. Itu adalah salah satu video yang paling banyak ditonton tentang JavaScript.

Jelaskan perbedaan penggunaan `foo` antara `function foo() {}` dan `var foo = function() {}`

Yang pertama adalah deklarasi fungsi sedangkan yang terakhir adalah ekspresi fungsi. Perbedaan utamanya adalah bahwa deklarasi fungsi memiliki tubuhnya yang di-hoist tetapi tubuh ekspresi fungsi tidak (mereka memiliki perilaku hoisting yang sama dengan variabel). Untuk penjelasan lebih lanjut tentang hoisting, lihat pertanyaan di atas tentang hoisting. Jika Anda mencoba memanggil ekspresi fungsi sebelum didefinisikan, Anda akan mendapatkan error Uncaught TypeError: XXX is not a function.

Deklarasi Fungsi

foo(); // 'FOOOOO' function foo() { console.log('FOOOOO'); }

Ekspresi Fungsi

foo(); // Uncaught TypeError: foo is not a function var foo = function () { console.log('FOOOOO'); };

Apa perbedaan antara variabel yang dibuat menggunakan `let`, `var`, atau `const`?

Variabel yang dideklarasikan menggunakan kata kunci var memiliki cakupan pada fungsi tempat mereka dibuat, atau jika dibuat di luar fungsi apa pun, pada objek global. let dan const bersifat block scoped, artinya mereka hanya dapat diakses dalam kurung kurawal terdekat (fungsi, blok if-else, atau perulangan for).

function foo() { // Semua variabel dapat diakses dalam fungsi. var bar = 'bar'; let baz = 'baz'; const qux = 'qux'; console.log(bar); // bar console.log(baz); // baz console.log(qux); // qux } console.log(bar); // ReferenceError: bar is not defined console.log(baz); // ReferenceError: baz is not defined console.log(qux); // ReferenceError: qux is not defined
if (true) { var bar = 'bar'; let baz = 'baz'; const qux = 'qux'; } // Variabel yang dideklarasikan var dapat diakses di mana saja dalam cakupan fungsi. console.log(bar); // bar // Variabel yang didefinisikan let dan const tidak dapat diakses di luar blok tempat mereka didefinisikan. console.log(baz); // ReferenceError: baz is not defined console.log(qux); // ReferenceError: qux is not defined

var memungkinkan variabel untuk di-hoist, artinya mereka dapat direferensikan dalam kode sebelum dideklarasikan. let dan const tidak akan mengizinkan ini, melainkan akan melempar kesalahan.

console.log(foo); // undefined var foo = 'foo'; console.log(baz); // ReferenceError: can't access lexical declaration 'baz' before initialization let baz = 'baz'; console.log(bar); // ReferenceError: can't access lexical declaration 'bar' before initialization const bar = 'bar';

Mendeklarasikan ulang variabel dengan var tidak akan melempar kesalahan, tetapi let dan const akan.

var foo = 'foo'; var foo = 'bar'; console.log(foo); // "bar" let baz = 'baz'; let baz = 'qux'; // Uncaught SyntaxError: Identifier 'baz' has already been declared

let dan const berbeda karena let memungkinkan penetapan ulang nilai variabel sedangkan const tidak.

// Ini baik-baik saja. let foo = 'foo'; foo = 'bar'; // Ini menyebabkan pengecualian. const baz = 'baz'; baz = 'qux';

Apa perbedaan antara kelas ES6 dan konstruktor fungsi ES5?

Mari kita lihat contoh masing-masing terlebih dahulu:

// Konstruktor Fungsi ES5 function Person(name) { this.name = name; } // Kelas ES6 class Person { constructor(name) { this.name = name; } }

Untuk konstruktor sederhana, keduanya terlihat cukup mirip.

Perbedaan utama dalam konstruktor muncul saat menggunakan pewarisan. Jika kita ingin membuat kelas Student yang merupakan subkelas Person dan menambahkan bidang studentId, inilah yang harus kita lakukan selain yang di atas.

// Konstruktor Fungsi ES5 function Student(name, studentId) { // Panggil konstruktor superkelas untuk menginisialisasi anggota turunan superkelas. Person.call(this, name); // Inisialisasi anggota milik subkelas. this.studentId = studentId; } Student.prototype = Object.create(Person.prototype); Student.prototype.constructor = Student; // Kelas ES6 class Student extends Person { constructor(name, studentId) { super(name); this.studentId = studentId; } }

Jauh lebih verbose menggunakan pewarisan di ES5 dan versi ES6 lebih mudah dipahami dan diingat.

Bisakah Anda menawarkan kasus penggunaan untuk sintaks fungsi panah => yang baru? Bagaimana sintaks baru ini berbeda dari fungsi lain?

Salah satu manfaat nyata dari fungsi panah adalah untuk menyederhanakan sintaks yang dibutuhkan untuk membuat fungsi, tanpa perlu kata kunci function. this dalam fungsi panah juga terikat pada cakupan penutup yang berbeda dibandingkan dengan fungsi reguler di mana this ditentukan oleh objek yang memanggilnya. this yang terlingkup secara leksikal berguna saat memanggil callback terutama di komponen React.

Apa keuntungan menggunakan sintaks panah untuk sebuah metode di dalam konstruktor?

Keuntungan utama menggunakan fungsi panah sebagai metode di dalam konstruktor adalah nilai this diatur pada saat pembuatan fungsi dan tidak dapat berubah setelah itu. Jadi, ketika konstruktor digunakan untuk membuat objek baru, this akan selalu merujuk pada objek tersebut. Misalnya, mari kita katakan kita memiliki konstruktor Person yang menerima nama depan sebagai argumen memiliki dua metode untuk console.log nama itu, satu sebagai fungsi reguler dan satu sebagai fungsi panah:

const Person = function (firstName) { this.firstName = firstName; this.sayName1 = function () { console.log(this.firstName); }; this.sayName2 = () => { console.log(this.firstName); }; }; const john = new Person('John'); const dave = new Person('Dave'); john.sayName1(); // John john.sayName2(); // John // Nilai 'this' fungsi reguler dapat diubah, tetapi fungsi panah tidak bisa john.sayName1.call(dave); // Dave (karena "this" sekarang adalah objek dave) john.sayName2.call(dave); // John john.sayName1.apply(dave); // Dave (karena 'this' sekarang adalah objek dave) john.sayName2.apply(dave); // John john.sayName1.bind(dave)(); // Dave (karena 'this' sekarang adalah objek dave) john.sayName2.bind(dave)(); // John var sayNameFromWindow1 = john.sayName1; sayNameFromWindow1(); // undefined (karena 'this' sekarang adalah objek window) var sayNameFromWindow2 = john.sayName2; sayNameFromWindow2(); // John

Inti utamanya di sini adalah bahwa this dapat diubah untuk fungsi normal, tetapi konteksnya selalu tetap sama untuk fungsi panah. Jadi, bahkan jika Anda meneruskan fungsi panah Anda ke bagian-bagian aplikasi Anda yang berbeda, Anda tidak perlu khawatir tentang perubahan konteks.

Ini bisa sangat membantu dalam komponen kelas React. Jika Anda mendefinisikan metode kelas untuk sesuatu seperti event handler klik menggunakan fungsi normal, dan kemudian Anda meneruskan event handler klik itu ke komponen anak sebagai prop, Anda juga perlu mengikat this di konstruktor komponen induk. Jika Anda malah menggunakan fungsi panah, tidak perlu mengikat "this" juga, karena metode tersebut akan secara otomatis mendapatkan nilai "this" dari konteks leksikal yang melingkupinya.

Apa definisi fungsi tingkat tinggi (higher-order function)?

Fungsi tingkat tinggi adalah setiap fungsi yang menerima satu atau lebih fungsi sebagai argumen, yang digunakannya untuk beroperasi pada beberapa data, dan/atau mengembalikan fungsi sebagai hasilnya. Fungsi tingkat tinggi dimaksudkan untuk mengabstraksi beberapa operasi yang dilakukan berulang kali. Contoh klasik dari ini adalah map, yang mengambil sebuah array dan sebuah fungsi sebagai argumen. map kemudian menggunakan fungsi ini untuk mengubah setiap item dalam array, mengembalikan array baru dengan data yang diubah. Contoh populer lainnya di JavaScript adalah forEach, filter, dan reduce. Fungsi tingkat tinggi tidak hanya perlu memanipulasi array karena ada banyak kasus penggunaan untuk mengembalikan fungsi dari fungsi lain. Function.prototype.bind adalah salah satu contohnya di JavaScript.

Map

Misalnya kita memiliki array nama yang perlu kita ubah setiap string menjadi huruf besar.

const names = ['irish', 'daisy', 'anna'];

Cara imperatif akan seperti ini:

const transformNamesToUppercase = function (names) { const results = []; for (let i = 0; i < names.length; i++) { results.push(names[i].toUpperCase()); } return results; }; transformNamesToUppercase(names); // ['IRISH', 'DAISY', 'ANNA']

Menggunakan .map(transformerFn) membuat kode lebih pendek dan lebih deklaratif.

const transformNamesToUppercase = function (names) { return names.map((name) => name.toUpperCase()); }; transformNamesToUppercase(names); // ['IRISH', 'DAISY', 'ANNA']

Bisakah Anda memberikan contoh destructuring objek atau array?

Destructuring adalah ekspresi yang tersedia di ES6 yang memungkinkan cara yang ringkas dan nyaman untuk mengekstrak nilai Objek atau Array dan menempatkannya ke dalam variabel yang berbeda.

Destructuring array

// Penugasan variabel. const foo = ['one', 'two', 'three']; const [one, two, three] = foo; console.log(one); // "one" console.log(two); // "two" console.log(three); // "three"
// Menukar variabel let a = 1; let b = 3; [a, b] = [b, a]; console.log(a); // 3 console.log(b); // 1

Destructuring objek

// Penugasan variabel. const o = { p: 42, q: true }; const { p, q } = o; console.log(p); // 42 console.log(q); // true

Literal Template ES6 menawarkan banyak fleksibilitas dalam menghasilkan string, bisakah Anda memberikan contoh?

Literal template membantu menyederhanakan interpolasi string, atau menyertakan variabel dalam string. Sebelum ES2015, lazim melakukan hal seperti ini:

var person = { name: 'Tyler', age: 28 }; console.log( 'Hi, nama saya ' + person.name + ' dan saya berusia ' + person.age + ' tahun!', ); // 'Hi, nama saya Tyler dan saya berusia 28 tahun!'

Dengan literal template, Anda sekarang dapat membuat output yang sama seperti ini:

const person = { name: 'Tyler', age: 28 }; console.log(`Hi, nama saya ${person.name} dan saya berusia ${person.age} tahun!`); // 'Hi, nama saya Tyler dan saya berusia 28 tahun!'

Perhatikan bahwa Anda menggunakan backticks, bukan kutipan, untuk menunjukkan bahwa Anda menggunakan literal template dan bahwa Anda dapat menyisipkan ekspresi di dalam placeholder ${}.

Kasus penggunaan kedua yang membantu adalah dalam membuat string multi-baris. Sebelum ES2015, Anda dapat membuat string multi-baris seperti ini:

console.log('Ini baris satu.\nIni baris dua.'); // Ini baris satu. // Ini baris dua.

Atau jika Anda ingin memisahkannya menjadi beberapa baris dalam kode Anda sehingga Anda tidak perlu menggulir ke kanan di editor teks Anda untuk membaca string yang panjang, Anda juga dapat menuliskannya seperti ini:

console.log('Ini baris satu.\n' + 'Ini baris dua.'); // Ini baris satu. // Ini baris dua.

Namun, literal template mempertahankan spasi apa pun yang Anda tambahkan padanya. Misalnya, untuk membuat output multi-baris yang sama yang kita buat di atas, Anda cukup melakukan:

console.log(`Ini baris satu. Ini baris dua.`); // Ini baris satu. // Ini baris dua.

Kasus penggunaan lain dari literal template adalah sebagai pengganti pustaka templating untuk interpolasi variabel sederhana:

const person = { name: 'Tyler', age: 28 }; document.body.innerHTML = ` <div> <p>Nama: ${person.name}</p> <p>Usia: ${person.age}</p> </div> `;

Perhatikan bahwa kode Anda mungkin rentan terhadap XSS dengan menggunakan .innerHTML. Bersihkan data Anda sebelum menampilkannya jika berasal dari pengguna!

Bisakah Anda memberikan contoh fungsi currying dan mengapa sintaks ini menawarkan keuntungan?

Currying adalah pola di mana fungsi dengan lebih dari satu parameter dipecah menjadi beberapa fungsi yang, ketika dipanggil secara berurutan, akan mengumpulkan semua parameter yang dibutuhkan satu per satu. Teknik ini dapat berguna untuk membuat kode yang ditulis dalam gaya fungsional lebih mudah dibaca dan disusun. Penting untuk dicatat bahwa agar sebuah fungsi dapat di-curried, fungsi tersebut perlu dimulai sebagai satu fungsi, kemudian dipecah menjadi urutan fungsi yang masing-masing menerima satu parameter.

function curry(fn) { if (fn.length === 0) { return fn; } function _curried(depth, args) { return function (newArgument) { if (depth - 1 === 0) { return fn(...args, newArgument); } return _curried(depth - 1, [...args, newArgument]); }; } return _curried(fn.length, []); } function add(a, b) { return a + b; } var curriedAdd = curry(add); var addFive = curriedAdd(5); var result = [0, 1, 2, 3, 4, 5].map(addFive); // [5, 6, 7, 8, 9, 10]

Apa manfaat menggunakan sintaks spread dan bagaimana perbedaannya dengan sintaks rest?

Sintaks spread ES6 sangat berguna saat mengkodekan dalam paradigma fungsional karena kita dapat dengan mudah membuat salinan array atau objek tanpa menggunakan Object.create, slice, atau fungsi pustaka. Fitur bahasa ini sering digunakan dalam proyek Redux dan RxJS.

function putDookieInAnyArray(arr) { return [...arr, 'dookie']; } const result = putDookieInAnyArray(['Saya', 'benar-benar', "tidak", 'suka']); // ["Saya", "benar-benar", "tidak", "suka", "dookie"] const person = { name: 'Todd', age: 29, }; const copyOfTodd = { ...person };

Sintaks rest ES6 menawarkan singkatan untuk menyertakan sejumlah argumen arbitrer untuk diteruskan ke fungsi. Ini seperti kebalikan dari sintaks spread, mengambil data dan memasukkannya ke dalam array daripada membongkar array data, dan ia berfungsi dalam argumen fungsi, serta dalam tugas destructuring array dan objek.

function addFiveToABunchOfNumbers(...numbers) { return numbers.map((x) => x + 5); } const result = addFiveToABunchOfNumbers(4, 5, 6, 7, 8, 9, 10); // [9, 10, 11, 12, 13, 14, 15] const [a, b, ...rest] = [1, 2, 3, 4]; // a: 1, b: 2, rest: [3, 4] const { e, f, ...others } = { e: 1, f: 2, g: 3, h: 4, }; // e: 1, f: 2, others: { g: 3, h: 4 }

Bagaimana Anda bisa berbagi kode antar file?

Ini tergantung pada lingkungan JavaScript.

Di sisi klien (lingkungan browser), selama variabel/fungsi dideklarasikan dalam cakupan global (window), semua skrip dapat mereferensikannya. Alternatifnya, adopsi Asynchronous Module Definition (AMD) melalui RequireJS untuk pendekatan yang lebih modular.

Di sisi server (Node.js), cara umum adalah menggunakan CommonJS. Setiap file diperlakukan sebagai modul dan dapat mengekspor variabel dan fungsi dengan melampirkannya ke objek module.exports.

ES2015 mendefinisikan sintaks modul yang bertujuan untuk menggantikan AMD dan CommonJS. Ini pada akhirnya akan didukung di lingkungan browser dan Node.

Mengapa Anda ingin membuat anggota kelas statis?

Anggota kelas statis (properti/metode) tidak terikat pada instance spesifik dari suatu kelas dan memiliki nilai yang sama terlepas dari instance mana yang mereferensikannya. Properti statis biasanya variabel konfigurasi dan metode statis biasanya fungsi utilitas murni yang tidak bergantung pada keadaan instance.