2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

MDCAdvent Calendar 2020

Day 3

JavaScriptの不思議な記法をBabelを通して解釈してみる

Last updated at Posted at 2020-12-02

概要

  • 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 だった場合エラーで落ちてしまいます
    • ?をつけておくと teamnullundefined だった場合エラーにさせずその場で 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

まとめ

  • とりあえず思いついたものだけ書きましたが他にも初見殺しの記法はいろいろありますね
  • どれも慣れてしまえば便利な構文なので当たり前に使えるようになっていきましょう
2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?