search
LoginSignup
38
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

イマドキな JavaScript で書かない・使わないもの: var, function, then, jQuery, その他

※2021/11/10: 記事を全体的に編集、一部内容追加しました。

※ IE 等では Babel やポリフィル等を使用する前提です。

0. まとめ

  • var を書かない
    • let もなるべく書かない
    • なるべく const を使う
    • 関数の引数は const のように扱う
  • function をなるべく書かない
    • アロー関数またはクラスやオブジェクトのメソッド定義を使う
    • 関数宣言でなく関数式を使用する
    • 理論上どうしても function が必要な場合もある
    • ジェネレータ関数 function* は IE 以外では基本的に使用できる
  • then() を書かない
    • await を使う
  • 理想的には jQuery を使わない
    • 純粋な WEB API を使用して DOM を操作する
      • getElementById(), getElementsByClassName()
      • querySelector(), querySelectorAll()
      • insertAdjacentHTML()
    • jQuery 非依存のライブラリを使用する
    • fetch() で通信する
  • XMLHttpRequest を使わない
    • fetch() で通信する
  • 配列を要素別に引数として扱いたいときに apply() を使用しない
    • スプレッド構文 ... を使用する
    • Array.prototype.reduce() を使用する
  • 数値や文字列の桁揃え (パディング) で文字列連結と slice() を使用しない
    • padStart() を使用する

1. var を書かない

1.1. なるべく const を使う (prefer-const)

なるべく let でなく const を使用します。

別記事に分離しました。

参考「[JavaScript] var を書かない&なるべく const を使う - Qiita

1.2. 関数の引数は const のように扱う (no-param-reassign)

関数の引数の値を変更すると混乱を招き、バグの原因にもなるので値を変更しません。

悪い例
const foo = bar => {
    if ( ! bar ) { // bar が falsy なとき
        bar = 23;
    }
};
良い例
const foo = bar => {
    const baz = bar || 23;
};

// メモ: falsy の場合は論理和 || 、nullish の場合は Null 合体演算子 ?? 、undefined の場合はデフォルト引数を用いる

※ ESLint のドキュメントでは全て function の関数宣言で例が載っていますが、関数式やアロー関数も ESLint での no-param-reassign の対象になっています。

参考「no-param-reassign - Rules - ESLint - Pluggable JavaScript linter
参考「7.12 Mutate Params - 7. Functions - Airbnb JavaScript Style Guide
参考「7.13 Reassign Params - 7. Functions - Airbnb JavaScript Style Guide

2. function をなるべく書かない

function による関数宣言のデメリットは以下のようなものがあります:

  • this の扱いが特殊で使いづらい
  • 関数の「巻き上げ」が可読性と保守性を損なう

2.1. コールバック関数はアロー関数を使用する (prefer-arrow-callback)

functionthis の挙動が特殊で、予期せぬ動作をしてバグの原因になったり、クラスやオブジェクトの this を呼ぶことが不便であったりするため、function を使用しない方が混乱を避けられます。

単にコールバック関数を生成したい場合はアロー関数を使用します。

参考「prefer-arrow-callback - Rules - ESLint - Pluggable JavaScript linter

悪い例
const foo = [10, 20, 30, 40, 50];

const bar = foo.map(function (x) { return x * x; }); // bar: [100, 400, 900, 1600, 2500]
良い例
const foo = [10, 20, 30, 40, 50];

const bar = foo.map(x => x * x); // bar: [100, 400, 900, 1600, 2500]

参考「アロー関数式 - JavaScript | MDN

addEventListener() では this を使用せず、コールバック関数の引数 event から event.currentTarget でイベントがアタッチされた要素を取得できます。

悪い例
element.addEventListener('click', function () {
    const currentTarget = this;
});
良い例
element.addEventListener('click', event => {
    const currentTarget = event.currentTarget;
});

参考「Event - Web API | MDN

2.2. クラスやオブジェクトのメソッド定義を使う (object-shorthand)

クラスやオブジェクトのメソッドの定義では function を表記せず簡潔に定義できます。

※古いバージョンではオブジェクトのメソッド定義で function が必要でしたが、現在は不要です。

悪い例
const obj = {
    foo: function() {},
    bar: function() {},
};
良い例
const obj = {
    foo() {},
    bar() {},
};

参考「9.1 Use class - 9. Classes & Constructors - Airbnb JavaScript Style Guide
参考「クラス - JavaScript | MDN

参考「object-shorthand - Rules - ESLint - Pluggable JavaScript linter
参考「メソッドの定義 - オブジェクト初期化子 - JavaScript | MDN

2.3. 関数宣言でなく関数式を使用する (func-style)

