Ini Dia Fitur-fitur Baru di ECMAScript 2016, 2017, dan 2018

Takagi Fujimaru 3 April 2018

Ini Dia Fitur-fitur Baru di ECMAScript 2016, 2017, dan 2018

Sulit untuk mengikuti hal-hal baru di beberapa versi ECMAScript dan lebih sulit lagi untuk mendapat contoh yang berguna tanpa melompat kesana kemari. Artikel ini akan membantu pembaca untuk mengenal 18 fitur yang terdapat di proposal TC39 untuk ditambahkan di ES2016, ES2017, dan ES2018 melalui contoh.

Image

1. Array.prototype.includes

includes adalah instance method di sebuah Array untuk membantu mencari apakah sebuah item ada di dalam Array tersebut (termasuk NaN seperti indexOf).

img

Orang-orang yang ada di balik pengembangan JavaScript ingin menamai fitur ini contains, tapi ternyata nama ini sudah dipakai oleh Mootools sehingga mereka memilih includes .

2. Exponentiation infix operator

Operasi matematika seperti penjumlahan dan pengurangan memiliki operator infix yaitu + dan -. Sama seperti kedua operator tersebut, operator ** juga sering dipakai untuk melakukan operasi pangkat. Di ECMAScript 2016, ** menggantikan Math.pow.

img

1. Object.values()

Object.values() adalah fungsi baru yang mirip dengan Object.keys() tapi mengembalikan semua nilai yang dimiliki oleh properti Objek kecuali nilai-nilai yang ada di prototypical chain.

img

2. Object.entries()

Object.entries() berhubungan dengan Object. keys, tapi ia tidak hanya mengembalikan keys, ia juga mengambalikan keys dan value dalam bentuk array. Method ini memudahkan kita untuk menggunakan objek di dalam loop atau mengubah object menjadi Map.

img
img

3. String Padding

Ada dua method baru yang ditambahkan ke String yaitu String.prototype.padStart dan String.prototype.padEnd yang memungkinkan operasi appending/prepending yaitu penambahan string baru ke string kosong atau string yang sudah ada.

'someString'.padLeft(numberOfCharcters [,stringForPadding]); 
'5'.padStart(10) // '          5'
'5'.padStart(10, '=*') //'=*=*=*=*=5'
'5'.padEnd(10) // '5         '
'5'.padEnd(10, '=*') //'5=*=*=*=*='

3.1 Contoh padStart:

Pada contoh di bawah, kita memiliki datar angka dengan panjang yang berbeda-beda. Kita ingin menambah angka 0 didepannya sehingga semua item memiliki panjang yang sama yaitu 10 digit agar memiliki tampilan yang rapih. Kita bisa memakai padStart(10, '0') untuk mendapatkannya.

img

3.2 Contoh padEnd

padEnd sangat berguna saat kita ingin mencetak beberapa item dengan panjang yang berbeda-beda dan ingin memberikan rata kanan.

