80
80

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

javascript 小技

Last updated at Posted at 2021-09-28

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

before
if (a === 1 || a === 2 || a === 3) {};
after
if ([1, 2, 3].includes(a)) {};

N個の連番の配列を作る

before
const serialNumbers = [];
for (let i = 0; i < 100; ++i) {
  serialNumbers.push(i);
}
after
const serialNumbers = [...Array(100)].map((_, i) => i);

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

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

引数名を指定せず渡す

before
fetchSomething()
  .then(data => console.log(data))
  .catch(error => console.error(error));
after
fetchSomething().then(console.log).catch(console.error);

配列の分割代入

before
const list = ["a", "b"];
const a = list[0];
const b = list[1];
after
const list = ["a", "b"];
const [a, b] = list;

オブジェクトの分割代入

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

ifの省略

before
if (a) console.log("truthy");
after(短絡評価)
a && console.log("truthy");

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

if notの省略

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

before
if (!a) console.log("falsy");
after(短絡評価)
a || console.log("falsy");

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

nullまたはundefinedのチェック

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

代入したい値が存在しなければエラーにする

before
const a = null;
let value;
if (a === null || a === undefined) {
  throw new Error("a is not defined");
} else {
  value = a;
}
after
const a = null;
const value = a ?? (() => { throw new Error("a is not defined") })();

文字列代入の省略

注: 省略したいだけなら、ショートコーディング以外では使わない方が無難です

before
const greeting = (name) => `Hello, ${name}!`;
console.log(greeting("Alice")); // Hello, Alice!
after(タグ付きテンプレートリテラル)
const greeting = (name) => `Hello, ${name}!`;
console.log(greeting`Alice`); // Hello, Alice!

ObjectのOptional要素の確認の省略

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

Objectのキーの省略

before
const a = "hi";
const b = 123;

const data = {
  a: a,
  b: b
}
after
const a = "hi";
const b = 123;

const data = {a, b}

Objectの一部を取り除く

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};
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の一部を取り出す

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};
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を使って取り出す

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 }
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な引数に値を代入する

before
const greet = (name) => {
  if (name === undefined) name = "Guest";
  console.log("Hello, " + name + "!");
};

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

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

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

before
let result;
if (condition) {
  result = "a";
} else {
  result = "b";
}
after
const result = condition ? "a" : "b";

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

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

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

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

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

カンマ演算子は左から右へ式を評価し、最後の式の値を返します

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

before
let x;
if (condition) {
  x = "a";
} else if (condition2) {
  x = "b";
} else {
  x = "c";
}
after1(即時実行関数, 可読性 ⚪︎)
const x = (() => {
  if (condition) return "a";
  if (condition2) return "b";
  return "c";
})();
after2(短絡演算, 可読性 △)
const x = (condition && "a") || (condition2 && "b") || "c";
after3(三項演算子のネスト, 可読性 x)
const x = condition ? "a" : condition2 ? "b" : "c";

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

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

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
after1(Object)
const myMap = {
  a: "alpha",
  b: "bravo",
  c: "charlie",
};

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

ObjectとMapの比較はこちら

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

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

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

before
const a = [1, 2];
const b = undefined;
const result = [];
if (a) result.push(a);
if (b) result.push(b);
after1
const a = [1, 2];
const b = undefined;
const result = [...(a ?? []), ...(b ?? [])];
after2
const a = [1, 2];
const b = undefined;
const result = [].concat(a, b).filter(Boolean); // or .filter(v => v)

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

before
let obj;
if (condition) obj = {a: 1, b: 2, c: 3}
else obj = {a: 1}
after1
const obj = {a: 1};
if (condition) Object.assign(obj, {b: 2, c:3});
after2
const obj = {
  a: 1,
  ...(condition && {b: 2, c: 3}),
};

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

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

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

before
const uniq = [1, 2, 1, 3, 1, 5].filter(
  (value, index, array) => array.indexOf(value) === index
);
after
const uniq = [...new Set([1, 2, 1, 3, 1, 5])];

連続的にインスタンスの値を処理する

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
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
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の代わりになります)

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

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!
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!
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内の文字結合の省略

before
console.log("1 plus 1 is " + (1 + 1));
after1(テンプレートリテラル)
console.log(`1 plus 1 is ${1 + 1}`);
after2(カンマ)
console.log("1 plus 1 is", 1 + 1);

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

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

繰り返し処理の最適化

before
const fibonacci = (n) => {
  if (n <= 1) return n;
  return fibonacci(n - 1) + fibonacci(n - 2);
};
console.log(fibonacci(45)); // 1134903170
// 実行時間: 5000ms
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

計算結果を保存してそれを使いまわすようにすれば、計算を減らせます。

テンプレートリテラルに複雑な処理をする

before
const i18n = (key) => {
  const translations = {
    Hello: "こんにちは",
    Goodbye: "さようなら",
  };

  return translations[key] || key;
};

console.log(i18n("Hello") + "😃 " + i18n("Goodbye") + "👋!"); // こんにちは😃 さようなら👋!
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"}👋!`); // こんにちは😃 さようなら👋!
80
80
1

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
80
80

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?