function による関数宣言は「巻き上げ」が起こり、可読性と保守性を損なうため、関数宣言でなく関数式を使用します。

function による関数式は this の扱いが面倒な場合があるため、なるべくアロー関数を使用した方が良いと思います。

悪い例
function foo() {
    // ...
}
良い例
const foo = () => {
    // ...
};

const bar = function () { // メモ: 無名関数にすべきかどうかは環境によります
    // ...
};

参考「func-style - Rules - ESLint - Pluggable JavaScript linter
参考「7.1 Declarations - 7. Functions - Airbnb JavaScript Style Guide

2.4. 理論上どうしても function が必要な場合

関数を生成したいプログラムで、function でないと実現不可能な場合もあります。

参考「JavaScript の デコレータ の使い方 - Qiita

3. ジェネレータ関数 function* は使用して良い

ジェネレータ関数 function* は関数 function とは別物で、用途が異なるため使用して良いです。

(※ジェネレータ自体の使い方は本記事では不説明。)

参考「function* 式 - JavaScript | MDN

3.1. 関数宣言でなく関数式を使用する (func-style)

ジェネレータ関数 function* の関数宣言も関数 function の関数宣言と同様に「巻き上げ」が起こり、可読性と保守性を損なうため、関数宣言でなく関数式を使用します。

悪い例
function* foo() {
    // ...
}
良い例
const foo = function* () { // メモ: 無名関数にすべきかどうかは環境によります
    // ...
};

※ ESLint のドキュメントでは全て関数 function で例が載っていますが、ジェネレータ関数 function* も ESLint での func-style の対象になっています。

参考「func-style - Rules - ESLint - Pluggable JavaScript linter

3.2. ジェネレータ関数での this の扱い

ジェネレータ関数 function* も関数 function と同様に this の扱いに注意が必要ですが、以下の理由により関数 function 程の問題は起きません:

  • そもそもジェネレータ関数内で this を使用すると副作用を持つことになるため、良くない
  • ジェネレータ関数で this を使用しない書き方ができる
    • 初期値等はジェネレータ関数の引数で受け取る
    • 処理に使用する値はローカル変数で管理
    • yield, yield* で値を受け取る
  • クラスまたはオブジェクトのメソッドとしてジェネレータを定義すると this を使用できる (静的 static でない場合)
    • 他の関数にジェネレータを渡したい場合、ジェネレータ関数を直接コールバック関数として渡さず、ジェネレータオブジェクトを返すアロー関数を使用すればいいため、ジェネレータ関数内の this を気にしなくて良い書き方ができる

3.3. ジェネレータを使用できない場合

現在、IE 以外の主要環境ではジェネレータに対応しており、IE 自体は 2022 年にサポートが終了し IE 対応を考えなくて良くなります。

"Airbnb JavaScript Style Guide" では、古いバージョンの JavaScript への変換が上手くできないことを理由にジェネレータを使用しないよう推奨していますが、ジェネレータ自体が非常に強力な機能であるため、IE 対応が必須でないなら切り捨てて、ジェネレータを使用して良いと思います。

どうしても IE 対応が必要な場合は使用できません。

参考「function* 式 - JavaScript | MDN
参考「11.2 Generators: Nope - 11. Iterators and Generators - Airbnb JavaScript Style Guide

4. Promise 利用時に then() を書かない

then() の処理を書きやすくしたものが await です。

IE 以外の主要環境では Promise に対応しており、そのいずれもが async, await をサポートしています。

then() は使用せずに await を使用してください。

参考「Promise - JavaScript | MDN
参考「await - JavaScript | MDN

catch() は状況に応じて使用します。

5. jQuery を使わない

現在は、理想的には jQuery を使用しないべきです。

jQuery は昔は便利なライブラリでしたが、今では jQuery 自体は使用するメリットが小さいです。
今ではネイティブ JavaScript の純粋な Web API が改善され、だいぶ扱いやすくなっています。

jQuery のデメリットとして以下のようなものがあります:

  • 読み込み時間・実行時間
  • jQuery 依存ライブラリやアプリケーションの設計が古いまたは悪いことによる問題 (テスト容易性、再利用性、レンダリングブロック等の問題)
  • jQuery のバージョン間の互換性の問題 (依存性、保守性の問題)

5.1. ネイティブ JavaScript で書く場合

ID やクラス名で要素を取得したいということが分かっているなら、getElementById()getElementsByClassName() を使用したほうが高速に動作します。

jQuery のように複雑なセレクタを用いて要素を取得したい場合は、querySelector()querySelectorAll() を使用します。