Contoh di bawah ini memberikan gambaran bagaimana padEnd, padStart, dan `Object.entries menghasilkan pesan keluaran yang menarik.

img
const cars = { '????BMW': '10', '????Tesla': '5', '????Lamborghini': '0' } Object.entries(cars).map(([name, count]) => { //padEnd appends ' -' until the name becomes 20 characters //padStart prepends '0' until the count becomes 3 characters. console.log(`${name.padEnd(20, ' -')} Count: ${count.padStart(3, '0')}`) }); //Prints.. // ????BMW - - - - - - - Count: 010 // ????Tesla - - - - - - Count: 005 // ????Lamborghini - - - Count: 000

3.3 padStart dan padEnd dengan Emoji dan karakter double-byte

Emoji dan karakter double-byte lainnya direpresentasikan oleh multiple byte unicode. Jadi padLeft dan padRight mungkin bisa bekerja dengan tidak semestinya.

Contoh: anggap kita ingin menmepel string heart untuk memiliki 10 karakter dengan emoji ❤️. Hasilnya akan terlihat seperit pada contoh di bawah:

//Notice that instead of 5 hearts, there are only 2 hearts and 1 heart that looks odd! 'heart'.padStart(10, "❤️"); // prints.. '❤️❤️❤heart'

Hal ini karena ❤️ memiliki pajang dua bytes ('\u2764\uFE0F')! Kata heart sendiri panjangnya sudah 5 karakter, sehingga kita hanya memiliki sisa 5 karakter. Sehingga, yang terjadi adalah JavaScript akan menambahkan dua hati menggunakan '\u2764\uFE0F' yang menghasilkan ❤️❤️. Untuk karakter yang terakhir kita memanggil emoji hati satu byte \u2764 yang menghasilkan ❤.

Karena itu lah kita mendapat ❤️❤️❤heart

4. Object.getOwnPropertyDescriptors

Method ini mengembalikan semua detail (termasuk methodget dan set) untuk semua properti di sebuah objek. Motivasi utama penambahan fitur ini adalha untuk memungkinkan persalinan/duplikasi objek ke objek lain termasuk getter setter-nya, berbeda dengan Object.assign.Object.assign menyalin semua data kecuali fungsi getter dan setter dari objek sumber.

Contoh di bawha ini menampilkan perbedaan antara Object.assign dan Object.getOwnPropertyDescriptors bersama dengan Object.defineProperties untuk menyalin objek Car ke objek bru bernama ElectricCar. Pembaca bisa melihat baha dengan menggunakan Object.getOwnPropertyDescriptors, fungsi getter dan setter discount juga disalin ke objek tujuan.

Menggunakan Object.assign

img

Menggunakan Object.getOwnPropertyDescriptors

img
var Car = {
 name: 'BMW',
 price: 1000000,
 set discount(x) {
  this.d = x;
 },
 get discount() {
  return this.d;
 },
};
//Print details of Car object's 'discount' property
console.log(Object.getOwnPropertyDescriptor(Car, 'discount'));
//prints..
// { 
//   get: [Function: get],
//   set: [Function: set],
//   enumerable: true,
//   configurable: true
// }
//Copy Car's properties to ElectricCar using Object.assign
const ElectricCar = Object.assign({}, Car);
//Print details of ElectricCar object's 'discount' property console.log(Object.getOwnPropertyDescriptor(ElectricCar, 'discount')); //prints.. // { // value: undefined, // writable: true, // enumerable: true, // configurable: true // } //⚠️Notice that getters and setters are missing in ElectricCar object for 'discount' property !???????? //Copy Car's properties to ElectricCar2 using Object.defineProperties //and extract Car's properties using Object.getOwnPropertyDescriptors const ElectricCar2 = Object.defineProperties({}, Object.getOwnPropertyDescriptors(Car)); //Print details of ElectricCar2 object's 'discount' property console.log(Object.getOwnPropertyDescriptor(ElectricCar2, 'discount')); //prints.. // { get: [Function: get], ???????????????????????? // set: [Function: set], ???????????????????????? // enumerable: true, // configurable: true // } // Notice that getters and setters are present in the ElectricCar2 object for 'discount' property!

5. Penambahan trailing comma di parameter fungsi

Fitur ini merupakan update minor yang memungkinkan kita untuk menulis trailing comma setelah parameter fungsi yang terakhir. Mengapa? Hal ini untuk membantu tools seperti git blame untuk memastikan bahwa yang slaah adalah developer yang baru (bukan developer yang sebelumnya menuliskan kode yang sudah ada).

Contoh di bawah menunjukkan masalah dan solusinya.

img

6. Async/Await

Fitur ini, merupakan yang paling penting dan paling berguna. Fungsi Async memungkinkan kita untuk terbebas dari callback hell dan membuat keseluruhan kode menjadi lebih sederhana.

Keyword async memberitahu kompiler JavaScript untuk meng-handle fungsi ini dengan cara yang berbeda. Kompiler akan melakukan pause saat menemukan keyword await di dalam fungsi tersebut. Kompiler mengasumsikan bahwa ekspresi setelah await akan mengembalikan sebuah promise dan menunggu sampai promise tersbut diterima atau ditolak sebelum melanjutkan.

Pada contoh di bawah, fungsi getAmount memanggil dua method asynchronous yaitu getUser dan getBlankUser. Kita bisa melakukannya dengan promise, tapi dengan async await, kode menjadi lebih elegan dan mudah dibaca.

img

6.1 Fungsi Async mengembalikan sebuah Promise

Jika pembaca menunggu sebuah nilai dari fungsi async, maka kita harus menggunakan sintaks Promise then untuk menangkap hasilnya.

Pada contoh di bawah ini, kita ingin mencetak hasil yang ditunggu ke dalam console.log tapi tidak di dalam doubleAndAdd, sehingga kita harus menunggu dan memakai sintaks then untuk mengirim hasilnya ke console.log.

img

6.2 Calling async/await in parallel

Di contoh sebelumnya kita memanggil await dua kali, tapi kita menghabiskan waktu satu detik untuk masing-masing pemanggilan (total 2 detik). Daripada menunggu secara beruruta, kita bisa melakukannya secara parallel karena a dan b tidak saling bergantung menggunakan Promise.all.

img

6.3 Error handling async/await

Ada berbagai cara untuk meng-handle error saat menggunakan async await.

**Opsi 1: Menggunakan try catch di dalam fungsi **

img
//Option 1 - Use try catch within the function async function doubleAndAdd(a, b) { try { a = await doubleAfter1Sec(a); b = await doubleAfter1Sec(b); } catch (e) { return NaN; //return something } return a + b; } //????Usage: doubleAndAdd('one', 2).then(console.log); // NaN doubleAndAdd(1, 2).then(console.log); // 6 function doubleAfter1Sec(param) { return new Promise((resolve, reject) => { setTimeout(function() { let val = param * 2; isNaN(val) ? reject(NaN) : resolve(val); }, 1000); }); }

Opsi 2: Catch semua ekspresi await

Karena setiap ekspresi await akan mengembalikan sebuah Promise, kita bisa menangkap error seperti apda contoh:

img
//Option 2 - *Catch* errors on every await line //as each await expression is a Promise in itself async function doubleAndAdd(a, b) { a = await doubleAfter1Sec(a).catch(e => console.log('"a" is NaN')); // ???? b = await doubleAfter1Sec(b).catch(e => console.log('"b" is NaN')); // ???? if (!a || !b) { return NaN; } return a + b; } //????Usage: doubleAndAdd('one', 2).then(console.log); // NaN and logs: "a" is NaN doubleAndAdd(1, 2).then(console.log); // 6 function doubleAfter1Sec(param) { return new Promise((resolve, reject) => { setTimeout(function() { let val = param * 2; isNaN(val) ? reject(NaN) : resolve(val); }, 1000); }); }

Opsi 3: Catch keseluruhan fungsi async-await

img
//Option 3 - Dont do anything but handle outside the function
//since async / await returns a promise, we can catch the whole function's error
async function doubleAndAdd(a, b) {
 a = await doubleAfter1Sec(a);
 b = await doubleAfter1Sec(b);
 return a + b;
}
//????Usage:
doubleAndAdd('one', 2)
.then(console.log)
.catch(console.log); // ????????????<------- use "catch"
function doubleAfter1Sec(param) {
 return new Promise((resolve, reject) => {
  setTimeout(function() {
   let val = param * 2;
   isNaN(val) ? reject(NaN) : resolve(val);
  }, 1000);
 });
}
img

ECMAScript 2018 saat ini masih dalam final draft dan akan dirilis sekitar bulan Juni atau Juli 2018. Semua fitur di bawha ini termasuk ke dalam Stage-4 dan akan menjadi bagian dari ECMASCript 2018.

1. Shared memory dan atomics

Ide utama untuk membawa fitur-fitur multi-threading ke JavaScript adalah agar developer JS bisa menulis aplikasi yang concurrent dan high-performance sendiri tanpa bergantung pada engine JavaScript untuk mengatur pengelolaan memori.

Hal ini dimungkinkan oleh satu tipe baru sebuah objek global bernama SharedArrayBuffer yang menyimpan data di sebuah shared memory space. Dengan begitu, data tersebut bisa di pakai bersama oleh JS Thread maupun web-worker thread.

Sampai hari ini, jika ingin berbagi data antara thread JS dengan web-worker, kita harus menyalin datanya dan mengirim ke thread lain dengan postMessage. Kini tidak lagi!

Kita bisa memakai SharedArrayBuffer dan datanya akan tersedia baik dari thread utama maupun thread multiple web-worker.

Akan tetapi berbagi memori antara thread bisa menyebabkan race condition. Untuk menjaga agar aplikasi aman dari race condition, objek global "Atomics" di perkenalkan. Atomics memberikan beberapa jenis method untuk mengunci shared memory saat sebuah thread sedang menggunakan datanya. Ia juga memberikan method untuk mengupdate data yang ada di dalam shared memory dengan aman.

Jika pembaca tertarik, silahkan baca tiga artikel berikut:

  1. From Workers to Shared Memory —lucasfcosta
  2. A cartoon intro to SharedArrayBuffersLin Clark
  3. Shared memory and atomicsDr. Axel Rauschmayer

2. Batasan Tagged Template literal dihapus

Pertama, kita harus klarifikasi dulu apa itu "Tagged Template literal" sehingga kita bisa memahami fitur ini lebih baik.

Di ES2015+, ada sebuah fitur bernama tagged template literal yang memungkinkan developer untuk mengkostumisasi bagaimana string disisipkan. Misalnya, untuk menyisipkan string cara standarnya adalah sebagai berikut:

img

Dengan tagged literal, kita menulis sebuah fugsi yang menerima bagian string literal yang hardcoded, misaln ['Hello', '!'], lalu mengganti variabelnya, misal, ['Raja'] sebagai parameter sebuah fungsi custom (misalnya greet) dan mengembalikan nilai apapun yang diinginkan dari fungsi custom tersebut.

Contoh di bawha ini menampilkan fungsi "Tag" bernama greet yang menyisipkan sapaan seperti "Good Morning!", "Good afternoon", dan seterusnya sesuai waktu saat itu pada sebuah stirng literal dan mengembalikan sebuah string baru.

img
//A "Tag" function returns a custom string literal.
//In this example, greet calls timeGreet() to append Good //Morning/Afternoon/Evening depending on the time of the day.
function greet(hardCodedPartsArray, ...replacementPartsArray) {
 console.log(hardCodedPartsArray); //[ 'Hello ', '!' ]
 console.log(replacementPartsArray); //[ 'Raja' ]
let str = '';
 hardCodedPartsArray.forEach((string, i) => {
  if (i < replacementPartsArray.length) {
   str += `${string} ${replacementPartsArray[i] || ''}`;
  } else {
   str += `${string} ${timeGreet()}`; //<-- append Good morning/afternoon/evening here
  }
 });
 return str;
}
//????Usage:
const firstName = 'Raja';
const greetings = greet`Hello ${firstName}!`; //????????<-- Tagged literal
console.log(greetings); //'Hello  Raja! Good Morning!' ????
function timeGreet() {
 const hr = new Date().getHours();
 return hr < 12
  ? 'Good Morning!'
  : hr < 18 ? 'Good Afternoon!' : 'Good Evening!';
}

Sekarang setelah kita mendiskusikan apa itu fungsi "Tagged", banyak orang ingin memakainya diberbagai domain aplikasi, misalnya di Terminal untuk perintah dan rikues HTTP untuk membentuk URI, dsb.

⚠️ Masalah di Tagged String literal

Permasalahannya di spesifikasi ES2015 dan ES2016 tidak memperbolehkan penggunaan escape character seperti "\u" (unicode), "\x" (hexadecimal) kecuali penulisannya persi seperii \u00A9 atau \u{2F804} atau \xA9.

Jadi, jika pembaca menggunakan fungsi Tagged yang juga dipakai oleh aturan domain lain (seperti aturan aplikasi Terminal), maka bisa mengakibatkan sintaks error jika penulisannya tidak persis sama dengan apa yang diperbolehkan.

Di ES2018, aturannya menjadi lebih longgar sehingga memperbolehkan karakter escape invalid selama fungsi Tagged mengembalikan objek dengan sebuah value di properti "cooked" (karakter yang invalid adalah "undefined"), kemudian properti raw yang mengembalikan nilai yang diinginkan.

function myTagFunc(str) { return { "cooked": "undefined", "raw": str.raw[0] } } var str = myTagFunc `hi \ubla123abla`; //call myTagFunc str // { cooked: "undefined", raw: "hi \\unicode" }

3. Flag "dotall" untuk Regular Expression

Saat ini di RegEx, sebuah karakter dot ("."), seharusnya cocok untuk karakter tunggal apapun tapi tidak cocok dengan karakter barus baru seperti \n \r \f.

Contoh:

//Before
/first.second/.test('first\nsecond'); //false

Peningkatan ini membuat penggunaan operator dot bisa cocok dengan semua karakter tunggal. Untuk memastikan bahwa kita tidak ada kesalahan yang terjadi, kita harus menggunakan flag \s saat menulis RegEx agar fitur ini bisa berjalan dengan semestinya.

//ECMAScript 2018
/first.second/s.test('first\nsecond'); //true   Notice: /s ????????
img

Diterjemahkan dari Here are examples of everything new in ECMAScript 2016, 2017, and 2018 karya rajaraodv