/**
* Bab 15 New OOP features beside classes
*/
/**
* 15.1 Overview
* 15.1.1 New Object literal features
*/
// method definition
// let obj = {
// myMethod1(param1, param2) {},
// };
// // Property value shorthand
// let first = "Jane";
// let last = "Doe";
// let obj2 = { first, last }; // same as let obj2 = { first:first, last:last }
// // computed property keys
// let propKey = "foo";
// let obj3 = {
// [propKey]: true,
// ["b" + "ar"]: 123,
// };
// // computed property keys, also be used for method definitions
// let obj4 = {
// ["h" + "ello"]() {
// return "hi";
// },
// };
// console.log(obj4);
// console.log(obj4.hello());
// THE MAIN USE CASE FOR COMPUTED PROPERTY KEYS IS TO MAKE IT EASY TO USE SYMBOLS AS PROPERTY KEYS
/**
* 15.1.2 New methods in `Object`
* The most important new method of `Object` is `assign()`.
* `Object.assign()` only considers `own (non-inherited)` properties
*/
// let obj = { foo: 123 };
// Object.assign(obj, { bar: true });
// console.log(JSON.stringify(obj));
/**
* 15.2 New features of object literals
* Getters and setters
*
* get foo() dan set bar(value) adalah getter dan setter:
* get foo() artinya ketika kamu mengakses obj.foo, maka fungsi ini akan dipanggil secara otomatis (tanpa tanda ()).
* set bar(value) artinya ketika kamu menetapkan obj.bar = ..., maka fungsi ini dipanggil dengan parameter value.
*
| Akses | Fungsi yang dipanggil | Bentuk syntax | Output / Catatan |
| ---------------- | --------------------- | ------------------- | ---------------------------------------- |
| `obj.foo` | `get foo()` | Tanpa `()` | Dipanggil otomatis, return-nya digunakan |
| `obj.foo()` | ‚ùå Error (undefined) | Getter bukan fungsi | Akan error karena `foo` bukan method |
| `obj.bar = true` | `set bar(value)` | Assignment | Setter dipanggil, return-nya diabaikan |
*
* Tips Tambahan:
* Getter dan setter ini berguna saat kamu ingin:
* - Mengontrol akses baca-tulis properti
* - Menyimpan log perubahan
* - Mengemas properti privat agar lebih aman
*/
// let obj = {
// get foo() {
// console.log(`GET foo`);
// return 123;
// },
// set bar(value) {
// console.log(`Set bar to ${value}`);
// // return value is ignored
// },
// };
// Pertanyaan: kenapa tidak dipanggil dengan syntax `obj.foo()` ?
// JS akan mengenali bahwa foo adalah getter, dan otomatis menjalankan get foo().
// console.log(obj.foo); // output: `GET FOO` dan `123`, ini bukan memanggil fungsi secara eksplisit.
// Ini memanggil setter set bar(value) secara otomatis, Argumen value = true, Tapi setter tidak bisa mengembalikan nilai (return value akan diabaikan)
// Statement ini tetap mengembalikan true karena itu nilai yang sedang di-assign
// console.log((obj.bar = true)); // output: `Set bar to true` dan `true`
/**
* 15.2.3 Computed property keys
* Remember that there are two ways of specifying a key when you set a property
*
* 1. Via a fixed name: `obj.foo = true`
* 2. Via an expression `obj['b' + 'ar'] = 123`
*/
// let propKey = "foo";
// let obj = {
// [propKey]: true,
// ["b" + "ar"]: 123,
// ["he" + "llo"]() {
// return "Hi";
// },
// };
// console.log(obj.bar); // 123
// console.log(obj.hello()); // hi
/**
* The main user for computed property keys are symbols.
* You can define a `public symbol` and use it as a special propperty key that is always unique.
* One prominent example is the `symbol` stored in `Symbol.iterator`.
* If on object has a method with that key, it becomes `iterable`: The method must return an `iterator`,
* which is used by constructors such as the `for loop` to iterate over the object.
*
* ✳️ Apa Itu * dan yield?
* *[Symbol.iterator]() artinya: ini adalah generator function.
* Tanda * berarti fungsi ini bisa ditunda (lazy) dan digunakan untuk membuat iterator.
* yield artinya "kembalikan satu nilai dulu, tapi nanti bisa lanjut lagi."
*/
// let obj = {
// // (A)
// *[Symbol.iterator]() {
// yield "Hello", yield "World";
// },
// };
// for (const x of obj) {
// console.log(x);
// }
/**
* 15.3 New methods of `Object`
*/
/**
* 15.3.1 Object.assign(target, source_1, source_2, ...)
* This method merges the sources into the target: It modifies `target`, first copies all enumerable own
* properties of `source_1` into it, then all own properties of `source_2`,etc.
*
* How `Object.assign()` works.
* 1. Both kinds of property keys: `Object.assign()` supports both strings and `symbols` as property keys
* 2. Only enumerable own properties: `Object.assign()` ignores inherited properties and properties that are enumerable
* 3. Copying via assignment. Properties in the target object are created via assignment (internal operation[[Put]]).
* That means that `IF` target has `own or inerited` setters, those will be invoked during `copying`.
* An alternative would have been to `define` new properties, an operation which always creates new own peroperties
* and never invokes setters. There originally was a proposal for a variant of `Object.assign` that use definition
* instead if assignment. That proposal has been rejected for ES6, but may reconsidered for later editions.
* 4. You can't move that uses `super`; Such a method has an interbal property [[Home-Object]] that ties it to the object
* it was created in. If you move it via `Object.assign`, it will continue to refer to the super-properties of
* the original object.
*
| No | Fitur / Catatan | Penjelasan Singkat |
| -- | --------------------------------------------- | --------------------------------------------------- |
| 1 | Mendukung `string` dan `symbol` keys | ‚úÖ selama enumerable |
| 2 | Hanya properti `own` dan `enumerable` | ‚ùå Tidak menyalin inherited atau non-enumerable |
| 3 | Menyalin via `assignment`, bukan `definition` | ⚠️ Men-trigger setter kalau ada |
| 4 | Tidak bisa menyalin method yang pakai `super` | ‚ùå Karena `[[HomeObject]]` tetap menunjuk objek asal |
*/
// let obj = { foo: 123 };
// Object.assign(obj, { bar: true });
// console.log(obj);
// console.log(JSON.stringify(obj));
/**
* 15.3.1.1 Use cases for `Object.assign()`
*/
/**
* 15.3.1.1.1 Adding properties to `this`, you can use `Object.assign()` to add properties to `this` in a constructor
*/
class Point {
constuctor(x, y) {
Object.assign(this, { x, y });
}
}
/**
* 15.3.1.1.2 Providing default values for object properties.
* `Object.assign`, is also useful for filling in defaults for missing properties.
* In the following example, we have an object `DEFAULTS` with default values for properties and an object options with data.
*/
// const DEFAULTS = {
// logLevel: 0,
// outputFormat: "html",
// };
// function processContent(options) {
// // (A), we created a fresh object,
// // copied the defaults into it,
// // and then copied options into it,
// // overriding the default values
// options = Object.assign({}, DEFAULTS, options); // (A)
// console.log(options);
// }
// let newProcessContent = new processContent({ logLevel: 1 });
/**
* 15.3.1.1.3 Adding methods to objects
*/
// class SomeClass {}
// Object.assign(SomeClass.prototype, {
// someMethod(argument1, argument2) {
// console.log(`Some method ${argument1} ${argument2}`);
// },
// anotherMethod() {
// console.log("Another method");
// },
// });
// let aSomeClass = new SomeClass();
// aSomeClass.someMethod("Merindu", "Lagi");
// Bisa juga SomeClass.property.someMethod = function(argument1, argument2) { ...}
/**
* 15.3.1.1.4 Cloning objects
* One last use case for `Object.assign()` is a quick way of cloning objects.
* But, if you use this way, you will get `somewhat dirty`.
* Because it does not preserve the property attributes of orig. You must use `property descriptors`
*/
// function clone(orig) {
// //return Object.assign({}, orig); // cloning objects
// let origProto = Object.getPrototypeOf(orig);
// return Object.assign(Object.create(origProto), orig); // The clone to have the same prototype as the original
// }
/**
* 15.3.2 Object.getOwnPropertySymbols(obj)
* => retrieve all own symboled-value property keys of `obj`.
* It complements `Object.getOwnPropertyNames()` which retrieve all `string-valued` own property keys.
*
*/
/**
* 15.3.3 Object.is(value1, value2)
* The strict equals operator (===) treats two values differently that one might expect
*/
// console.log(NaN === NaN); // false
// console.log([0, NaN, 2].indexOf(NaN)); // -1 alias ga ketemu
// console.log([0, NaN, 2].findIndex(Number.isNaN)); // 1,ketemu
// Javascript has two zeros, but strict equals treats them as if they were the same value:
//console.log(-0 === +0);
// console.log(Object.is(NaN, NaN));
// console.log(Object.is(-0, +0));
/**
* 15.3.3.1 Using `Object.is()` to find Array elements
* If we combine `Object.is()` with the ES6 Array method `findIndex()`, we can find `NaN` in Arrays.
*/
// function myIndexOf(arr, elem) {
// return arr.findIndex((x) => Object.is(x, elem));
// }
// console.log(myIndexOf([0, NaN, 2], NaN));
// console.log([0, NaN, 2].indexOf(NaN));
/**
* 15.3.4 `Object.setPrototypeOf(obj, proto)`
* This method sets the prototype of `obj` to `proto`. The non standard way of doing in ES5, that is supported by many engines,
* is via assigning to the special property `__proto__`. The recommended way of setting the prototype remains the same as in ES5;
* During the creation of object, via `Object.create()`. That will always be faster than first creating an object and then setting
* its prototype. Obviously, it does not work if you want to change the prototype of an existing object.
*
* üí° Analoginya
* Object.create(proto) seperti melahirkan anak dengan warisan genetik langsung dari orang tua.
* Object.setPrototypeOf(obj, proto) seperti mengubah orang tua anak setelah dia lahir, yang biologisnya sudah telanjur beda.
*
*
| Metode | Contoh | Status | Catatan |
| ----------------------------------- | ----------------------------------- | ------------- | ------------------------------ |
| `Object.setPrototypeOf(obj, proto)` | `Object.setPrototypeOf(obj, proto)` | ‚úÖ standar ES6 | Tapi **lambat** untuk performa |
| `__proto__` | `obj.__proto__ = proto` | ⚠️ deprecated | Non-standard, hindari |
| `Object.create(proto)` | `Object.create(proto)` | ‚úÖ recommended | Paling efisien dan bersih |
*/
/**
* 15.4 Iterating over property keys in ES6
* - Object.keys(obj): Array <string>
* - Object.getOwnPropertyNames(obj): Array <string>
* - Object.getOwnPropertySymbols(obj) : Array <symbol>
* - Reflect.ownKeys(obj) : Array <string | symbol>
* - Reflect.enumerate(obj) : Iterator
*/
/**
* 15.4.1 Iteration order of property keys
* All methods that iterate over property keys do so in the same order:
*
* - First all Array indices, sorted numerically
* - Then all string keys (that are not indices), in the order in which they were created.
* - Then all symbols, in the order in which they were created
*
| Jenis Key | Contoh | Urutan |
| ------------------ | ------------ | ---------------- |
| Array Index | `'0'`, `'1'` | Numerik naik |
| String (non-index) | `'name'` | Urutan deklarasi |
| Symbol | `Symbol()` | Urutan deklarasi |
*/
console.log(
Reflect.ownKeys({
[Symbol()]: 0,
b: 0,
10: 0,
2: 0,
a: 0,
})
); // [ '2', '10', 'b', 'a', Symbol() ]
/**
*
* Why does the spec standardize in which order property keys are returned?
*
| Pertimbangan | Alasan |
| ------------------------------------------------------- | ----------------------------------------------------------------- |
| Banyak kode lama tergantung pada urutan properti object | Jadi urutan yang lazim (de facto) dijadikan standar |
| Browser harus konsisten agar web tetap bekerja | Maka dijadikan **requirement dalam spesifikasi ECMAScript** |
| Alternatif: urutan acak pada Map/Set | Terlalu mahal, susah, dan tidak bermanfaat praktis |
| Ordered structure useful (Python’s OrderedDict) | Maka dipilih model **creation-order yang stabil dan prediktabel** |
| Bagian | Fungsi | Dampak pada Iterasi |
| ------------------------- | ------------------------------------------------------------------- | ------------------------------------------- |
| **Array exotic object** | Menjelaskan **apa itu array index** (misalnya `"0"`, `"1"`) | Index diurutkan numerik dulu |
| **`[[OwnPropertyKeys]]`** | Metode internal standar ECMAScript | Menentukan **urutan properti** saat iterasi |
| Digunakan oleh | `Object.keys`, `Object.getOwnPropertyNames`, `Reflect.ownKeys`, dll | Semua iterasi key mengikuti aturan ini |
*/
const obj = {
b: 1,
100: 2,
a: 3,
2: 4,
[Symbol("x")]: 5,
};
console.log(Reflect.ownKeys(obj));
// ['2', '100', 'b', 'a', Symbol(x)]
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