あなたが知らないかもしれないが、JavaScriptの奇術がいくつかあります.

10024 ワード

ここでは以前各種の書籍や文章の中に出てきたJSのテクニックを記録してみます.みんなに共有したり、自分で調べたりします.また、これらの技術を発見して共有してくれた先輩や大牛たちに感謝します.
1、objの属性を遍歴して配列に行く
  var a=[];
  for(a[a.length] in obj);
  return a;
一見して蒙古に見えるかもしれませんが、よく分析すると理解に苦しむことはありません.よく使われるのはfor(var key in obj)です.ここでkeyは最初はundefinedで、a[a.length]も全体的にundefinedです.だから、両者は等価です.forサイクルの中で、objの属性は順番にkeyに与えられます.同様に、a[a.length]にも順次与えられます.ここでlengthはずっと変化しています.配列の各要素に巧みに割り当てられます.
2、文字列を繰り返す(abc=>abcabcなど)
     function repeat(target,n){
          return (new Array(n+1).join(target));
     }
改良バージョン:
     function repeat(target,n){
          return Array.prototype.join.call(
          {length:n+1},target);//       length     ,            ,          ,           length     
     }
新しい配列ではなく、length属性を持つオブジェクトで置換し、配列のjoinメソッドを呼び出して、パフォーマンスを向上させます.
再改善:
     var repeat=(function(){
          var join=Array.prototype.join,obj={};
          return function(target,n){
               obj.length=n+1;
               return join.call(obj,target);
          }
     })();
クローズドバックを利用してオブジェクトとジョイン方法をキャッシュし、毎回新しいオブジェクトを作成したり、探したりする必要はありません.
3、forサイクルの中で、第二項がfalseである時にサイクルが終了します.ここでは必ずしも比較が存在しなくて、直接的に値を賦与することができます.undefinedのような値を賦与すれば、boot値も偽になります.だから、遍歴行列が書いてもいいです.
    for(var i=arr.length,element;element=arr[—-i];){…}
