効率的なJavaScriptクラスライブラリを書きたいのですが、開始できません。
他の人の図書館を読んでみてくださいが、それが理解されているかのように理解してください。
高度なJS機能をよく研究する予定ですが、権威ある本のコンテンツはあまりにも散らばっています。
「使用」を覚えていても、「使用」したいときは「メソッド」については考えません。
私のように、あなたは私たちの計画を抑える目に見えない力を持っているように見えます。
この期間中、さまざまな宿題、コースデザイン、および実験レポートが2倍になりました。少し時間を絞り出したり、眠らないで、自分のライブラリを書くことに近づいて、過去に読んだ本を整理することはまれです。
この記事は、「JavaScript Language Essence」および「Effective JavaScript」から参照されています。すべての例がデバッグされています。それらを理解した後、私はいくつかの「深い」原則を少し単純にしたいと思っています。
1。可変スコープ
スコープは、プログラマーにとって酸素のようなものです。それはどこにでもあり、あなたはそれについてさえ考えていません。しかし、それが汚染されている場合(グローバルオブジェクトの使用など)、窒息を感じる(アプリケーションの応答が遅いなど)。 JavaScriptコアスコープルールはシンプルで、慎重に設計されており、非常に強力です。 JavaScriptを効果的に使用するには、可変範囲の基本概念の一部を習得し、とらえどころのない迷惑な問題につながる可能性のある極端な状況を理解する必要があります。
1.1グローバル変数をできるだけ使用しないようにしてください
JavaScriptは、グローバルネームスペースに変数を簡単に作成できます。グローバル変数を作成するのは楽なことではありません。なぜなら、宣言の形態を必要とせず、プログラム全体のすべてのコードによって自動的にアクセスできるからです。
米国の初心者の場合、特定のニーズに遭遇した場合(たとえば、送信されたデータが記録されている場合、特定の時間が呼び出されるのを待つとき、または特定の関数がよく使用される場合)、最終的にグローバルな機能を考えます。新入生の年に学んだC言語プロセス指向の思考でさえ、あまりにも深く根付いており、システムは機能にきちんといっぱいです。グローバル変数を定義すると、共有されたパブリックネームスペースを汚染し、予期しない命名対立につながる可能性があります。グローバル変数は、プログラム内の独立したコンポーネント間で不必要な結合を引き起こすため、モジュール性を助長しません。真剣に言えば、あまりにも多くのグローバル(スタイルシートを含む、Divまたはaのスタイルを直接定義する)、およびそれらを複数の人々の開発に統合することは、壊滅的な間違いです。これが、すべてのjQueryコードが即座に実行された匿名式、つまりセルフコール匿名関数に包まれている理由です。ブラウザがjQueryファイルをロードすると、匿名関数を呼び出した直後に実行を開始し、さまざまなモジュールのjQueryを初期化して、グローバル変数の破壊と汚染を避け、他のコードに影響を与えます。
コードコピーは次のとおりです。
(関数(ウィンドウ、未定義){
var jquery = ...
// ...
window.jquery = window。$ = jquery;
})(ウィンドウ);
さらに、「最初の方法を書いて後で整理する方法を書く」方が便利だと思うかもしれませんが、優れたプログラマーはプログラムの構造に常に注意を払い、関連する機能を継続的に分類し、無関係なコンポーネントを個別に分類し、これらの動作はプログラミング式の一部です。
グローバルネームスペースは、JavaScriptプログラムの独立したコンポーネントが対話する唯一の方法であるため、グローバル命名コントロールの使用は避けられません。コンポーネントまたはライブラリは、いくつかのグローバル変数を定義する必要があります。プログラムの他の部分で使用するため。それ以外の場合は、ローカル変数を使用するのが最善です。
コードコピーは次のとおりです。
this.foo; //未定義
foo = "global foo";
this.foo; // "Globalfoo"
var foo = "global foo";
this.foo = "変更";
foo; //変更
JavaScriptのグローバルネームスペースは、このキーワードの初期値として機能するプログラムのグローバルスコープでアクセスできるグローバルオブジェクトにもさらされています。 Webブラウザでは、グローバルオブジェクトがグローバルウィンドウ変数にバインドされています。これは、グローバル変数を作成するには2つの方法があることを意味します。Globalスコープ内でVARを宣言するか、グローバルオブジェクトに追加することです。 VAR宣言を使用する利点は、プログラム範囲におけるグローバル変数の影響を明確に表現できることです。
バインドされたグローバル変数への参照がランタイムエラーを引き起こす可能性があることを考えると、クリアおよび簡潔な保存スコープにより、コードユーザーがプログラムが宣言するグローバル変数を理解しやすくなります。
グローバルオブジェクトはグローバル環境に動的な応答メカニズムを提供するため、実行中の環境を照会し、このプラットフォームで使用できる機能を検出するために使用できます。
EG.ES5は、JSON形式のデータを読み書きするためのグローバルJSONオブジェクトを導入します。
コードコピーは次のとおりです。
if(!this.json){
this.json = {
解析:..、
stringify:...
}
}
JSONの実装を提供する場合は、もちろん独自の実装を単純かつ無条件に使用できます。しかし、ホスト環境によって提供される組み込みの実装は、Cのブラウザに書き込まれるため、ほぼ適切です。特定の標準に応じて正確性と一貫性を厳密にチェックし、一般にサードパーティの実装よりも優れたパフォーマンスを提供するためです。
データ構造コースの設計シミュレーション文字列の基本的な操作では、言語自体によって提供される方法を使用できないことが必要でした。 JavaScriptは、配列の基本操作を非常によく実装しています。一般的な学習のニーズだけの場合、言語自体によって提供されるシミュレーション方法のアイデアは良いですが、実際に開発に投資する場合は、できるだけ早くJavaScriptビルトインメソッドを使用することを選択する必要はありません。
1.2で使用しないでください
withステートメントは、アプリケーションを信頼性が低く非効率的にする「利便性」を提供します。単一のオブジェクト上の一連のメソッドを順番に呼び出す必要があります。 withステートメントを使用すると、オブジェクトへの重複参照を簡単に回避できます。
コードコピーは次のとおりです。
関数ステータス(情報){
var widget = new Widget();
with(widget){
setbackground( "blue");
SetForeGround( "White");
settext( "status:"+info);
見せる();
}
}
また、withステートメントを使用して、モジュールオブジェクトから変数を「インポート」(インポート)することは非常に魅力的です。
コードコピーは次のとおりです。
関数f(x、y){
(数学){
MIN(ROUND(x)、SQRT(y)); //要約の引用を返します
}
}
実際、JavaScriptはすべての変数を同じように扱います。 JavaScriptは最も内側の範囲から始まり、変数を外側に探します。 with言語は、オブジェクトを変数スコープを表すかのように扱うため、with codeブロック内では、変数検索が指定された変数名の属性を検索することから始まります。このオブジェクトにプロパティが見つからない場合は、外部範囲で検索を続けます。 Blockの各外部変数への参照は、With Object(およびそのプロトタイプオブジェクト)に同じ名前の属性がないことを暗黙的に想定しています。プログラムの他の場所でオブジェクトまたはそのプロトタイプを作成または変更することは、必ずしもそのような仮定に従うわけではありません。 JavaScriptエンジンは、使用しているローカル変数を取得するためにローカルコードを読み取っていません。 JavaScriptの範囲は、効率的な内部データ構造として表すことができ、可変検索は非常に高速です。ただし、With Codeブロックはオブジェクトのプロトタイプチェーンを検索して、with Codeのすべての変数を見つける必要があるため、その実行速度は一般的なコードブロックの速度よりもはるかに低くなります。
with言語の代わりに、オブジェクトを短い変数名にバインドするのは簡単です。
コードコピーは次のとおりです。
関数ステータス(情報){
var w = new Widget();
w.setbackground( "blue");
W.SetForeGround( "White");
w.settext( "status:"+info);
w.show();
}
それ以外の場合、最良の方法は、ローカル変数を関連するプロパティに明示的にバインドすることです。
コードコピーは次のとおりです。
関数f(x、y){
var min = math.min、
round = math.round、
sqrt = math.sqrt;
return min(round(x)、sqrt(y));
}
1.3閉鎖に習熟
閉鎖を理解するための単一の概念があります。
a)JavaScriptを使用すると、現在の関数の外側に定義された変数を参照できます。
コードコピーは次のとおりです。
function makeandwich(){
var MagicingRedient = "Peanut Butter";
関数make(filling){
Return MagicIngRedient + "および" + filling;
}
return make( "jelly");
}
makeandwich(); //「ピーナッツバターとゼリー」
b)外部関数が返されたとしても、現在の関数は、外部関数によって定義される変数を参照できます。
コードコピーは次のとおりです。
function makeandwich(){
var MagicingRedient = "Peanut Butter";
関数make(filling){
Return MagicIngRedient + "および" + filling;
}
return make;
}
var f = sandwichmaker();
f( "jelly"); //「ピーナッツバターとゼリー」
f( "バナナ"); //「ピーナッツバターとバナナ」
f( "mallows"); //「ピーナッツバターとマロウ」
JavaScriptDの関数値には、呼び出されたときに実行するために必要なコードよりも多くの情報が含まれています。さらに、JavaScript関数値は、同封の範囲で定義されている変数も内部的に保存します。カバーするスコープ内で変数を追跡する機能は、閉鎖と呼ばれます。
MAKE関数は、コードが2つの外部変数を指す閉鎖です:MagicingRedientとFilling。閉鎖はこれらの2つの変数を保存するため、メイク関数が呼び出されるときはいつでも、そのコードはこれらの2つの変数を参照できます。
関数は、パラメーターや外部関数変数を含む、範囲内の任意の変数を参照できます。これを使用して、より一般的なサンドイッチメーカー機能を記述できます。
コードコピーは次のとおりです。
function makeandwich(magicingredient){
関数make(filling){
Return MagicIngRedient + "および" + filling;
}
return make;
}
var f = sandwichmaker( "ham");
f( "チーズ"); //「ハムとチーズ」
f( "マスタード"); //「ハムとマスタード」
閉鎖は、JavaScriptの最もエレガントで表現力のある特徴の1つであり、多くのイディオムの中核でもあります。
c)閉鎖は、外部変数の値を更新できます。実際、閉鎖は、その値のコピーではなく、外部変数への参照を保存します。したがって、これらの外部変数へのアクセスを伴う閉鎖は更新できます。
コードコピーは次のとおりです。
functionbox(){
var val =未定義;
戻る {
セット:function(newval){val = newval;}、
get:function(){return val;}、
タイプ:function(){return typeof val;}
};
}
var b = box();
b.Type(); //未定義
B.セット(98.6);
b.get(); // 98.6
b.Type(); //番号
この例では、3つの閉鎖を含むオブジェクトを生成します。これらの3つのクロージャーは設定され、タイプ、およびゲットプロパティがあり、すべてVAL変数へのアクセスを共有し、設定された閉鎖はVALの値を更新します。次に、getと入力を呼び出して更新された結果を表示します。
1.4変数宣言の改善を理解する
JavaScriptはこのメソッドスコープをサポートします(変数FOOへの参照は、FOO変数に最も近いものを宣言するスコープにバインドされます)が、ブロックレベルのスコープ(変数によって定義されるスコープは最も近い閉じたステートメントまたはコードブロックではありません)をサポートしません。
この機能を理解しないと、いくつかの微妙なバグが発生します。
コードコピーは次のとおりです。
function iswinner(プレイヤー、その他){
var high hight = 0;
for(var i = 0、n = others.length; i <n; i ++){
var player = others [i];
if(player.score>最高){
最高= player.score;
}
}
return player.score>最高;
}
1.5命名関数式の不器用な範囲に注意してください
コードコピーは次のとおりです。
関数double(x){return x*2; }
var f = function(x){return x*2; }
同じ関数コードを式として使用することもできますが、まったく異なる意味を持っています。匿名関数と名前付き関数式の公式の違いは、後者が関数名と同じ関数名を持つ変数に結合し、関数のローカル変数として機能することです。これを使用して、再帰関数式の式を書き込むことができます。
コードコピーは次のとおりです。
var f = function find(tree、key){
// ...
return find(tree.Left、key)||
find(tree.right、key);
}
変数検索の範囲は、それ自体の関数のみであることは注目に値します。関数宣言とは異なり、名前付き関数式は、内部関数名を介して外部的に参照することはできません。
コードコピーは次のとおりです。
find(mytree、 "foo"); //エラー:findは定義されていません。
var constructor = function(){return null; }
var f = function(){
return constructor();
};
f(); // {}(es3環境で)
このプログラムは、nullを生成するように見えますが、実際には新しいオブジェクトが生成されます。
指定された関数変数はobject.prototype.constructor(すなわち、Ojectのコンストラクター)を継承するため、ステートメントと同様に、このスコープはobject.prototypeの動的な変化の影響を受けます。システム内の関数式の範囲を汚染するオブジェクトを回避する方法は、標準object.prototypeプロパティと同じ名前を持つローカル変数を使用しないように、オブジェクトにプロパティをいつでも追加しないようにすることです。
人気のあるJavaScriptエンジンのもう1つの欠点は、名前付き関数式の宣言の促進です。
コードコピーは次のとおりです。
var f = function g(){return 17;}
g(); // 17(コンフォーマット環境ではない)
一部のJavaScript環境では、2つの機能fとgを異なるオブジェクトとして使用することさえ、不要なメモリ割り当てをもたらします。
1.6ローカルブロック関数の不器用な範囲に注意してください
コードコピーは次のとおりです。
function f(){return "global"; }
関数テスト(x){
関数f(){"local";}を返します
var result = [];
if(x){
result.push(f());
}
result.push(f());
結果結果の結果。
}
テスト(true); // ["local"、 "local"]
テスト(false); //["地元"]
コードコピーは次のとおりです。
function f(){return "global"; }
関数テスト(x){
var result = [];
if(x){
関数f(){"local";}を返します
result.push(f());
}
result.push(f());
結果結果の結果。
}
テスト(true); // ["local"、 "local"]
テスト(false); //["地元"]
JavaScriptにはブロックレベルの範囲がないため、内部関数fの範囲はテスト関数全体にする必要があります。いくつかのJavaScript環境はそうしますが、すべてのJavaScript環境がそうではありません。 JavaScriptの実装は、そのような関数を厳密なモードでのエラー(ローカルブロック関数宣言を使用した厳密なモードのプログラムが構文エラーとして報告する)など、ポータブルコードを検出するのに役立ち、将来の標準バージョンのローカルブロック関数宣言により賢明で可能なセマンティクスを提供します。この場合、テスト関数内のローカル変数を宣言して、グローバル関数fを指すことを検討することができます。