モダンJavaScript: 今日から使うべき10のこと
Twitterでフォローしてね。トピックや改善点の提案をお待ちしています。/Chris
あなたはJavaScriptが完全に新しいかもしれませんし、年に数回使う程度かもしれません。しかし、明らかなのは—色々変わったってことです。そして、あなたがJavaScriptに真剣なら、絶対に使うべき機能があります。この記事では、毎日使うべきだと思う機能を説明します。
こちらがビデオバージョンです:
リソース
以下は、ES6以降のお気に入りリソースです:
1. スプレッドオペレータ
これはオブジェクトまたは配列の前に ...
を置くことで示され、名前が示している通り、構造物をコンマ区切りのリストに変換することを意味します。以下で説明します:
配列のスプレッド
let firstHalf = ['one', 'two'];
let secondHalf = ['three', 'four', ...firstHalf];
これはとてもすっきりとした書き方です。これをスプレッドオペレータなしで書くとこんな感じです:
配列のスプレッドなし
let firstHalf = ['one', 'two'];
let secondHalf = ['three', 'four'];
for(var i = 0; i < firstHalf.length; i++) {
secondHalf.push(firstHalf[i]);
}
オブジェクトにも使えて、プロパティを マージ する手段となります:
オブジェクトのスプレッド
const hero = {
name: 'Xena - Warrior Princess',
realName: 'Lucy Lawless'
}
const heroWithSword = {
...hero,
weapon: 'sword'
}
オブジェクトの全プロパティをループするのが大変な場合は次のようになります:
オブジェクトのスプレッドなし
let keys = Object.keys(hero);
let obj = {};
for(var i = 0; i < keys.length; i++) {
obj[keys[i]] = hero[keys[i]];
}
ただし、Object.assign()
も使えることを付け加えておくと、こんな風に見えます:
const heroWithSword = Object.assign({}, hero, {weapon: "sword"})
それでも、以下のように さらに 簡単に読めるという点で議論の余地があります:
const heroWithSword = {
...hero,
weapon: 'sword'
}
2. レストパラメータ
レストパラメータとは、残りのパラメータを配列に 集約 することに関するものです。JavaScriptは与えられた入力パラメータの数に対して柔軟に振る舞う能力を持っています。通常はarguments
変数がこれらを集めます。以下をご覧ください:
function add(first, second, ...remaining) {
return first + second;
}
上記の場合、first
とsecond
のパラメータだけを合計しています。これはadd(1,2)
で呼び出すのも、add(1,2,3,4)
で呼び出すのも同じ結果になります。これを修正するには以下のように書きます:
function add(first, second, ...remaining) {
return first + second + remaining.reduce((acc, curr) => acc + curr, 0);
}
上記は問題を修正し、すべての入力パラメータを使用しています。
3. 文字列の挿入
こんな文を見たことがありますか?
class Product {
constructor(name, description, price) {
this.name = name;
this.description = description;
this.price = price;
}
getDescription() {
return "Full description \n" +
"name: " + this.name +
"description: " + this.description
}
}
もちろんgetDescription()
メソッドのことですが、これは長くて読みにくい式です。ほとんどのプログラミング言語の現実です。ありがたいことに、JavaScriptにも文字列の挿入があります。以下のようにgetDescription()
メソッドを書き直せます:
getDescription() {
return `Full description \n:
name: ${this.name}
description ${this.description}
`;
}
ダブルバッククォート `
を使って多行文字列を定義します。また${}
を使用して挿入します。これであなたの世界がかなり良くなったはずです :)
4. 省略プロパティ記法
これはあなたがすでに使っているものかもしれません。ES5では次のように書く必要がありました:
function createCoord(x, y) {
return {
x: x,
y: y
}
}
ES6以降では、:
の右側が同じ名前の場合、それを省略できるようになります:
function createCoord(x, y) {
return {
x,
y
}
}
すっきり見えますよね?
5. メソッドプロパティ
これはオブジェクト内でメソッドを指すプロパティを定義する方法です。以下のES5の例を考えてみましょう:
const math = {
add: function(a, b) { return a + b; },
sub: function(a, b) { return a - b; },
multiply: function(a, b) { return a * b; }
}
ES6以降では、add:
という全体が不要です。次のように単純にタイプできます:
const math = {
add(a, b) { return a + b; },
sub(a, b) { return a - b; },
multiply(a, b) { return a * b; }
}
6. 分割代入
分割代入は、あなたが開発者として持つ精神衛生上の問題に対処するものです。
オブジェクトの分割代入
以下のコードを考えてみましょう:
function handle(req, res) {
const name = req.body.name;
const description = req.body.description;
const url = req.url;
log('url endpoint', url);
// たくさんのロジック
dbService.createPerson(name, description)
}
上記のコードは完璧ではありませんが、異なるレベルでオブジェクトからデータを取り出したいケースを表しています。問題は何かと言うと、これらの変数を宣言することなく、少し手を抜いてもいいのではないかということです。以下のようにできます:
function handle(req, res) {
const { body: { name, description }, url } = req;
log('url endpoint', url);
// たくさんのロジック
dbService.createPerson(name, description)
}
上記では、3行が1行になりました。
配列の分割代入
これはオブジェクトに限定されるものではありません。配列にも適用可能です。以下のコードを考えてみましょう:
const array = [1, 2, 3, 4, 5, 6];
const a = array[0];
const c = array[2];
これは、もっとエレガントに以下のようにできます:
const array = [1, 2, 3, 4, 5, 6];
const [a, , c, ...remaining] = array;
// remaining = [4, 5, 6]
上記のように配列の値をパターンマッチングで分割できます。何かをスキップしたい場合は, ,
とタイプし、REST文を追加して残りのアイテムを取得するというボーナスを投入しました。
パラメータのマッチング
これを関数とそのパラメータにも適用できます。関数のパラメータが2-3個以上ある場合、すべてのパラメータをオブジェクトとしてまとめることが事実上の標準になり、次のような関数が得られます:
function doSomething(config) {
if(config.a) { /*...*/ }
if(config.b) { /*...*/ }
if(config.c) { /*...*/ }
}
これをより良い方法で行うには:
function doSomething({ a, b, c }) {
if(a) { /*...*/ }
if(b) { /*...*/ }
if(c) { /*...*/ }
}
7. 配列メソッド
ES6には非常に便利な配列メソッドがたくさん搭載されました:
find()
、リストのアイテムを探す、なければnullfindIndex()
、アイテムのインデックスを探すsome()
、リストの少なくとも1つのアイテムで述語がtrueかincludes()
、アイテムがリストに含まれているか
以下のコードで使用方法を確認しましょう:
const array = [{ id: 1, checked: true }, { id: 2 }];
array.find(item => item.id === 2) // { id: 2 }
array.findIndex(item => item.id === 2) // 1
array.some(item => item.checked) // true
const numberArray = [1, 2, 3, 4];
numberArray.includes(2) // true
8. プロミスとAsync/Await
昔を思い出せば、コールバックだけが私たちが持っていたものだとわかります。こんな感じです:
function doSomething(cb) {
setTimeout(() => {
cb('done')
}, 3000)
}
doSomething((arg) => {
console.log('done here', arg);
})
私たちは一部の操作が非同期であり、完了するのに時間がかかるという事実に対処するためこれを使用していました。その後、人々が使用し始め、最終的に言語にネイティブサポートが提供されたプロミスライブラリを手に入れました。ですので、今では以下のようにすることができます:
function doSomething() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('done')
}, 3000)
})
}
doSomething().then(arg => {
console.log('done here', arg);
})
この全体の経験をチェーンできるので、次のような呼び出しを行うことができます:
getUser()
.then(getOrderByUser)
.then(getOrderItemsByOrder)
.then(orderItems => {
// order itemsで何かする
})
Async/Await
その後、私たちはasync/awaitを手に入れ、人生はさらに栄光になりました。プロミスで示された上記の例がこれになります:
async function getItems() {
try {
const user = await getUser();
const order = await getOrderByUser(user);
const items = await getOrderItemsByOrder(order);
return items;
} catch(err) {
// ここでエラーを処理することを提案する、何かを返すか例外を再スローする
}
}
getItems().then(items => {
// order itemsで何かする
})
非同期コードが同期的に見えるのです。:)
9. モジュール
ほぼどんなコーディング言語もモジュールの概念をサポートしています。コードを多くの異なるファイルに分割できる能力、それらのファイルも自己完結型ユニットであるモジュールです。以下のコードを考えてみてください:
// math.js
export function add(a, b) { return a + b; }
export function sub(a, b) { return a - b; }
export default (a, b) => a * b;
// main.js
import mult, { add, sub } from './math';
mult(2, 4) // 8
add(1, 1) // 2
sub(1, 2) // -1
上記では、これらの構造物add
とsub
がこのモジュールをインポートする任意のモジュールに公開されることを示すためにexport
キーワードを使用しています。単にインポートすると取得されるものはexport default
キーワードです。main.js
では、デフォルトをmult
として取り込み、特にadd()
とsub()
メソッドを選び出しています。
this
10. アロー関数+レキシカルこの記事の至る所でアロー関数を使用していましたが、単に別の関数表記です。昔は、関数を次のようにしか書くことができませんでした:
function printArray(arr) {
// 何かする
}
今、次のように定義できます:
const printArray = (arr) => {
// 何かする
}
ワンライン関数
関数をワンライナーとしても定義できます:
const add = (a, b) => a + b
これは自動的に操作を行い、結果を返します。同じことをしてオブジェクトを返すと、以下のようになります:
const create = (a, b) => ({ x: a, y: b })
レキシカルなthis
問題はthis
が何を指しているかわからない状況でした。以下の問題を考えてください:
let array = [1, 2, 3];
function sum() {
this.total = 0;
array.forEach(function(item) {
this.total += item; // `this`は内部関数の`this`です。ダメです。
})
return total;
}
上記の場合、forEach
内のthis
が間違って指しています。これを解決するために、次のようにしていました:
function<br><br>こちらの記事はdev.toの良い記事を日本人向けに翻訳しています。<br>[https://dev.to/azure/modern-javascript-10-things-you-should-be-using-starting-today-1adm](https://dev.to/azure/modern-javascript-10-things-you-should-be-using-starting-today-1adm)