javascript 小技

## 複数の値のどれかにマッチ

```javascript:before
if (a === 1 || a === 2 || a === 3) {};
```

```javascript:after
if ([1, 2, 3].includes(a)) {};
```


## N個の連番の配列を作る
```javascript:before
const serialNumbers = [];
for (let i = 0; i < 100; ++i) {
  serialNumbers.push(i);
}
```

```javascript:after
const serialNumbers = [...Array(100)].map((_, i) => i);
```

メソッドチェーンで繋げられるので応用がしやすいです。

```javascript:奇数の総和であればこう繋げられます
[...Array(100)]
  .map((_, i) => i)
  .filter((i) => i % 2)
  .reduce((a, b) => a + b);
```

## 引数名を指定せず渡す

```javascript:before
fetchSomething()
  .then(data => console.log(data))
  .catch(error => console.error(error));
```

```javascript:after
fetchSomething().then(console.log).catch(console.error);
```

## 配列の分割代入

```javascript:before
const list = ["a", "b"];
const a = list[0];
const b = list[1];
```

```javascript:after
const list = ["a", "b"];
const [a, b] = list;
```

## オブジェクトの分割代入

```javascript:before
const obj = {a: 1, b: 2, c: 3};
const a = obj.a;
const rest = {b: obj.b, c: obj.c};
```

```javascript:after
const obj = {a: 1, b: 2, c: 3};
const {a, ...rest} = obj;
```

## ifの省略

```javascript:before
if (a) console.log("truthy");
```

```javascript:after(短絡評価)
a && console.log("truthy");
```

場合によってはifより取り回しがしやすいです。使い分けましょう。

## if notの省略

注： if notでは0や空文字も判定されます。

```javascript:before
if (!a) console.log("falsy");
```

```javascript:after(短絡評価)
a || console.log("falsy");
```

場合によってはifより取り回しがしやすいです。使い分けましょう。

## nullまたはundefinedのチェック

```javascript:before
if (a === null || a === undefined) console.log("nullish");
```

```javascript:after1(非推奨：同値演算子を使わないのでlinterによっては怒られます)
if (a == null) console.log("nullish");
```

```javascript:after2(null合体演算子)
a ?? console.log("nullish");
```

## 代入したい値が存在しなければエラーにする
```javascript:before
const a = null;
let value;
if (a === null || a === undefined) {
  throw new Error("a is not defined");
} else {
  value = a;
}
```
```javascript:after
const a = null;
const value = a ?? (() => { throw new Error("a is not defined") })();
```

## 文字列代入の省略
注： 省略したいだけなら、ショートコーディング以外では使わない方が無難です
```javascript:before
const greeting = (name) => `Hello, ${name}!`;
console.log(greeting("Alice")); // Hello, Alice!
```

```javascript:after(タグ付きテンプレートリテラル)
const greeting = (name) => `Hello, ${name}!`;
console.log(greeting`Alice`); // Hello, Alice!
```
## ObjectのOptional要素の確認の省略

```javascript:before
const obj = {};
if (obj.fnc) obj.fnc();
if (obj.options) obj.options.a;
```

```javascript:after(オプショナルチェーン)
const obj = {};
obj.fnc?.();
obj.options?.a;
```

## Objectのキーの省略

```javascript:before
const a = "hi";
const b = 123;

const data = {
  a: a,
  b: b
}
```

```javascript:after
const a = "hi";
const b = 123;

const data = {a, b}
```

## Objectの一部を取り除く

```javascript:before
const dict = {a: 1, b: 2, c: 3, d: 4, e: 5, f: 6};
const cLess = {a: dict.a, b: dict.b, d: dict.d, e: dict.e, f: dict.f};
```

```javascript:after
const dict = {a: 1, b: 2, c: 3, d: 4, e: 5, f: 6};
const cLess = (({c, ...rest}) => rest)(dict);
```

`delete dict.c;`も楽ですが、dictを破壊してしまいます。

## Objectの一部を取り出す

```javascript:before
const dict = {a: 1, b: 2, c: 3, d: 4, e: 5, f: 6};
const ace = {a: dict.a, c: dict.c, e: dict.e};
```

```javascript:after
const dict = {a: 1, b: 2, c: 3, d: 4, e: 5, f: 6};
const ace = (({a, c, e}) => ({a, c, e}))(dict);
```

読みにくくなることが多いので、使い所は考えた方がいいでしょう。

## Objectの一部をkeyを使って取り出す

```javascript:before
const dict = { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6 };
const keys = ["a", "c", "e"];
const ace = keys.reduce((obj, key) => {
  obj[key] = dict[key];
  return obj;
}, {});

console.log(ace); // { a: 1, c: 3, e: 5 }
```