ここで、二番目の項目はarr[--i]であって、arr[i-]ではない.後者の場合はundefinedとなり、循環体、またはfor(var i=0,element;element=arr[i+]){
4、NaNはJSの中で唯一自分の値に等しくないので、一つの変数が本当にNaN:aであるかどうかを判断するのに使えます.==a.
5、「<」、「+」などの演算子は、シンボルの両側の表現をvalueOfに強制的に実行して比較しますので、両方が関数やオブジェクトであり、またそのオブジェクトのvalueOf方法を書き換えると、自動的に両方の方法が実行されます.例えば:
    var a={valueOf:function(){console.log(“aaa”);}},b={valueOf:function(){console.log(“bbb”);}};
    a<b;//   :aaa;bbb;false;
6、JSは自動的にセミコロンを挿入する能力を備えていますが、自動的にセミコロンを挿入するのは万能ではなく、三つのルールがあります.
1)「}」マークの前、1つ以上の改行の後、およびプログラム入力の最後だけを挿入します.
2)セミコロンは、その後の入力フラグが解析できない時だけ挿入されます.
     この点が重要です.例えば:
     a=b
     (f()
     a=bの後に自動的にセミコロンを挿入することはありません.a=b(f()は解析されますので、像"(",","+","-","/",","/"は、前の行は自動的に挿入されないかもしれないので、注意が必要です.
また、解析エラーは発生しませんが、JSはやはりセミコロンを強制的に挿入します.いわゆるJS文法制限発生式です.2文字の間で改行は許されません.一番危険なのは、return文です.
return 
{}
強制的に挿入されます.
return;
{}
似たようなものがあります.
throw文、
表示ラベル付きのbreak活着contine文、
後の自動増減演算子
3)セミコロンは、セパレータとしてfor循環空文の先頭に自動的に挿入されません.
だから、一番いい方法は自分のjsファイルの一番最初に防御的に挿入することです.
7、クローズド.クローズドを理解するには3つの基本的な事実を習得する必要があります.
(1)JSは、現在の関数で意外に定義されている変数を参照することができます.
(2)外部関数が戻っても、現在の関数は外部関数で定義されている変数を参照できます.JSの関数値は呼び出し時より必要なコードを含むためです.
(3)閉じたパケットは、外部変数の値を更新することができます.これは、値のコピーではなく、外部変数の参照が格納されているからです.
function box(){
     var val=undefined;
     return {
          set:function(x){val=x;},
          get:function(){return val;}
     };
}
var b=box();
b.get();//“undefined”
b.set(5);
b.get();//5
この点は非常に重要です.例えば、関数のfor循環体内では、クローズドバックまたはクローズドのforループをとるカウンタ値が返されます.このクローズドが取ったのは、forループが終了した時のiの最終値です.なぜなら、クローズドバックが格納されているのは、その参照であって、当時の値のコピーではありません.
8、JSはブロックレベルのスコープがないので、関数のスコープには通常関数内部のすべての変数が結合されています.つまり、関数の最初に宣言されたものに相当します.例外としてtry/catchの変数はブロックレベルで、try/catchブロックにのみ属します.
9、ご存知のように、関数内の声明関数は可能ですが、関数内の部分ブロックで声明したら、問題が発生する可能性があります.
function f(){return “global”;}
function test(x){
     function f(){return “local”}
     var result=[];
     if(x){
          result.push(f());
     }
     result.push(f());
     return result;
}
test(true);//[“local”,”local”]
test(false);//[“local”]
関数をifブロックに宣言します.
function f(){return “global”;}
function test(x){
     var result=[];
     if(x){
          function f(){return “local”}
          result.push(f());
     }
     result.push(f());
     return result;
} 
test(true);//?
test(false);//?
結果はどうなりますか?理論的には、JSはブロックレベルの作用領域がないので、f()の作用域はtest関数全体ですので、前回の出力と同じで、全部「local」であるべきだと合理的に推測しています.しかし、すべてのJSが環境を実行しているわけではなく、fを含むコードブロックを実行して関数fを条件付きで結びつける場合もあります.(バインディングとは、この変数をその最も近い作用領域に結びつけることを意味し、割当値はコードが実際にそのステップを実行するときに行われる).
したがって、最も良い方法は、ネスト関数を宣言する場合、その最外層声明の中にあるか、関数を宣言しないで、var宣言と関数表現を使用して実現することです.
function f(){return “global”;}
function test(x){
     var result=[];
     if(x){
          var g=function(){return “local”}
          result.push(g());
     }
     result.push(f());
     return result;
}  
10、jsで辞書を作成する場合、オブジェクトを利用する方式(JSオブジェクトの核心は文字列属性名と属性値のマッピングテーブルですので)辞書の属性値を取得する時はハスOwnPropertyを使ってもいいです.for inで遍歴すると、対象自体を遍歴するだけでなく、プロトタイプも含めて他のところでObjectのプロトタイプを汚染したら、for inは予期せぬ結果を生みます.この時はハスOwnPropertyを使ってまず対象が検出されるかもしれません.属性を含まないでプロトタイプの汚染を避けますが、もっと極端な場合はハスOwnPropertyというプロトタイプの方法さえ汚染される可能性があります.プロトタイプの汚染を避ける方法は辞書の対象を作る時にObject.create(null)を使います.完全な空のオブジェクトを作成します.このオブジェクトにはプロトタイプがありません.この方法はES 5です.この方法がない場合は、辞書クラスを作成して、そして辞書クラスの中に整列したセットを配列で保存して、自分でこのセットを維持してください.
11、JSのクラスの配列オブジェクトは、配列のほとんどのプロトタイプ方法を楽しむことができます.例えば、mapなど、クラスの配列オブジェクトは、2つの条件を満たすオブジェクトを指します.1つは、合理的な範囲の値を持つlength属性、2つはlength属性がオブジェクトの最大インデックスより大きいので、インデックスは合理的な範囲の証明書です.文字列は対象のkeyを表します.しかし、配列のプロトタイプの1つです.法contactはクラスの配列オブジェクトに呼び出されないので、まず[].slite.calでクラスの配列オブジェクトを本物の配列に変換する必要があります.例えば[].slite.cal.
12、すべての時に継承が必要ではなく、継承も完璧ではないです.時々彼より解決できる問題を作ることができます.特にレベル関係がそんなにはっきりしていない時は、構造タイプを多く使うべきです.構造タイプでフレキシブルなオブジェクトインターフェースを設計する場合、クラス工場を作ってクラスのインスタンスに戻る必要はなく、直接にオブジェクトに戻ります.
SomeObj.someWidget=function(opts){
     return {
          a:blabla,
          b:function(){...},
          c:blabla
     }
}