DOM の生成に insertAdjacentHTML() を使用する方法があります。

参考「Document.querySelector() - Web API | MDN
参考「Document.querySelectorAll() - Web API | MDN
参考「element.insertAdjacentHTML - Web API | MDN

jQuery.ajax() を使わなくても fetch() で簡単に通信できます。

参考「WindowOrWorkerGlobalScope.fetch() - Web API | MDN

5.2. jQuery 非依存のライブラリを使用する場合

ライブラリの例として、「Bootstrap 5」は jQuery 非依存になりました。

参考「jQuery依存を脱した「Bootstrap 5」正式リリース、IEもついにサポート対象外に。右からの横書き「RTL」など新機能 - Publickey

5.3. 読み込み時間・実行時間

通信時間を考慮したチューニングが行われている場合はある程度改善しますが、キャッシュが保存されていない状態だと jQuery の読み込みに数百ミリ秒かかることもあり、できることの割に時間がかかり過ぎです。
(動的読み込みしておらず、CDN を利用せず、バンドルされておらず、HTTP のバージョンが古い場合にはかなり悪いです。)

規模が大きな Web アプリケーションの場合にはそもそも jQuery よりも便利なライブラリ (Angular, React, Vue 等) がありますし、ネイティブ JavaScript で簡単に実装可能な部分に関しては jQuery を使用しないことで動作速度が改善します。

ブログ等、JavaScript が主体ではないような場合では読み込み速度を少しでも早くするのが理想的です。

5.4. 設計の問題やバージョン間の互換性の問題

本記事では深く触れませんが、jQuery を使用することによってソフトウェアの本質的ではないところで様々な問題が発生します。

jQuery を使用しないことでこれらの問題を回避できます。

5.5. それでも jQuery を使用する場合

ソフトウェア品質 (製品品質) を落としてでも開発コストを抑えたいか、または使用したいライブラリが jQuery 依存でありコストの問題で脱 jQuery がむずかしい場合は jQuery を使用します。

jQuery を使用することで初期の開発コストの削減につながりますが、保守性を考慮した上で本当にコストが削減できるかは検討する必要があります。

6. XMLHttpRequest を使わない

fetch() で簡単に通信できます。

参考「Fetch の使用 - Web API | MDN

7. 配列を要素別に引数として扱いたいときに apply() を使用しない

7.1. スプレッド構文 ... を使用する (prefer-spread, no-useless-call)

配列または NodeList 等の配列風オブジェクトに関して、要素別に、関数やメソッドの引数として扱いたいとき、apply() でなくスプレッド構文 ... を使用します。

関数引数におけるスプレッド構文は、function による関数だけでなくアロー関数でも使用できます。

悪い例
const foo = function (a, b, c) { return a * b + c; };

const bar = foo.apply(null, [2, 10, 3]); // bar === 23
良い例
const foo = (a, b, c) => a * b + c;

const bar = foo(...[2, 10, 3]); // bar === 23

参考「prefer-spread - Rules - ESLint - Pluggable JavaScript linter
参考「no-useless-call - Rules - ESLint - Pluggable JavaScript linter

7.2. Array.prototype.reduce() を使用する

apply() やスプレッド構文の例として Math.max()Math.min() が良く用いられますが、そもそも要素数が多い配列を引数として扱おうとすると問題が起こるため、Array.prototype.reduce() を使用すべきです。

悪い例
const array = [20, 30, 10, 50, 40]; // メモ: 本番環境では個数が変わる可能性がある仮定

const max = Math.max.apply(null, array);
// or
const max = Math.max(...array);
良い例
const array = [20, 30, 10, 50, 40]; // メモ: 本番環境では個数が変わる可能性がある仮定

const max = array.reduce((acc, x) => Math.max(acc, x));

参考「Math.max() - JavaScript | MDN

8. 数値や文字列の桁揃え (パディング) で文字列連結と slice() を使用しない

古い JavaScript の書き方で、数値の桁揃え等で文字列連結して slice() で文字数分切り取るという手法が使われていましたが、現在は IE 以外の主要環境で padStart() が利用できるため、padStart() を使用します。

悪い例
const foo = 23;

const paddedFoo = ('00' + foo).slice(-3); // paddedFoo === '023'
良い例
const foo = 23;

const paddedFoo = foo.toString().padStart(3, '0'); // paddedFoo === '023'

※桁があふれた場合の挙動は異なります (slice() は切り捨て、padStart() は元文字列を保持) 。
※実用的には桁揃えする数の正当性を確認する必要があります。

参考「String.prototype.padStart() - JavaScript | MDN

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
What you can do with signing up
38
Help us understand the problem. What are the problem?