メインコンテンツへ移動

JS プログラミング常識

無料2015-05-10#JS#js代码优化#js编程规范

よりメンテナンス性の高いコードを書くためには、コードスタイルを知るだけでは不十分で、これらのプログラミング常識を牢记する必要があります

##一.UI レイヤーの疎結合

疎結合とは、各レイヤーが「最小知識の原則」に従うこと、または各レイヤーが自分の役割を果たし、権限を超えないことを要求します:

  • HTML:構造レイヤー

  • CSS:表現レイヤー

  • JS:行動レイヤー

各レイヤーの役割について、適切な説明があります:HTML は名詞(n)、CSS は形容詞(adj)と副詞(adv)、JS は動詞です

3 つのレイヤーは密接に関連しているため、実際の適用では権限を超えやすいです:

耦合

###1.css から js を分離

できるだけcss 式を使用しないでください。使用する必要がある場合は、対応するコードを hack に配置して、メンテナンスを容易にします

###2.js から css を分離

ele.style.attr および ele.cssText を使用しないでください。クラス名を操作する方法で置き換えるべきです

###3.html から js を分離

onclick などの属性を使用してイベント処理関数を直接指定しないでください。イベントハンドラを追加する方法で置き換えるべきです

一般に*<script>タグを使用して js コードを直接埋め込まないでください*。外部 js ファイルに配置してください。もちろん、機能が単一で再利用が不要なコードは HTML に直接埋め込むことができます。例えばフォーム検証コードなど

###4.js から html を分離

innerHTML を直接使用してハードコーディングしないでください。以下の 3 つの方法で置き換えることができます:

  1. Ajax を使用してサーバーから HTML 文字列を取得し、ハードコーディングを回避

  2. 単純なクライアントテンプレートを使用。2 つの実装方法があります:

    • コメントでパターン文字列を保持

    • script タグでパターン文字列を保持。type 属性をブラウザが認識できない値に設定し、script タグに id 属性を設定してパターン文字列を取得しやすくします。例えば:

       <script type="text/x-my-template" id="list-item">
           <li><a href="%s">%s</li>
       </script>
       
      

      この方法を推奨します。パターン文字列を取得しやすいためです

  3. 複雑なクライアントテンプレートを使用。例えば jade、ejs

P.S.html と css の疎結合は js プログラミングに関係ないため、書籍には対応する内容がありません

##二.グローバル変数を少なめに

###1.グローバル変数がもたらす問題

  • 命名競合

  • コードの堅牢性が低い。関数に必要なすべての外部データはパラメータで渡すべきで、グローバル変数でパラメータを渡さない

  • テストが困難。グローバル環境全体を再構築する必要がある

###2.暗黙のグローバル変数

暗黙のグローバル変数方式でグローバル変数を宣言しないでください。すべての変数宣言にvarキーワードを付けることを推奨します

暗黙のグローバル変数を回避するために、厳格モード("use strict";)を有効にするべきです。[IE10+] がサポート

###3.単一グローバル変数方式

  1. 名前空間を使用。命名空間競合を回避する方法を提供:

    // 顶级命名空间
    var app = {
        /*
         * 创建/获取子命名空间,支持链式调用
         */
        namespace: function(ns) {
            var parts = ns.split("."),
                object = this,
                i, len;
                
            for (i = 0, len = parts.length; i < len; i++) {
                if (!object[parts[i]]) {
                    object[parts[i]] = {};
                }
                object = object[parts[i]];
            }
            
            return object;  // 支持链式调用
        }
    }
    
    // 测试
    app.namespace("Consts").homepage = "http://ayqy.net/";
    app.namespace("Consts").author = "ayqy";
    // http://ayqy.net/, ayqy
    alert(app.Consts.homepage + ", " + app.Consts.author);
    
  2. モジュール化

AMD/CMD。拡張知識は以下の通り:

CommonJS は理論規範(例えば js の理論規範は ES)であり、SeaJS、RequireJS はすべて CommonJS の Modules 部分の具体的な実装です

CommonJS はブラウザ外(server 端)の js に向けて制定されたもので、同期モジュールロードです。SeaJS は CommonJS の一つの実装であり、RequireJS も CommonJS の実装ですが、非同期モジュールロードで、ブラウザのシングルスレッド環境により適合しています