```javascript:after
const dict = { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6 };
const keys = ["a", "c", "e"];
const ace = Object.fromEntries(
  Object.entries(dict).filter(([key]) => keys.includes(key))
);

console.log(ace); // { a: 1, c: 3, e: 5 }

```

## Optionalな引数に値を代入する
```javascript:before
const greet = (name) => {
  if (name === undefined) name = "Guest";
  console.log("Hello, " + name + "!");
};

greet(); // 出力: Hello, Guest!
greet("John"); // 出力: Hello, John!
```
```javascript:after(デフォルトパラメータ)
const greet = (name = "Guest") => console.log("Hello, " + name + "!");

greet(); // 出力: Hello, Guest!
greet("John"); // 出力: Hello, John!
```

## 条件によって違う値を入れる

```javascript:before
let result;
if (condition) {
  result = "a";
} else {
  result = "b";
}
```

```javascript:after
const result = condition ? "a" : "b";
```

三項演算子は代入やreturnで使うと見やすいです。ただし、ネストすると可読性が落ちるので注意が必要です。

## if文で戻り値を使わない処理を実行してから値を返す

```javascript:before
const getUserType = (isAdmin) => {
  if (isAdmin) {
    console.log("管理者です");
    return "admin";
  } else {
    console.log("一般ユーザーです");
    return "user";
  }
};

const userType = getUserType(true); // 管理者です
console.log(userType); // admin
```

```javascript:after
const getUserType = (isAdmin) =>
  isAdmin
    ? (console.log("管理者です"), "admin")
    : (console.log("一般ユーザーです"), "user");

const userType = getUserType(true); // 管理者です
console.log(userType); // admin
```
カンマ演算子は左から右へ式を評価し、最後の式の値を返します

## 複数の条件によって違う値を入れる

```javascript:before
let x;
if (condition) {
  x = "a";
} else if (condition2) {
  x = "b";
} else {
  x = "c";
}
```

```javascript:after1(即時実行関数, 可読性 ⚪︎)
const x = (() => {
  if (condition) return "a";
  if (condition2) return "b";
  return "c";
})();
```

```javascript:after2(短絡演算, 可読性 △)
const x = (condition && "a") || (condition2 && "b") || "c";
```

```javascript:after３(三項演算子のネスト, 可読性 x)
const x = condition ? "a" : condition2 ? "b" : "c";
```

慣れないと読みにくいですが、代入時に値が決定する安心感があります。
さらに条件が増えた時を考えたら、即時実行関数が無難です。

## キーから値を得る(辞書)

```javascript:before
const myMap = (key) => {
  switch (key) {
    case "a":
      return "alpha";
    case "b":
      return "bravo";
    case "c":
      return "charlie";
    default:
      break;
  }
};
console.log(myMap("a")); // alpha
```

```javascript:after1(Object)
const myMap = {
  a: "alpha",
  b: "bravo",
  c: "charlie",
};

console.log(myMap["a"]); // alpha
```

```javascript:after2(Map)
const myMap = new Map([
  ["a", "alpha"],
  ["b", "bravo"],
  ["c", "charlie"],
]);
console.log(myMap.get("a")); // alpha
```

ObjectとMapの比較はこちら

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Map#objects_vs._maps

## 条件によって配列に値を入れる

```javascript:before
const a = [1, 2];
if (condition) a.push(3, 4)
```

```javascript:after
const a = [1, 2, ...(condition ? [3, 4] : [])];
```

## undefinedかもしれない配列同士を結合

```javascript:before
const a = [1, 2];
const b = undefined;
const result = [];
if (a) result.push(a);
if (b) result.push(b);
```

```javascript:after1
const a = [1, 2];
const b = undefined;
const result = [...(a ?? []), ...(b ?? [])];
```

```javascript:after2
const a = [1, 2];
const b = undefined;
const result = [].concat(a, b).filter(Boolean); // or .filter(v => v)
```


## 条件によってオブジェクトにkey/valueを追加

```javascript:before
let obj;
if (condition) obj = {a: 1, b: 2, c: 3}
else obj = {a: 1}
```

```javascript:after1
const obj = {a: 1};
if (condition) Object.assign(obj, {b: 2, c:3});
```

```javascript:after2
const obj = {
  a: 1,
  ...(condition && {b: 2, c: 3}),
};
```

## オブジェクトに要素が存在しない場合にkey/valueを追加

```javascript:before
const obj = {a: "a"};
if (!obj.b) obj.b = "b";
```

```javascript:after(null合体代入)
const obj = {a: "a"};
obj.b ??= "b";
```

