TypeScriptのベストプラクティスを見てみる #8

github.com

TypeScriptのベストプラクティスを見てみようの第8弾です。

36. コールバックよりもPromiseを優先する

Promiseは非同期処理を扱うためのオブジェクトで、コールバックに比べてコードが読みやすく、エラー処理も簡単です。特にasync/awaitと組み合わせることで、非同期処理が直感的に書けるようになります。

サンプルコード

// コールバックの例
function fetchData(callback) {
    setTimeout(() => {
        callback(null, "データを取得しました");
    }, 1000);
}

fetchData((err, data) => {
    if (err) {
        console.error(err);
    } else {
        console.log(data);
    }
});

// Promiseの例
function fetchData() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("データを取得しました");
        }, 1000);
    });
}

fetchData().then(data => {
    console.log(data);
}).catch(err => {
    console.error(err);
});

// async/awaitの例
async function fetchData() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("データを取得しました");
        }, 1000);
    });
}

async function getData() {
    try {
        const data = await fetchData();
        console.log(data);
    } catch (err) {
        console.error(err);
    }
}

getData();

あまりコールバック自体を使うケースがないので、今後もasync/awaitを使っていく所存です。Promiseを使うことで、コードが読みやすくなり、非同期処理が簡単になります。JavaScriptの非同期処理を効率的に行うために、引き続きPromiseを活用していきたいです。


37. 奇妙なJavaScriptの機能を使わない

JavaScriptには多くの機能がありますが、使うべきでないものも存在します。例えば、配列のlengthプロパティの更新、withキーワード、voidキーワード、ネイティブオブジェクトのプロトタイプ更新、eval()関数などです。これらはコードの可読性や保守性を損なう原因となります。

サンプルコード

// 配列のlengthプロパティの更新
let arr = [1, 2, 3, 4];
arr.length = 2; // [1, 2]

// withキーワード
with (Math) {
    let x = cos(3.14);
    console.log(x);
}

// voidキーワード
void function() {
    console.log("これは無視されます");
}();

// ネイティブオブジェクトのプロトタイプ更新
Array.prototype.customMethod = function() {
    return this.length;
};

// eval()関数
let x = 10;
eval('x = 20');
console.log(x); // 20

lengthを更新するケースもないですし、withは使いません。ただ、voidは関数の戻り値の型を定義する際に使ったりはしていたので、推奨されないんだと思いました。これらの奇妙な機能は、コードの可読性や保守性を損なう可能性があるため、今後は使用を控えるようにします。


38. ウェブブラウザの気まぐれに屈しない

特定のブラウザに特化したコードを書くことは避けるべきです。これにより、コードの保守が難しくなり、他のブラウザでの動作保証ができなくなります。標準に基づいたコードを書くことで、幅広いブラウザでの互換性を保つことができます。

サンプルコード

// 特定のブラウザに特化したコード(避けるべき)
if (navigator.userAgent.indexOf('MSIE') !== -1) {
    // Internet Explorer特有の処理
}

// 標準に基づいたコード
const element = document.querySelector('#example');
element.textContent = '標準に基づいたテキストの設定';

特定のブラウザ特化したコードが何なのか不明ですが、今はChromeSafariのシェアが高いと思うのでそれらをカバーできていれば良さそうに思えます。ただし、標準に基づいたコードを書くことは、将来的な保守性を考えると非常に重要です。ブラウザのシェアは変動することが多いため、標準に従ったコードを意識していきたいです。


39. スクリプトはページの一番下に配置する

スクリプトをページの一番下に配置することで、ページの読み込み速度を向上させることができます。ブラウザはスクリプトの読み込み中に他の処理を中断するため、スクリプトを最後に読み込むことで、ユーザーはページの他の部分を早く見ることができます。

サンプルコード

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ページタイトル</title>
</head>
<body>
    <h1>コンテンツ</h1>
    <p>このページにはスクリプトがあります。</p>
    <script src="script.js"></script> <!-- スクリプトはここに配置 -->
</body>
</html>

直接HTMLファイルにJavaScriptを記述することはないですが、感覚的にスクリプトは下に配置するイメージを持っていました。ページの読み込み速度を向上させるために、非同期に読み込むスクリプトも含めて、適切に配置することが重要です。これにより、ユーザー体験が向上します。


40. DOMアクセスを最小限に抑える

DOMアクセスはブラウザのパフォーマンスに大きな影響を与えるため、できるだけ最小限に抑えるべきです。Reactなどのフレームワークを使うと、仮想DOMを通じて効率的にDOM操作が行えますが、直接DOMを操作する場合は注意が必要です。

サンプルコード

// 非効率的なDOM操作
const list = document.getElementById('list');
for (let i = 0; i < 100; i++) {
    const item = document.createElement('li');
    item.textContent = `アイテム ${i}`;
    list.appendChild(item);
}

// 効率的なDOM操作
const list = document.getElementById('list');
const fragment = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
    const item = document.createElement('li');
    item.textContent = `アイテム ${i}`;
    fragment.appendChild(item);
}
list.appendChild(fragment);

高頻度で使うReactでは仮想DOMを使用しているため、あまり直接DOMを操作する場面はないですが、jQueryを使う場合は気をつけていきたいです。DOM操作はパフォーマンスに影響を与えるため、できるだけ最小限に抑えるようにします。効率的なDOM操作を意識することで、アプリケーションのパフォーマンスが向上します。