JavaScript秘密ガーデン-scope,namespace,constructor,equality and comparisons

70947 ワード

  • JavaScript Garden-原文
  • JavaScript Garden-中国語翻訳

  • 役割ドメインとネーミングスペース


    JavaScriptはカッコで作成されたコードセグメントのペアをサポートしていますが、ブロックレベルの役割ドメインはサポートされていません.関数の役割ドメインのみをサポートします.
    function test() { //      
       
    for(var i = 0; i < 10; i++) { //
           
    // count
       
    }
        console
    .log(i); // 10
    }

    注:{...}は、付与文ではなくreturn式または関数パラメータの場合、オブジェクトとしての字面構文解析ではなく、コードセグメント解析として機能します.自動セミコロン挿入を考慮すると、これはいくつかの気づきにくいエラーを引き起こす可能性があります.
    訳者注:returnオブジェクトの左かっことreturnが1行にないとエラーが発生します.
    //    :     undefined
    function add(a, b) {
           
    return
                    a
    + b;
    }
    console
    .log(add(1, 2));

    JavaScriptには明示的なネーミングスペース定義がありません.これは、すべてのオブジェクトがグローバル共有ネーミングスペースの下に定義されていることを意味します.
    1つの変数を参照するたびに、JavaScriptはこの変数が見つかるまで役割ドメイン全体を上に移動します.グローバル役割ドメインに到達してもこの変数が見つからない場合、ReferenceError異常が放出されます.

    暗黙的グローバル変数(The bane of global variables)

    // script A
    foo
    = '42';

    // script B
    var foo = '42'

    上の2つのスクリプトの効果は異なります.スクリプトAは、グローバル役割ドメイン内で変数fooを定義し、スクリプトBは、現在の役割ドメイン内で変数fooを定義する.
    さらに、上記の効果はまったく異なり、varを使用して変数を宣言しないと、暗黙的なグローバル変数が生成されることを強調します.
    //      
    var foo = 42;
    function test() {
       
    //
        foo
    = 21;
    }
    test
    ();
    foo
    ; // 21

    関数test内でvarキーワード宣言foo変数を使用しないと、外部の同名変数が上書きされます.最初は大きな問題ではないように見えますが、数千行のコードがある場合、varを使用して変数を宣言しないと、追跡しにくいBUGをもたらします.
    //      
    var items = [/* some list */];
    for(var i = 0; i < 10; i++) {
        subLoop
    ();
    }

    function subLoop() {
       
    // subLoop
       
    for(i = 0; i < 10; i++) { // var
           
    // do amazing stuff!
       
    }
    }

    外部ループは、subLoopがグローバル変数subLoopをカバーするため、iが最初に呼び出された後に終了する.2番目のforサイクルでvar宣言変数を使用すると、このようなエラーを回避できます.変数を宣言するときは、外部の役割ドメインに影響を及ぼすことが望ましい場合を除き、varのキーワードを絶対に漏らさないでください.

    ローカル変数(Local variables)


    JavaScriptのローカル変数は、2つの方法でのみ宣言できます.1つは関数#カンスウ#パラメータとして、もう1つはvarキーワードで宣言されます.
    //     
    var foo = 1;
    var bar = 2;
    var i = 2;

    function test(i) {
       
    // test
        i
    = 5;

       
    var foo = 3;
        bar
    = 4;
    }
    test
    (10);
    fooおよびiは、関数test内のローカル変数であり、barに対する付与値は、グローバル役割ドメイン内の同名変数を上書きする.

    変数宣言の移動(Hosting)


    JavaScriptは変数宣言を上げます.これは、var式およびfunction宣言が、現在の役割ドメインの上部に昇格することを意味する.
    bar();
    var bar = function() {};
    var someValue = 42;

    test
    ();
    function test(data) {
       
    if (false) {
            goo
    = 1;

       
    } else {
           
    var goo = 2;
       
    }
       
    for(var i = 0; i < 100; i++) {
           
    var e = data[i];
       
    }
    }

    上のコードは実行前に変換されます.JavaScriptは、var式とfunction宣言を現在の役割ドメインの上部に引き上げます.
    // var          
    var bar, someValue; // 'undefined'

    //
    function test(data) {
       
    var goo, i, e; // ,
       
    if (false) {
            goo
    = 1;

       
    } else {
            goo
    = 2;
       
    }
       
    for(i = 0; i < 100; i++) {
            e
    = data[i];
       
    }
    }

    bar
    (); // :TypeError, bar 'undefined'
    someValue
    = 42; // (hoisting)
    bar
    = function() {};

    test
    ();

    ブロックレベルの役割ドメインがないと、var式がサイクル内から外部に移動するだけでなく、if式の一部がより理解しにくくなる.
    従来のコードでは、if式はすべての変数gooを修正したように見えますが、実際にはリフトルールが適用された後、ローカル変数を修正しています.
    アップグレードルールの知識がなければ、次のコードは異常ReferenceErrorを投げ出すように見えます.
    //    SomeImportantThing         
    if (!SomeImportantThing) {
       
    var SomeImportantThing = {};
    }

    実際には、var式がグローバル役割ドメインの上部に昇格するため、上記のコードは正常に動作します.
    var SomeImportantThing;

    // , SomeImportantThing,

    //
    if (!SomeImportantThing) {
       
    SomeImportantThing = {};
    }

    Nettuts+のウェブサイトにはhoistingを紹介する文章があり、そのコードは啓発的です.
    //    :   Nettuts+      ,       JavaScript          
    var myvar = 'my value';  

    (function() {  
            alert
    (myvar); // undefined  
           
    var myvar = 'local value';  
    })();  

    名前解析順序(Name resolution order)


    JavaScriptのすべての役割ドメイン(グローバル役割ドメインを含む)には、現在のオブジェクトを指す特別な名前`this`があります.
    関数の役割ドメインには、関数に渡されるパラメータを含むデフォルトの変数`arguments`もあります.
    たとえば、関数内のfoo変数にアクセスすると、JavaScriptは次の順序で検索されます.
  • 現在の役割ドメイン内にvar fooの定義があるかどうか.
  • 関数形式パラメータにfoo名が使用されているかどうか.
  • 関数自体をfooと呼ぶかどうか.
  • は、前のレベルの役割ドメインに遡り、#1から再開する.

  • 注:カスタムargumentsパラメータは、オリジナルのargumentsオブジェクトの作成を阻止します.

    ネームスペース(Namespaces)


    グローバルな役割ドメインが1つしかないため、一般的なエラーはネーミング競合です.JavaScriptでは、匿名パッケージで簡単に解決できます.
    (function() {
       
    // ( : )

        window
    .foo = function() {
           
    // ,
       
    };

    })(); //

    匿名関数は式#シキ#と考えられる.したがって、呼び出し性のために、まず実行されます(evaluated).
    ( //             
    function() {}
    ) //
    () // ,

    次の2つの方法で文法が異なりますが、効果は同じです.
    //       
    +function(){}();
    (function(){}());

    結論(In conclusion)


    匿名パッケージ(自己実行匿名関数)を使用して、ネーミングスペースを作成することをお勧めします.これにより,ネーミング競合を防止するだけでなく,プログラムのモジュール化にも有利である.
    また,グローバル変数の使用は悪い習慣とされている.このようなコードは、エラーが発生し、高いメンテナンスコストをもたらす傾向があります.

    コンストラクタ


    JavaScriptのコンストラクション関数は、他の言語のコンストラクション関数とは異なります.newキーワード方式で呼び出された関数は、いずれも構築関数とみなされる.
    コンストラクション関数の内部、すなわち呼び出された関数内のthisは、新しく作成されたオブジェクトObjectを指す.この新しく作成されたオブジェクトの`prototype`は、コンストラクション関数のprototypeに向けられる.
    呼び出された関数に明示的なreturn式がない場合、暗黙的にはthisオブジェクト、すなわち新しく作成されたオブジェクトが返されます.
    function Foo() {
       
    this.bla = 1;
    }

    Foo.prototype.test = function() {
        console
    .log(this.bla);
    };

    var test = new Foo();

    上記のコードは、Fooをコンストラクション関数として呼び出し、新しく作成されたオブジェクトのprototypeFoo.prototypeに設定します.
    明示的なreturn式は、返される結果に影響しますが、返されるオブジェクトに限定されます.
    function Bar() {
       
    return 2;
    }
    new Bar(); //

    // :new Bar() , 2。
    // new Bar().constructor === Bar
    // ,
    // function Bar() {
    //    return new Number(2);
    // }
    // new Bar().constructor === Number


    function Test() {
       
    this.value = 2;

       
    return {
            foo
    : 1
       
    };
    }
    new Test(); //
    // : , new
    // (new Test()).value undefined, (new Test()).foo === 1。
    newが欠落している場合、関数は新しく作成されたオブジェクトを返しません.
    function Foo() {
       
    this.bla = 1; //
    }
    Foo(); // undefined

    上記の例では、場合によっては正常に動作することもありますが、JavaScriptの`this`の動作原理のため、ここでのthisはグローバルオブジェクトを指します.

    ファクトリモード(Factories)

    newキーワードを使用しないためには、コンストラクション関数は明示的に値を返さなければなりません.
    function Bar() {
       
    var value = 1;
       
    return {
            method
    : function() {
               
    return value;
           
    }
       
    }
    }
    Bar.prototype = {
        foo
    : function() {}
    };

    new Bar();
    Bar();

    上記の2つのBar関数の呼び出しに対して返される値は完全に同じであり、新しく作成されたmethod属性を持つオブジェクトが返され、実際にはここでクローズドパッケージが作成されます.
    また、new Bar()は、戻りオブジェクトのプロトタイプを変更することはありません(すなわち、戻りオブジェクトのプロトタイプはBar.prototypeを指しません).コンストラクション関数のプロトタイプは作成したばかりの新しいオブジェクトに指し示されるため、ここのBarはこの新しいオブジェクトを返さなかった(訳者注:methodのプロパティを含むカスタムオブジェクトを返した).
    上記の例では、newキーワードを使用するか使用しないかには機能的な違いはない.
    //    :                Bar        
    var bar1 = new Bar();
    typeof(bar1.method); // "function"
    typeof(bar1.foo); // "undefined"

    var bar2 = Bar();
    typeof(bar2.method); // "function"
    typeof(bar2.foo); // "undefined"

    ファクトリモードによる新規オブジェクトの作成(Creating new objects via factories)


    私たちがよく耳にする忠告は、newキーワードを使用して関数を呼び出さないことです.使用を忘れるとエラーになるからです.
    新しいオブジェクトを作成するには、ファクトリメソッドを作成し、メソッド内に新しいオブジェクトを構築します.
    function Foo() {
       
    var obj = {};
        obj
    .value = 'blub';

       
    var private = 2;
        obj
    .someMethod = function(value) {
           
    this.value = value;
       
    }

        obj
    .getPrivate = function() {
           
    return private;
       
    }
       
    return obj;
    }

    上記の方式はnewの呼び出し方式よりもエラーが発生しにくく、プライベート変数による利便性を十分に利用できるが、それに伴ういくつかの悪い点である.
  • は、新しく作成したオブジェクトがプロトタイプ上のメソッドを共有できないため、より多くのメモリを消費します.
  • 継承を実現するために、ファクトリメソッドは、別のオブジェクトからすべてのプロパティをコピーするか、新しい作成オブジェクトのプロトタイプとしてオブジェクトを使用する必要があります.
  • がプロトタイプチェーンを放棄したのは、newの漏れを防ぐためだけで、言語そのものの思想に反しているようだ.

  • まとめ(In conclusion)

    newのキーワードを漏らすと問題になる可能性がありますが、プロトタイプチェーンの使用を放棄する言い訳ではありません.最終的にどの方法を使用するかは、アプリケーションのニーズに応じて、コードの書くスタイルを選択し、堅持することが最も重要です.

    同等と比較


    JavaScriptでは、2つの値が等しいかどうかを判断する方法が2つあります.

    イコールオペレータ


    イコールオペレータは2つの等号で構成されています:==JavaScriptは弱い言語です.これは、オペレータが2つの値を比較するために強制的なタイプ変換を行うことを意味します.
    ""           ==   "0"           // false
    0            ==   ""            // true
    0            ==   "0"           // true
    false        ==   "false"       // false
    false        ==   "0"           // true
    false        ==   undefined     // false
    false        ==   null          // false
    null         ==   undefined     // true
    " \t\r
    "
       ==   0             // true

    上記の表は強いタイプの変換を示しており、==を用いることがプログラミング習慣が悪い主な原因として広く考えられているが、その複雑な変換規則のため、追跡しにくい問題を招く.
    また、強制タイプ変換は、文字列が配列と比較するために事前に数値に強制的に変換されるなど、パフォーマンスの消費をもたらします.

    厳密にはオペレータ(The strict equals operator)に等しい


    厳密なイコールオペレータは、===という3つの等号で構成されています.
    通常はオペレータに等しくしたくないし、厳密にはオペレータに等しくて強制タイプ変換は行われません.
    ""           ===   "0"           // false
    0            ===   ""            // false
    0            ===   "0"           // false
    false        ===   "false"       // false
    false        ===   "0"           // false
    false        ===   undefined     // false
    false        ===   null          // false
    null         ===   undefined     // false
    " \t\r
    "
       ===   0             // false

    上記の結果はより明確で、コードの分析に有利である.2つのオペランドのタイプが異なる場合、等しくないことは間違いなく、パフォーマンスの向上にも役立ちます.

    オブジェクトの比較(Comparing objects)

    =====のオペレータはすべてオペレータに等しいが、そのうちの1つのオペランドがオブジェクトである場合、動作は異なる.
    {} === {};                   // false
    new String('foo') === 'foo'; // false
    new Number(10) === 10;       // false
    var foo = {};
    foo
    === foo;                 // true

    ここで、オペレータが比較するのは、値が等しいかどうかではなく、同じアイデンティティに属しているかどうかです.すなわち,オブジェクトの同じインスタンスのみが等しいと考えられる.これはPythonのisとCのポインタの比較に似ています.

    結論(In conclusion)


    厳密なイコールオペレータの使用を強くお勧めします.タイプが変換する必要がある場合は、言語自体の複雑な強制変換ルールではなく、比較前の明示的の変換を使用する必要があります.
    http://www.cnblogs.com/sanshi/archive/2011/03/22/1991987.html