## 配列の中身をユニークな値にする

```javascript:before
const uniq = [1, 2, 1, 3, 1, 5].filter(
  (value, index, array) => array.indexOf(value) === index
);
```

```javascript:after
const uniq = [...new Set([1, 2, 1, 3, 1, 5])];
```

## 連続的にインスタンスの値を処理する
```javascript:before
class Point {
  sum = 0;
  add(x) {
    this.sum += x;
  }
}
const myPoint = new Point();
myPoint.add(10);
myPoint.add(5);

console.log(myPoint.sum); // 15
```

```javascript:after1(class版メソッドチェーン)
class MyPoint {
  sum = 0;
  add(x) {
    this.sum += x;
    return this;
  }
}
const myPoint = new MyPoint();
myPoint.add(10).add(5);

console.log(myPoint.sum); // 15
```

```javascript:after2(関数版メソッドチェーン)
const createPoint = () => ({
  sum: 0,
  add(x) {
    this.sum += x;
    return this;
  },
});
const myPoint = createPoint();
myPoint.add(10).add(5);

console.log(myPoint.sum); // 15
```
thisを返せば、連続的な処理ができます。class版の方が無難です。
(関数の場合、引数を取って代入しておけばコンストラクタの代わりになり、シンボルを使えばprivateの代わりになります)

## 部分適用で再利用可能な関数を作る

```javascript:before
const createGreeting = (greeting, name) => `${greeting}, ${name}!`;
const sayHello = (name) => createGreeting("Hello", name);
const sayHi = (name) => createGreeting("Hi", name);

console.log(createGreeting("Hello", "John")); // Hello, John!
console.log(createGreeting("Hi", "Alice")); // Hi, Alice!
```

```javascript:after1(bind)
const createGreeting = (greeting, name) => `${greeting}, ${name}!`;
const sayHello = createGreeting.bind("Hello");
const sayHi = createGreeting.bind("Hi");

console.log(sayHello("Hello", "John")); // Hello, John!
console.log(sayHi("Hi", "Alice")); // Hi, Alice!
```

```javascript:after2(カリー化)
const createGreeting = (greeting) => (name) => `${greeting}, ${name}!`;
const sayHello = createGreeting("Hello");
const sayHi = createGreeting("Hi");

console.log(sayHello("John")); // Hello, John!
console.log(sayHi("Alice")); // Hi, Alice!
```

## console.log内の文字結合の省略

```javascript:before
console.log("1 plus 1 is " + (1 + 1));
```

```javascript:after1(テンプレートリテラル)
console.log(`1 plus 1 is ${1 + 1}`);
```

```javascript:after2(カンマ)
console.log("1 plus 1 is", 1 + 1);
```

## 表形式のデータを見やすくログに出す

```javascript:before
console.log([
  [1, 2],
  [3, 4],
]);
console.log({
  a: {x: 1, y: 2},
  b: {x: 3, y: 4},
});
```

```javascript:after
console.table([
  [1, 2],
  [3, 4],
]);
console.table({
  a: {x: 1, y: 2},
  b: {x: 3, y: 4},
});
```

### 繰り返し処理の最適化
```javascript:before
const fibonacci = (n) => {
  if (n <= 1) return n;
  return fibonacci(n - 1) + fibonacci(n - 2);
};
console.log(fibonacci(45)); // 1134903170
// 実行時間：　5000ms
```
```javascript:after(メモ化)
const memo = [0, 1]; // ここに計算結果を追加していきます
const fibonacci = (n) => {
  if (memo[n] !== undefined) return memo[n];
  memo[n] = fibonacci(n - 1) + fibonacci(n - 2);
  return memo[n];
};
console.log(fibonacci(45)); // 1134903170
// 実行時間: 1ms
```
計算結果を保存してそれを使いまわすようにすれば、計算を減らせます。

### テンプレートリテラルに複雑な処理をする
```javascript:before
const i18n = (key) => {
  const translations = {
    Hello: "こんにちは",
    Goodbye: "さようなら",
  };

  return translations[key] || key;
};

console.log(i18n("Hello") + "😃 " + i18n("Goodbye") + "👋!"); // こんにちは😃 さようなら👋!
```

```javascript:after(タグ付きテンプレートリテラル)
const i18n = (strings, ...values) => {
  const translations = {
    Hello: "こんにちは",
    Goodbye: "さようなら",
  };

  return strings.reduce((result, string, i) => {
    const translated = translations[values[i]] || values[i];
    return result + string + (translated || "");
  }, "");
};

console.log(i18n`${"Hello"}😃 ${"Goodbye"}👋!`); // こんにちは😃 さようなら👋!
```