結論:CommonJS の Modules 部分はモジュール化コード管理の理論を提案し、js をモジュール化ロードできるようにしました。RequireJS、SeaJS などの各種実装はモジュール化スクリプトローダーと呼べます

-  CMD:Common モジュール定義。例えば SeaJS

-  AMD:非同期モジュール定義。例えば RequireJS

すべてコードモジュールを定義するための規範で、モジュール化スクリプトロードに便利で、レスポンス速度を向上

CMD と AMD の違い:

  • CMD は依存を近くに配置。使用に便利。モジュール内部で随時取得でき、依存項目を事前に宣言する必要がないため、パフォーマンス面で若干低下(モジュール全体を遍历して依存項目を探す必要がある)

  • AMD は依存を前置。依存項目を厳格に宣言する必要があり、ロジック内部の依存項目(ソフト依存)は、非同期ロード、コールバック処理で解決

###4.ゼログローバル変数方式

IIFE(匿名関数即時実行)で実装。再利用が不要な機能モジュールに対して IIFE を使用してグローバル変数を完全に消除できます。そのため一般に IIFE は名前空間/モジュール化方式を補助するために使用されます

##三.イベント処理

###1.典型的な使い方(良くない)

// 事件处理器
function handleClick(event) {
    var popup = document.getElementById("popup");
    popup.style.left = event.clientX + "px";
    popup.style.top = event.clientY + "px";
    popup.className = "display";
}

// 添加事件处理器
ele.addEventListener("click", handleClick);

###2.アプリケーションロジックを分離(少し良い)

var app = {
    // 事件处理
    handleClick: function(event) {
        this.showPopup(event);
    },
    
    // 应用逻辑
    showPopup: function(event) {
        var popup = document.getElementById("popup");
        popup.style.left = event.clientX + "px";
        popup.style.top = event.clientY + "px";
        popup.className = "display";
    }
};

// 添加事件处理器
// P.S.事件处理器是一个方法声明,而不是方法调用,无法传参,所以需要多一层匿名函数
ele.addEventListener("click", function() {
    app.handleClick(event);
});

###3.イベントオブジェクトを渡さない(最良)

var app = {
    // 事件处理
    handleClick: function(event) {
        this.showPopup(event.clientX, event.clientY);   // 参数变了
    },
    
    // 应用逻辑
    showPopup: function(x, y) { // 形参变了
        var popup = document.getElementById("popup");
        popup.style.left = x + "px";
        popup.style.top = x + "px";
        popup.className = "display";
    }
};

// 添加事件处理器
ele.addEventListener("click", function() {
    app.handleClick(event);
});

