- この記事はMDCアドベントカレンダー3日目の記事です
- ちなみに1日目は[PWA/A2HS]Webアプリをインストールしてホーム画面へ追加できるようにするを書きました
概要
- JavaScriptは言語仕様であるEcmaScriptのバージョンによって仕様が定義されています
- EcmaScriptのバージョン6(ES6/ES2015)で大きなアップデートが入って以降は様々な記法が追加され、ES5の時代しか知らない人からすると奇妙な書き方も増えてきました
- ES6からは毎年VUPされることとなりES2015のように年をバージョンとして扱うようになった
- 今回はJavaScriptのコードを異なるEcmaScriptのバージョンにトランスパイルできるBabelを使って奇妙な記法をES5に変換し、従来の書き方でいうとどう書くか見ることで動作を理解したいと思います
アロー関数
- アロー関数は比較的わかりやすい構文ですが、初見では
=>
があったり一行だと省略できるものが多くて戸惑うこともあるかもしれません
Babel実行前後の比較
- 大雑把にいうと
=>
とfunction
が置き換わっただけですね(厳密にはそれだけではないが) - Babelの実行結果
ES2015
const add = (a, b) => a + b;
const result = add(1, 2);
console.log(result) // 3
ES5
var add = function add(a, b) {
return a + b;
};
var result = add(1, 2);
console.log(result); // 3
プロパティ名の省略
- 省略記法なので初見だと何をしているのか一瞬迷いますね
- 一度覚えてしまえば簡単だしコードがすっきり見えていいですね
Babel実行前後の比較
- オブジェクトのkey名とvalueに設定する変数名が同様の場合省略できるというものです
- Babelの実行結果
ES20XX
const name = 'ozaki25';
const age = 30;
const user = {
name,
age,
};
console.log(user); // { name: 'ozaki25', age: 30 }
ES5
var name = "ozaki25";
var age = 30;
var user = {
name: name,
age: age
};
console.log(user); // { name: 'ozaki25', age: 30 }
分割代入
- 分割代入はよく見ればなんとなく想像つきそうですが、左辺の書き方が見慣れないかもしれません。
Babel実行前後の比較
-
const { name } = user;
がvar name = user.name;
の、const [dog, cat] = animals;
がvar dog = animals[0], cat = animals[1];
のショートカットになってる感じですね
Object
ES20XX
// Object
const user = {
name: 'ozaki25',
age: 30,
};
const { name } = user;
console.log(name); // ozaki25
ES5
// Object
var user = {
name: "ozaki25",
age: 30
};
var name = user.name;
console.log(name); // ozaki25
Array
ES20XX
// Array
const animals = ['犬', '猫']
const [dog, cat] = animals;
console.log(dog); // 犬
console.log(cat); // 猫
ES5
// Array
var animals = ["犬", "猫"];
var dog = animals[0],
cat = animals[1];
console.log(dog); // 犬
console.log(cat); // 猫
スプレッド構文
- 初めて見る人は
...
でもう意味不明ですね
Babel実行前後の比較
- Objectの方は長くてドン引きですね。実行結果から想像つくと思いますがObjectをコピーして新しいObjectに埋め込んでいます。これを
...
で表現できちゃうので便利な構文ですね(Babelの結果を見て理解を深めるという趣旨が・・・)- 同じkey名の値が複数回登場したら後勝ちになります
- Arrayの方は短くてわかりやすいですね。
[...xx]
はconcatにそのまま置き換えられます。...
で配列のコピーを展開して新しい配列に埋め込んでいます- concatでも記述量は大して変わりませんが視覚的に新しい配列をイメージできるのがいいですね
Object
ES20XX
// Object
const user = { name: 'ozaki25', age: 30 };
const newUser = { job: 'Webエンジニア', ...user };
console.log(newUser); // { job: 'Webエンジニア', name: 'ozaki25', age: 30 }
ES5
function ownKeys(object, enumerableOnly) {
var keys = Object.keys(object);
if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymbols(object);
if (enumerableOnly)
symbols = symbols.filter(function (sym) {
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
});
keys.push.apply(keys, symbols);
}
return keys;
}
function _objectSpread(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i] != null ? arguments[i] : {};
if (i % 2) {
ownKeys(Object(source), true).forEach(function (key) {
_defineProperty(target, key, source[key]);
});
} else if (Object.getOwnPropertyDescriptors) {
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
} else {
ownKeys(Object(source)).forEach(function (key) {
Object.defineProperty(
target,
key,
Object.getOwnPropertyDescriptor(source, key)
);
});
}
}
return target;
}
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
// Object
var user = {
name: "ozaki25",
age: 30
};
var newUser = _objectSpread(
{
job: "Webエンジニア"
},
user
);
Array
ES20XX
// Array
const animals = ['犬', '猫']
const newAnimals = ['パンダ', 'ライオン', ...animals]
console.log(newAnimals); // ['パンダ', 'ライオン', '犬', '猫']
ES5
// Array
var animals = ["犬", "猫"];
var newAnimals = ["パンダ", "ライオン"].concat(animals);
console.log(newAnimals); // ['パンダ', 'ライオン', '犬', '猫']
オプショナルチェイニング
- 他の言語で知ってる人は違和感ないと思いますが、知らない人だとこの
?
は何?となりがちです
Babel実行前後の比較
-
user1.team?.name
の部分は(_user1$team = user1.team) === null || _user1$team === void 0 ? void 0 : _user1$team.name;
で表現されています- もし
user.team.name
と書いてteamがundefined
だった場合エラーで落ちてしまいます - ?をつけておくと
team
がnull
やundefined
だった場合エラーにさせずその場でundefined
を返却してくれます
- もし
- Babelの実行結果
ES20XX
const user1 = {
name: 'ozaki25',
team: {
name: 'Runners',
},
}
const user2 = {
name: 'ozaki26',
}
const teamName1 = user1.team?.name
console.log(teamName1); // Runners
const teamName2 = user2.team?.name
console.log(teamName2); // undefined
ES5
var _user1$team, _user2$team;
var user1 = {
name: "ozaki25",
team: {
name: "Runners"
}
};
var user2 = {
name: "ozaki26"
};
var teamName1 =
(_user1$team = user1.team) === null || _user1$team === void 0
? void 0
: _user1$team.name;
console.log(teamName1); // Runners
var teamName2 =
(_user2$team = user2.team) === null || _user2$team === void 0
? void 0
: _user2$team.name;
console.log(teamName2); // undefined
まとめ
- とりあえず思いついたものだけ書きましたが他にも初見殺しの記法はいろいろありますね
- どれも慣れてしまえば便利な構文なので当たり前に使えるようになっていきましょう