/**
* Bab 14 ARROW FUNCTIONS
*
* 14.1 Overview
* There are two benefits to arrow functions.
* 1. They are less verbose than traditional function expressions
* 2. Their `this` is picked up from surroundings (lexical). Therefore, you don't need `bind()` or `that = this` anymore
*
| Kasus | `this` milik siapa? |
| ---------------------------- | ------------------------------------------------------- |
| Fungsi biasa (`function`) | Bergantung pada siapa yang memanggil |
| Arrow function (`=>`) | Milik scope tempat arrow itu ditulis (lexical scope) |
| Dalam contoh `UiComponent()` | `this` milik konteks fungsi `UiComponent`, bukan button |
| Di dalam `class` | `this` biasanya merujuk ke instance class |
| Konsep | Penjelasan |
| -------------------------- | ------------------------------------------------------------------- |
| `lexical` | Ditentukan dari lokasi penulisan, bukan runtime |
| `this` pada arrow function | Tidak punya sendiri, mewarisi dari lingkungan penulisan |
| Fungsi biasa vs arrow | Fungsi biasa: `this` bergantung pada pemanggil; Arrow: `this` tetap |
| Manfaat praktis | Tidak perlu `bind(this)` atau `let that = this` |
*/
// 14.1 Overview Proven 1
// let arr = [1, 2, 3];
// let square = arr.map((x) => x * x);
// 14.1 Overview Proven 2
// function UiComponent() {
// let button = document.getElementById("myButton");
// button.addEventListener("click", () => {
// console.log("click");
// this.handleClick(); // lexical `this`
// });
// }
/**
*
| Fitur | Arrow Function Punya Sendiri? | Lexical Binding (diwarisi)? |
| ------------ | ----------------------------- | --------------------------- |
| `this` | ‚ùå Tidak | ‚úÖ Ya |
| `arguments` | ‚ùå Tidak | ‚úÖ Ya |
| `super` | ‚ùå Tidak | ‚úÖ Ya |
| `new.target` | ‚ùå Tidak | ‚úÖ Ya |
* - Hal-hal ini menmbuat `arrow function` sangat berguna dalam konteks `class`, `callback`, dan `closure`
* karena tidak perlu mengikat ulang `this
* - Tapi justru itu pula kenapa `arrow function` tidak cocok untuk `method` atau `constructor` -- karena
* ia tidak punya `this` atatu `new.target` sendiri
*/
/**
* 1. `this` -> Lexical in Arrow Function
*/
// class Person {
// constructor(name) {
// this.name = name;
// setTimeout(() => {
// console.log(this.name); // <- lexical this, which is Person, bukan milik setTimeOut
// }, 2000);
// }
// }
// let person = new Person("Dzil");
/**
* 2. `arguments` -> Lexical in Arrow Function
*/
// function outer() {
// const arrow = () => {
// return arguments; // ini akan mengambil arguments dari `outer`, bukan dari `arrow`
// };
// return arrow("Wrong", "Scope");
// }
// console.log(outer("Dzil", "Fadly"));
/**
* 3. `super` -> lexical in class Methods
*/
// class Parent {
// greet() {
// return "Hi from Parent";
// }
// }
// class Child extends Parent {
// greet() {
// const arrow = () => super.greet(); // <- lexical super
// return arrow();
// }
// }
// class Child2 extends Parent {
// greet() {
// // solusi 1
// const self = this;
// function traditional() {
// return Parent.prototype.greet.call(this); // bisa `this`, mengarah ke instance, atau `self` langsung panggil parent
// }
// return traditional.call(this);
// // solusi 2
// // const parentGreet = super.greet; // karena `super` masih bisa dipakai di scope `method class`
// // function traditional() {
// // return parentGreet.call(this); // `this` disini harus disesuaikan
// // }
// // return traditional.call(this); // panggi dengan konteks yang benar
// }
// }
// const c = new Child();
// console.log(c.greet());
// const c2 = new Child2();
// console.log(c2.greet());
/**
* 4. new.target
* new.target mengacu pada constructor yang sedang dipakai saat `new` dipanggil
*/
// function Factory() {
// const arrow = () => {
// console.log(new.target.name); // return `Factory`
// };
// arrow();
// }
// new Factory();
/**
* 14.2 Traditional functions are bad non-method functions, due to `this`
* In JavaScript, traditional functions can be used as:
* 1. Non-method function
* 2. Methods
* 3. Constructors
*
* These roles class, due to roles 2and 3, function always have their `own` `this`.
* But that prevents you from accessing the `this` e.g, surrounding method from inside a callback (role 1)
*/
/**
* - In line C, we would like access that name that passed by an array argument
* - But can not do that because the `this` of the function from line (B) shadows the `this` of the method from line (A)
*/
// function Prefixer(prefix) {
// this.prefix = prefix;
// }
// Function (A)
// Prefixer.prototype.prefixArray = function (arr) {
// "use strict";
// // Function (B);
// return arr.map(function (x) {
// //console.log(this);
// return this.prefix + x; // (C); mencoba mengakses `hi`, this sekarang `undefined`, lihat `role1`
// });
// };
// Solution 1
// Prefixer.prototype.prefixArray = function (arr) {
// "use strict";
// var that = this; // `this` adalah instance Prefixer
// return arr.map(function (x) {
// console.log(`${that.prefix} ${x}`);
// return that.prefix + x; // (C); mencoba mengakses `hi`
// });
// };
// Solution 2, specifying a value for `this`
// Prefixer.prototype.prefixArray = function (arr) {
// return arr.map(function (x) {
// console.log(`${this.prefix} ${x}`); // (C) `${this}` akses dari second parameter `.map(callback, value)`
// }, this);
// };
// Solution 3, `bind(this)`,
// convert a function whose `this` is determined by how it is called to a function whose `this` is always the same fixed value
// Prefixer.prototype.prefixArray = function (arr) {
// "use strict";
// return arr.map(
// function (x) {
// console.log(`${this.prefix} ${x}`);
// }.bind(this)
// );
// };
// Solution 4, ES6 Arrow function
// Prefixer.prototype.prefixArray = function (arr) {
// return arr.map((x) => console.log(`${this.prefix} ${x}`));
// };
// Solution 5, ES5 turn to fully class based
// class Prefixer {
// constructor(prefix) {
// this.prefix = prefix;
// }
// prefixArray(arr) {
// return arr.map((x) => console.log(`${this.prefix} ${x}`));
// }
// }
// var prefixer = new Prefixer("hi");
// prefixer.prefixArray(["Joe", "Alex"]);
/**
* 14.3 Arrow function syntax
* `Fat arrow: =>`
*
* () => { ... } // empty parameter
* x => { ... } // one parameter, an identifier
* (x,y) => { ... } // several parameters
*/
// let squares = [1, 2, 3].map(function (x) {
// return x * x;
// });
//let squares = [1, 2, 3].map((x) => x * x); // implicit return
//console.log(squares);
/**
* 14.4 Lexical variables
*/
/**
* 14.4.1 Sources of variable values: static versus dynamic
* The following are two ways in which a variable can receive its value
*/
// First, statically (lexically)
// let x = 123; // determined by the structure of program
// function foo(y) {
// return x; // value receives statically
// }
// console.log(foo(12));
// // Second, dynamically
// function bar(arg) {
// return arg; // value receives dynamically
// }
// console.log(bar(12));
/**
* 14.4.2 Variables that are lexical in arrow functions
* The source of `this` is an important distinguishing aspect of arrow functions
*
* - Traditional functions have a `dynamic` `this`, its value is determined by how they are called
* - Arrow functions have a `lexical` `this`, its value is determined by the sorrounding scope
*
* The complete list of variables whose value are determined lexically is:
* - arguments
* - super
* - this
* - new.target
*
*
| Entity | Traditional Function (`function`) | Arrow Function (`=>`) |
| ------------ | --------------------------------- | ---------------------------- |
| `this` | Dynamic (berubah-ubah) | Lexical (mewarisi dari luar) |
| `arguments` | Local untuk masing-masing fungsi | Lexical dari scope luar |
| `super` | Tidak valid di function biasa | Lexical dari class method |
| `new.target` | Ada jika dipanggil dengan `new` | Lexical dari fungsi luar |
*/
/**
* 14.5 Syntax pitfalls
* 14.5.1 Arrow functions bind very loosely
* Salah satu sifat sintaksis arrow function yang jarang dibahas tapi sangat penting:
* yaitu bahwa arrow functions memiliki binding precedence (prioritas pengikatan operator) yang rendah.
* Penggunaan tanda kurung `()` harap diperhatikan
*/
// console.log(typeof (() => {}));
// const f = (x) => typeof x;
/**
* 14.5.2 Immediately-invoked arrow functions
* Yang sudah dipelajari adalah `Immediately-invokef function expressions (IIFEs)` untuk meng-simulasi `block-scoping` dan `value-returning blocks` di ES5
*
* `(function(){ // open IIFE
* // IIFED body inside
* }()) // close IIFE`
*
* Jika ada premis:
*
* ```
* const value = () => foo();
* const value = () => (foo()); // true interpreted
* const value = (() => foo)(); // false interpreted
*
* ```
*
*/
// (() => {
// console.log(123);
// })();
/**
* 14.5.3 Omitting the parentheses around single parameters
* => only possible if they consist of a single identifier
*/
// let variable = [1, 2, 3].map((x) => 2 * x);
// console.log(variable);
// let arr = [
// [1, 2],
// [3, 4],
// ];
// let variable2 = arr.map(([a, b]) => a + b); // [3, 7]
// let variable3 = arr.map((a, b) => a + b); // ['1,2', '3,41']
// // and you need parens if a single parameter has a default value (`undefined` triggers the default value!)
// let arr2 = [1, undefined, 3].map((x = "yes") => x);
// console.log(arr2);
/**
* 14.5.4 You can`t use statements as expression bodies
*/
/**
* 14.5.4.1 Expressions versus statements
* Expressions produces (are evaluated to) values,eg `3+4` , `foo(7)`, `'abc'.length`
* Statements do things, eg `while(true){ ... }`, `return 123`
*
| Fitur | Expression | Statement |
| ----------------------------------- | ------------------ | ------------------------------ |
| Menghasilkan nilai? | ‚úÖ Ya | ‚ùå Tidak |
| Bisa digunakan dalam ekspresi lain? | ‚úÖ Ya | ‚ùå Tidak |
| Mengontrol alur? | ‚ùå Tidak | ‚úÖ Ya |
| Bisa jadi bagian dari statement? | ‚úÖ Ya | ‚úÖ Ya |
| Contoh | `3+5`, `x = y * 2` | `if`, `for`, `return`, `break` |
*/
/**
* 14.5.4.2 The bodies of arrow functions
* If an expression is the body an arrow function, you don't need braces
* If an statement is the body an arrow function, you have to be put in braces
*/
/**
* 14.5.5 Returning an object literal
* If you wanth the expression body to be an `object literal` you have to put it in parentheses
*/
// let f1 = (x) => {bar: 123}; // block with the label bar and the expression statement 123
// let f2 = (x) => ({ bar: 123 }); // expression as `object literal`
// console.log(f1());
// console.log(f2());
/**
* 14.6 Arrow functions versus normal functions
* An arrow function is different from a normal function in only two ways
*
* 1. The following constructs are `lexical`: `arguments` , `super`, `this`, `new.target`
* 2. It can not be used as a constructor.
* There is no internal method `[[Construct]]` that allows a normal function to be invoked via `new`
* and no property `prototype`. Therefore , `new ( () => {}) ` must be throw an `Error`
*/
Dukung Saya supaya tetap menulis artikel-artikel yang baik, membayar sewa domain, dan server untuk blog ini. Caranya dengan donasi cendol via Trakteer.id.
Komentar: 0
Login untuk meninggalkan komentar