"イベントオブジェクトを渡さない"は最適化原則であり、[js 高程の最適化部分](http://ayqy.net/blog/JS 学习笔记 12_优化/) でも言及されていますが、本書は詳細な理由を示しています

イベントオブジェクトを直接渡すことには以下の欠点があります:

  1. インターフェース定義が不明確。パラメータの役割が不明

  2. テストが困難(event オブジェクトを再構築?)

##四.null との比較を少なめに

###1.基本値の検出

typeof で検出しますが、typeof nullobjectを返すことに注意してください。これは科学的ではありません。js は null を空のオブジェクト参照と考えているためです

ただし、=== null で DOM 要素を検出するのは合理的です。null は document.getXXByXXX の可能な出力の一つであるためです

###2.参照値の検出

instanceof はサブタイプを正確に検出できず、fun と arr の検出に使用しないでください。frame を跨げないためです

  1. fun の検出

typeof で一般メソッドを検出。in で DOM メソッドを検出

  1. arr の検出

Object.prototype.toString.call(arr) === "[Object Array]"で検出

注意:ES5 にはネイティブの Array.isArray() メソッドがあります。[IE9+] がサポート

###3.プロパティの検出

in と hasOwnProperty() を組み合わせて検出

注意:[IE8-] の DOM 要素は hasOwnProperty() をサポートしていません。使用する前に検出が必要です

##五.設定データの分離

###1.設定データには何がある?

  1. ハードコーディングされた値

  2. 将来変化する可能性のある値

例えば:

  • URL

  • ユーザーに表示する必要がある文字列

  • 繰り返し使用される一意の値

  • 設定(例えば 1 ページに表示するリスト項目数)

  • 変化する可能性のある任意の値(メンテナンスが難しいものはすべて設定データとみなす)

###2.設定データの分離

まずアプリケーションロジックから設定データを分離します。最も簡単には、すべての設定データをカスタムの config オブジェクトに階層的に保存できます

###3.設定データの保存

js ファイルで設定データを保存できます。ロードに便利ですが、設定データは js 構文に厳格に準拠する必要があり、エラーが発生しやすいです。著者は設定データを形式が簡単なプロパティファイルとして保存し、ツールで JSON/JSONP/js 形式ファイルに変換してロードすることを推奨しています。著者は自身が作成したツールprops2jsを推奨しています

##六.カスタムエラーをスロー

###1.Error の本質

予期せぬものをマークするために使用され、サイレントフェイルを回避し、デバッグを容易にします

###2.js でエラーをスロー

他の型をスローしないでください。Error オブジェクトをスローしてください。互換性のため。例えば:

throw "error: invalid args";    // 有些浏览器不显示该字符串

###3.エラーをスローする利点

エラーを正確に位置特定。エラー情報形式を推奨:関数名 + エラー原因

###4.いつエラーをスローすべきか

一般的なメソッド(ツールメソッド)内のエラーのみをスロー。一般原則:

  1. 奇妙なバグを修正した後、カスタムエラーを追加して、エラーが再び発生するのを防止

  2. コード 작성 시 어떤 부분이 큰 문제를 일으킬 수 있다고 생각되면, 사용자 정의 오류를 throw 해야 함

  3. 코드가 다른 사람이 사용하도록 작성된 경우, 다른 사람이 사용할 때 발생할 수 있는 문제를 생각하고, 사용자 정의 오류에서 힌트를 제공해야 함

###5.try-catch 문

finally 는 자주 사용되지 않음. catch 의 return 에 영향을 주기 때문

빈 catch 블록을 남기지 마세요. 조용히 실패하면 문제가 더 복잡해질 수 있음

###6.여러 가지 오류 유형

네이티브 오류 유형 인스턴스를 throw 하여 instanceof 와 함께 사용하여 대상 오류 처리 가능

P.S.구체적인 오류 유형 및 오류 처리에 대한 자세한 정보는 黯羽轻扬:JS 학습노트 8_오류 처리 를 참조하세요

##七.객체 소유권 존중

###1.어떤 객체가 우리에게 속하지 않는가?

  • 네이티브 객체 (Object, Array 등)

  • DOM 객체 (예: document)

  • BOM 객체 (예: window)

  • 라이브러리 객체 (예: JQuery)

###2.구체적인 원칙

  1. 메서드를 재작성하지 마세요

  2. 새로운 메서드를 추가하지 마세요. 라이브러리 기능을 변경해야 한다면, 라이브러리에 플러그인을 개발하세요

  3. 메서드를 삭제하지 마세요. 사용하고 싶지 않은 것은 삭제하지 말고,过时라고 표시하세요

주의: delete 는 프로토타입 속성에는 무효하며, 인스턴스 속성에만 유효함

    function Fun() {
        this.attr1 = 1; // 实例属性
    }
    Fun.prototype.attr2 = 2;    // 原型属性
    
    // 测试
    var obj = new Fun();
    alert(obj.attr1 + ", " + obj.attr2);    // 1, 2
    delete obj.attr1;
    delete obj.attr2;
    alert(obj.attr1 + ", " + obj.attr2);    // undefined, 2
    delete Fun.prototype.attr2;
    alert(obj.attr1 + ", " + obj.attr2);    // undefined, undefined
    

###3.더 나은 방법

  1. 객체 기반 상속

즉, 새로운 객체를 clone 하면, 새로운 객체는 우리 자신의 것이므로 마음대로 수정 가능

  1. 타입 기반 상속

주의: DOM/BOM/Array 를 상속하지 마세요. 지원성이 좋지 않음

P.S.객체 상속/타입 상속의 구체적인 구현에 대해서는 [黯羽轻扬:JS 의 6 가지 상속 방식 재이해](http://ayqy.net/blog/重新理解 JS 的 6 种继承方式/) 를 참조하세요

  1. 퍼사드 패턴

실제로는 조합입니다. 상속/조합 모두 코드 재사용을 구현하는 방법이기 때문입니다. 퍼사드 패턴에 대한 자세한 정보는 黯羽轻扬:디자인 패턴之 퍼사드 패턴(Facade Pattern) 를 참조하세요

약간의 잡담:facade 와 adapter 의 차이는 전자가 새로운 인터페이스를 생성하고, 후자는 기존 인터페이스를 구현한 것입니다. 저자가 한 마디로 꿰뚫음

###4.polyfill 의 장단점

polyfill 은 "구형 브라우저에서 표준 API 를 복제한 JavaScript 보충"입니다. "표준 API"란 HTML5 기술 또는 기능을 말하며, 예를 들어 Canvas 입니다. "JavaScript 보충"이란 동적으로 JavaScript 코드 또는 라이브러리를 로드하여 이러한 표준 API 를 지원하지 않는 브라우저에서 시뮬레이션할 수 있음을 말합니다. polyfill 이 표준 API 를 시뮬레이션하기 때문에, 이러한 API 에 대한 개발을 모든 브라우저의 미래를 향해 진행할 수 있으며, 최종 목표는:이러한 API 에 대한 지원이 절대 다수가 되면, 추가 작업 없이 polyfill 을 쉽게 제거할 수 있습니다.

polyfill 에 대한 자세한 정보는 博客园:[번역]shim 과 polyfill 의 차이점은? 를 참조하세요

장점:필요하지 않을 때 쉽게 제거 가능

단점:polyfill 구현이 표준과 완전히 일치하지 않으면麻烦

polyfill 을 사용하지 않는 것을 권장하며, 네이티브 메서드 + 퍼사드 패턴으로 대체하는 것이 더 유연함

###5.변조 방지

엄격 모드를 활성화해야 함. 엄격하지 않은 모드에서는 조용한 실패가 디버깅 어렵기 때문

##八.브라우저 감지

###1.UA(User Agent)감지

꼭 UA 감지를 해야 한다면, 앞쪽이 아닌 뒤쪽을 감지하세요. 뒤쪽은 다시 변하지 않기 때문

P.S.저자는 UA 가 변할지 걱정하지 않아도 된다고 생각함. 이유:UA 를 설정할 수 있는 사용자는 이렇게 하는 결과를 알고 있어야 함

###2.기능 감지

기능 감지의 일반 형식은 다음과 같음:

  1. 표준 방식 시도

  2. 특정 브라우저 구현 방식 시도

  3. 지원하지 않으면 논리적 피드백 제공(예: return null)

예를 들어:

function setAnimation (callback) {
    // 1.尝试标准方式
    if (window.requestAnimationFrame) {                 // standard
        return requestAnimationFrame(callback);
    }
    // 2.尝试特定浏览器的实现方式
    else if (window.mozRequestAnimationFrame) {         // Firefox
        return mozRequestAnimationFrame(callback);
    }
    else if (window.webkitRequestAnimationFrame) {      //WebKit
        return webkitRequestAnimationFrame(callback);
    }
    else if (window.oRequestAnimationFrame) {           // Opera
        return window.oRequestAnimationFrame(callback);
    }
    else if (window.msRequestAnimationFrame) {          // IE
        return window.msRequestAnimationFrame(callback);
    }
    // 3.不支持的逻辑反馈
    else {
        return setTimeout(callback, 0);
    }
}

###3.기능 추론 철저 금지

한 기능으로 다른 기능을 추론할 수 없음. 오리와 비슷하다고 해서 반드시 오리와 같이 꽥꽥거리는 것은 아님

###4.브라우저 추론 철저 금지

기능으로 브라우저를 추론하지 마세요. 대표적인 예:

if (document.all) { // IE
    // ...
}

이렇게 하는 것은 옳지 않음. 특징으로 어떤 것을 설명하려고 하면 안 됨. 조건이 너무 적거나 너무 많으면 설명이 정확하지 않을 수 있음

###5.도대체 어느 것을 사용해야 하는가?

직접 기능 감지를 사용하고, 안 되면 UA 감지를 사용하세요. 추론은 전혀 고려하지 마세요. 추론을 사용할 어떤 이유도 없음

###참고 자료

  • 《Maintainable JavaScript》

コメント

コメントはまだありません

コメントを書く