『メンテナンスできるjavascriptを書く』のノートを読みます.

36683 ワード

第一章基本的なフォーマット
  • インデント層:おすすめtab:4;
  • 改行:演算子の後で改行し、2行目に2つのインデントを追加します.
  • // Good: Break after operator, following line indented two levels
    callAFunction(document, element, window, "some string value", true, 123,
            navigator);
    
    // Bad: Following line indented only one level
    callAFunction(document, element, window, "some string value", true, 123,
        navigator);
    
    // Bad: Break before operator
    callAFunction(document, element, window, "some string value", true, 123
            , navigator);
  • 空き行を追加する:方法の間;方法の局所変数と最初のステートメントの間に.複数行または単一行のコメントの前に.方法内の論理セグメントを空の行に挿入すると、読み取り可能性が向上する(例えば、for、if).
  • // good
    if (wl && wl.length) {
    
        for (i = 0, l = wl.length; i < l; ++i) {
            p = wl[i];
            type = Y.Lang.type(r[p]);
    
            if (s.hasOwnProperty(p)) {
    
                if (merge && type == 'object') {
                    Y.mix(r[p], s[p]);
                } else if (ov || !(p in r)) {
                    r[p] = s[p];
                }
            }
        }
    }
  • 定数:大文字でアンダースコアを付けた方式(var MAXUCOUNT);
  • var MAX_COUNT = 10;
    var URL = "http://www.nczonline.net/";
    
    if (count < MAX_COUNT) {
        doSomething();
    }
  • 表示の構築配列とオブジェクトを推奨しない(new方式を採用する);
  • 文字列:二重引用符の使用を推奨します. 
  • // Good
    var name = "Nicholas";
    
    // Bad: Single quotes
    var name = 'Nicholas';
    
    // Bad: Wrapping to second line
    var longString = "Here's the story, of a man \
    named Brady.";
  • 「\」を使って文字列を接続しないで、「+」を使うことをおすすめします.
  • 第二章コメント
  • 注釈:前の行が空いていて、次の行が正しい;プログラムコードが比較的明瞭な場合は、コメントの追加は推奨されません.間違っていると思われるコードは勝手に修正しないように注釈してもいいです. 
  • /*
     *       (  )
     *        ( )
     */
    第三章文と表現
  • ブロック文間のスペースは、以下のような
  • if (condition) {
        doSomething();
    }
  • for inを使って配列を循環しないでください.indexは文字列形式であり、数字形式ではなく、問題が発生しやすいからです.オブジェクトを循環させるために使用できます.
  • 第四章変数、関数、演算子
  • 厳格モード:必要なところだけを追加して、全体的に使用しないようにしてください.
  • //       
    function fn() {
        "use strict";
        //  
    }
    
    //  
    (function() {
        "use strict";
        function fn1() {
    
        }
        function fn2() {
            
        }
    })();
  • 等しい:「==」と「!==」を使うことを推奨します.
  • eval、Functionを適用しないようにし、setTimeoutを与えないようにし、set Intervalを文字列に入力しないようにする.
  • 第五章UIの松結合
  • css表現expressionの使用を避ける;
  • jsでスタイルを変更する場合は、直接使うのではなく、クラス名を追加または削除する方式を採用する.style.co lor.style.cssText.
  • // Bad
    element.style.color = "red";
    element.style.left = "10px";
    element.style.top = "100px";
    element.style.visibility = "visible";
    
    element.style.cssText = "color: red; left: 10px; top: 100px; visibility: hidden";
    
    //  
    .reveal {
        color: red;
        left: 10px;
        top: 100px;
        visibility: visible;
    }
    
    // Good - Native
    element.className += " reveal";
    
    // Good - HTML5
    element.classList.add("reveal");
    
    // Good - YUI
    Y.one(element).addClass("reveal");
    
    // Good - jQuery
    $(element).addClass("reveal");
    
    // Good - Dojo
    dojo.addClass(element, "reveal");
  • HTMLではjavascriptを使用しない;例えばHTMLに埋め込まれたオンロック
  • 
    
  • JavaScriptからHTMLを抜き出す.
  • // Bad
    var div = document.getElemenetById("my-div"); div.innerHTML = "

    Erroe

    ";
  • 解決方法は3つあります.1、サーバーからロードします.
  • function loadDialog(name, oncomplete) {
        var xhr = new XMLHttpRequest();
        xhr.open("get", "/js/dialog/" + name, true);
        xhr.onreadystatechange = function() {
            if (xhr.readyState == 4 && xhr.status == 200) {
                var div = document.getElementById("dlg-holder");
                div.innerHTML = xhr.responseText;
                oncomplete();
            } else {
                // handle error
            }
        };
        xhr.send(null);
    }
  • 2、簡易クライアントテンプレートを使用する.
  • //   
    
  • %s
  • function sprintf(text){
    var i=1、args=argments;
    return text.replace(/%s/g,function()
    return(i<args.length)?args[i+]:“”;

    )
    //usage
    var reult=sprintf(templateText、「/item/4」、「Fourth item」);
  • 3、複合クライアントテンプレート;例えば、Handlebars
  • //      script  
    
        <li><a href="{{url}}">{{text}}</a></li>
    
    
    function addItem(url, text) {
        var mylist = document.getElementById("mylist"),
        script = document.getElementById("list-item"),
        templateText = script.text,
        template = Handlebars.compile(script.text),
        div = document.createElement("div"),
        result;
    
        result = template({
            text: text,
            url: url
        });
    
        div.innerHTML = result;
        list.appendChild(div.firstChild);
    }
    
    //   
    addItem("/item/4", "Fourth item");
    第六章グローバル変数の使用を避ける
  • グローバル変数の使用を避けるため、問題がある:命名衝突;コードの脆弱性テスト困難
  • 意外なグローバル変数:varを忘れました.  解決方法:JSLintまたはJSHintを使用する.
  • シングルグローバル変数:YUIがYUIグローバル変数を定義しています.jQueryは二つのグローバル変数を定義しました.
  • 名前空間:例えばYUI.イベントでのあらゆる方法はイベントに関連しています.
  • モジュール:AMDモジュールとYUIモジュール;
  • // AMD    
    define("my-books", [ "dependency1", "dependency2" ],
         function(dependency1, dependency2) {
              var Books = {};
              Books.MaintainableJavaScript = {
              author: "Nicholas C. Zakas"
         };
        return Books;
      });
  • ゼログローバル変数:内部コードが外部コードと無関係であることが前提です.
  •     (function(win) {
            var doc = win.document;
    
        })(window)
    第七章事件処理
  • 事件処理の典型的な使い方
  • //    
    function handleClick(event) {
        var popup = document.getElementById("popup");
        popup.style.left = event.clientX + "px";
        popup.style.top = event.clientY + "px";
        popup.className = "reveal";
    }
    
    // addListener() from Chapter 7
    addListener(element, "click", handleClick);
  • ルール1:隔離応用ロジック
  • //    - separate application logic
    var MyApplication = {
        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 = "reveal";
        }
    };
    
    addListener(element, "click", function(event) {
        MyApplication.handleClick(event);
    });
  • ルール2:イベントの対象者は配布しない;上記の問題はイベントが無制限に配布されていることです.匿名イベントハンドラ関数から別の関数に、もう一つの関数に移動します.最善の方法:イベントハンドラをイベントオブジェクトでイベントを処理し、必要なすべてのデータをアプリケーションロジックに渡す.
  • // Good
    var MyApplication = {
        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 = y + "px";
            popup.className = "reveal";
        }
    };
    addListener(element, "click", function(event) {
        MyApplication.handleClick(event); // this is okay
    });
  • イベントを処理する際には、イベントハンドラをイベントオブジェクトに接触する唯一の関数にした方が良い.
  • // Good
    var MyApplication = {
        handleClick: function(event) {
            // assume DOM Level 2 events support
            event.preventDefault();
            event.stopPropagation();
            // pass to application logic
            this.showPopup(event.clientX, event.clientY);
        },
        showPopup: function(x, y) {
            var popup = document.getElementById("popup");
            popup.style.left = x + "px";
            popup.style.top = y + "px";
            popup.className = "reveal";
        }
    };
    addListener(element, "click", function(event) {
        MyApplication.handleClick(event); // this is okay
    });
    第八章「空の比較」を避ける.
  • 以下は悪い例です.nullとは同じではないことが多いです.数字、文字列、オブジェクトなどを含めて、隠しエラーをもたらすことがあります.期待の値がnullの場合だけ、nullと比較することができます.
  • var Controller = {
        process: function(items) {
            if (items !== null) { // Bad
                items.sort();
                items.forEach(function(item) {
                    // do something
                });
            }
        }
    };
  • 基本タイプがtypeofを使用していることを検出し、引用タイプがinstance ofを使用しているほか、対象特有の方法や属性が存在するかどうかを判断して、例えばarray.sort()、正則のtestメソッドを使用している.共通の方法は、他のタイプが類似していることである.
  • function isArray(value) {
        return Object.prototype.toString.call(value) === "[object Array]";
    }
  • 属性を検出し、属性を判断する最も良い方法はin
  • //   ,     0, "", false, null   undefined 
    if (object[propertyName]) {
        // do something
    }
    
    // Bad: Compare against null
    if (object[propertyName] != null) {
        // do something
    }
    
    // Bad: Compare against undefined
    if (object[propertyName] != undefined) {
        / do something
    }
    
    var object = {
        count: 0,
        related: null
    };
    
    // Good
    if ("count" in object) {
        // this executes
    }
    if ("related" in object) {
        // this executes
    }
    
    //                        hasOwnProperty()
    // Good for all non-DOM objects
    if (object.hasOwnProperty("related")) {
        //this executes
    }
    // Good when you're not sure
    if ("hasOwnProperty" in object && object.hasOwnProperty("related")) {
        //this executes
    }
     第九章配置データをコードから分離する
  • 配置データは何ですか?JavaScriptコードで変更される可能性があります.例えば、urlは、ユーザーに見せる文字列、重複する値、設定、変化の可能性がある値を設定します.以下は良くない例です.invalid value、hrefの値、selctedは配置データです.
  • // Configuration data embedded in code
    function validate(value) {
        if (!value) {
            alert("Invalid value");
            location.href = "/errors/invalid.php";
        }
    }
    function toggleSelected(element) {
        if (hasClass(element, "selected")) {
            removeClass(element, "selected");
        } else {
            addClass(element, "selected");
        }
    }
  •  抽出配置データ
  • // Configuration data externalized
    var config = {
        MSG_INVALID_VALUE: "Invalid value",
        URL_INVALID: "/errors/invalid.php",
        CSS_SELECTED: "selected"
    };
    function validate(value) {
        if (!value) {
            alert(config.MSG_INVALID_VALUE);
            location.href = config.URL_INVALID;
        }
    }
    function toggleSelected(element) {
        if (hasClass(element, config.CSS_SELECTED)) {
            removeClass(element, config.CSS_SELECTED);
        } else {
            addClass(element, config.CSS_SELECTED);
        }
    }
     第十章カスタムエラーを打ち出す
  • エラーメッセージに関数名が含まれていることと、関数が失敗した理由を紹介します.
  • function getDivs(element) {
        if (element && element.getElementsByTagName) {
            return element.getElementsByTagName("div");
        } else {
            throw new Error("getDivs(): Argument must be a DOM element.");
        }
    }
  •  いつエラーを出しますか?コードの中でどの部分が特定の場合に最適で失敗につながるかを識別し、どの部分だけエラーを出すかがポイントです.私たちの目的はエラー防止ではなく、エラーが発生した時にもっと調整しやすいです.
  • //
    function addClass(element, className) {
        if (!element || typeof element.className != "string") {
            throw new Error("addClass(): First argument must be a DOM element.");
        }
        if (typeof className != "string") {
            throw new Error("addClass(): Second argument must be a string.");
        }
        element.className += " " + className;
    }
    //             null                       
    function addClass(element, className) {
        if (!element || typeof element.className != "string") {
            throw new Error("addClass(): First argument must be a DOM element.");
        }
        element.className += " " + className;
    }
  •  try catch文を使って、tryでエラーが発生したら、すぐにcatch文にジャンプして、エラーの対象に入ります.finallyモジュールを追加してもいいです.エラーが発生しても発生しなくても実行します.
  • try {
        somethingThatMightCauseAnError();
    } catch (ex) {
        handleError(ex);
    } finally {
        continueDoingOtherStuff();
    }
     第十一章はあなたの相手ではないから動かないでください.
  • これらのオブジェクトは、元のオブジェクト(Object、Aray)、DOMオブジェクト(document)、ブラウザオブジェクトモデル(BOM)オブジェクト(window)がすでに存在しているので、彼らを修正しないでください.クラスの対象.彼らに対する原則としては、上書きしない方法、追加しない方法、削除しない方法、以下のような良くない例があります.
  • document.getElementById = function() {
        return null; // talk about confusing
    };
    
    document._originalGetElementById = document.getElementById;
    document.getElementById = function(id) {
        if (id == "window") {
            return window;
        } else {
            return document._originalGetElementById(id);
        }
    };
    
    document.sayImAwesome = function() {
        alert("You're awesome.");
    };
    
    Array.prototype.reverseSort = function() {
        return this.sort().reverse();
    };
    
    YUI.doSomething = function() {
        // code
    };
    
    document.getElementById = null;
     より良い方法:三つのコードがあります.
    //       
    var person = {
        name: "Nicholas",
        sayName: function() {
        alert(this.name);
    }
    };
    var myPerson = Object.create(person);
    myPerson.sayName(); // pops up "Nicholas"
    
    //       
    function MyError(message) {
        this.message = message;
    }
    MyError.prototype = new Error();
    var error = new MyError("Something bad happened.");
    console.log(error instanceof Error); // true
    console.log(error instanceof MyError); // true
    
    //
    function DOMWrapper(element) {
        this.element = element;
    }
    DOMWrapper.prototype.addClass = function(className) {
        this.element.className += " " + className;
    };
    DOMWrapper.prototype.remove = function() {
        this.element.parentNode.removeChild(this.element);
    };
    
    // Usage
    var wrapper = new DOMWrapper(document.getElementById("my-div"));
    
    // add a CSS class
    wrapper.addClass("selected");
    
    // remove the element
    wrapper.remove();
     第十二章ブラウザの嗅ぎ込み
  • User agent検出
  • // Bad
    if (navigator.userAgent.indexOf("MSIE") > -1) {
        // it's Internet Explorer
    } else {
        // it's not
    }
    
    // good       IE 9     ,      IE8     
    if (isInternetExplorer8OrEarlier) {
        // handle IE8 and earlier
    } else {
        // handle all other browsers
    }
  • 特性検出、推奨使用特性検出
  • // Bad
    if (navigator.userAgent.indexOf("MSIE 7") > -1) {
        // do something
    }
    
    // Good
    if (document.getElementById) {
        // do something
    }
  • 特性推定回避
  • // Bad - uses feature inference
    //                    
    function getById (id) {
        var element = null;
        if (document.getElementsByTagName) { // DOM
            element = document.getElementById(id);
        } else if (window.ActiveXObject) { // IE
            element = document.all[id];
        } else { // Netscape <= 4
            element = document.layers[id];
        }
        return element;
    }
  • ブラウザからの推測を避けるために、何らかの方法で存在していると判断できないブラウザ
  • // Bad
    if (document.all) { // IE
        id = document.uniqueID;
    } else {
        id = Math.random();
    }
    
    var isIE = !!document.all;
    
    var isIE = !!document.all && document.uniqueID;
    後の章は別の部分です.例えば、ファイルリスト、自動化など、まとめにくいです.
    もしいいと思いましたら、下のオススメをクリックしてください.読んでくれてありがとうございます.