接触JSのほとんどは、実際には、言語を学ぶのほとんどのほとんどは、このように取られるべきである学ぶために使用される最初のされています。ちょうど練習に行くので、また始まったが、それを見れば、学校に行けない推定するのは簡単です。そうそう、他人を欺くために、最初にすべての自分自身を欺くために持っている(のふりをした後、作業を開始し、他の人をばかに行き、その後、学校に行くことはありませんが、彼らはだまさ感じたら、その後、正直にビデオや本を見て、ノートをやって、何度も何度も、あなたが本当に他の誰か)をだますことができます。^ 0 ^
まず第一に、本を読んで、私は二つの最も重要な知識のJSを理解する助けた - も、この時点閉鎖を、次のポイントは、コンパイラの理論とオブジェクトのプロトタイプです。
知識の閉鎖についての記録をここに。第1の閉鎖JSコンパイラ理論、問い合わせや変数のスコープの前に理解する必要性を理解しています。
1.基本
1.1コンパイラの理論
JavaScriptは、通常「動的」または「解釈」言語として分類されているが、実際にそれがコンパイル言語ですが。これは、伝統的なコンパイル言語のコンパイラに比べて、事前にコンパイルされていない、JavaScriptエンジンは、はるかに複雑です。
コードが実行される前にはJavaScriptの場合、コンパイラほとんどの場合は、時間の数マイクロ秒以内に起こります。任意のJavaScriptコードスニペットは、実行前にコンパイルして、実行しなければなりません。
上のvar a = 2;
コンパイル処理:
遭遇するvar A、変数名が同じスコープに存在チェック、存在は無視され、または他の新しい変数を宣言しています。
処理に必要なランタイム・コード生成
a = 2
の割り当てを、
コードが実行されると、エンジンは、変数aを探します、見つかった場合は、それ以外の場合は、例外がスローされます、割り当てられます。
約1.2変数を探します
変数へのお問い合わせLHS查询
及びRHS查询
割り当て上記は、となりますLHS查询
。
お問合せRHS LHS変数は代入演算の左側に問い合わせを行い、表示されたとき、それが右側に表示されます。
ターゲットの割り当ては、誰が
LHS
、誰割り当ての源ですRHS
。
LHS查询
これに値を割り当てることができ、コンテナ自体の変数を、検索しようとしています。RHS查询
変数の換算値を検索しRHS查询
、より正確には、本当の意味での「割り当ての右側に」ない「非左側。」
などconsole.log( a );
RHS参照への参照である、とされ、a = 2;
実際に私たちは現在の値がyesで何を気にしないので、LHSへの参照は、参照です。
広げます
function foo(a) {
var b = a;
return a + b;
}
var c = foo( 2 );
ここではLHS查询
3であり、RHS查询
一つはまた、メソッドfooを呼び出す必要があり、4つがありRHS查询
、パラメータ渡す必要2
仮パラメータメソッドに割り当てられているがa
。
約1.3適用範囲
作用域
変数名は、一連のルールに基づいて検索。通常は、いくつか考慮して必要となります作用域
。
機能ブロックまたは他のブロックまたは関数内にネストされたときには、発生します作用域的嵌套
。変数の現在のスコープが見つからない場合は、エンジンは、(1レベル)の外側のネストされた中にはなり作用域
ますが、変数を見つけるまで見て、または最も外側に到達し続ける作用域
(つまり全局作用域
今のところ)。
場合はRHS查询
、あなたがしたい変数が見つからない場合、エンジンがスローされますReferenceError
例外を。
エンジンが実行されるとLHS查询
、グローバルスコープ内のターゲット変数を見つけることができない場合は、グローバルスコープは、その名前の変数を作成し、エンジンにそれを返します提供されるもの以外で「strictモード。」
場合はRHS查询
成功したが、変数が不当な操作があり、それがスローされますTypeError
例外を。
効果をシャドウイング
スコープ検索は、識別子の最初の試合で停止します。
グローバル変数は、自動的に(例えば、ウィンドウオブジェクトブラウザなど)グローバルオブジェクトグローバルオブジェクト変数を介してアクセスすることができる性質、となるであろう:window.a
;しかし、グローバル変数にアクセスできませんどのような場合に、非シールドされています。
字句欺きます
function foo(str, a) {
eval( str ); // 欺骗! console.log( a, b );
}
var b = 2;
foo( "var b = 3;", 1 ); // 1, 3
使用するeval
変数を宣言メソッドfooでb
グローバル変数をマスキングし、割り当てをb
。
プログラムのstrictモードでは、evalの(..)は、文はスコープが配置されて変更できないことを意味し、実行時に、独自のレキシカルスコープを持っています。
用法
var obj = { a: 1, b: 2 };
foo(obj){
with (obj) {
a = 3;
b = 4;
c = 5;
}
}
foo(obj)
console.log(obj.a) // 3
console.log(obj.c) // undefine
console.log(c) // 5
1.4スコープ機能
これは多重化(入れ子になったスコープを含む)全体の機能の範囲内とすることができる。この機能に属するすべての変数を使用して、スコープの機能を指します。
最小は、許可または最小限の曝露ガイドラインは:ソフトウェア設計では、最低限必要なコンテンツにさらされるべきであり、その他のコンテンツは、APIは、モジュールまたはオブジェクトをデザインするなど、「非表示」になります。
給付の対象範囲:
競合を避けます
サードパーティのライブラリを持つグローバル名前空間変数競合が発生しやすくなります。
スコープを使用すると、共有スコープの中に注入することができないすべての識別子を強制するルールが、プライベートで効果的にすべての偶発的衝突を回避することができますスコープの競合、残っていません。
var a = 2;
(function foo(){ // <-- 添加这一行
var a = 3;
console.log( a ); // 3
})(); // <-- 以及这一行
console.log( a ); // 2
関数は、式の機能ではなく、に対処するための標準的な関数宣言として扱われます。
区別するための関数宣言と表現する最も簡単な方法は、関数キーワードの位置を見ている宣言(1つのだけでなく、コードの行が、位置の文全体)に表示されます。関数が最初の単語の中で宣言されている場合は、それはそれ以外の場合は、関数式で、関数宣言です。
functionステートメントと関数式の最も重要な違いは、その名前の識別子がどこバインドされますです。
匿名関数式
setTimeout( function() {
console.log("I waited 1 second!");
}, 1000 );
関数式は、識別子に名前を付けることができない、と関数宣言を省略することができる関数名ではありません。
匿名関数式はおよそいくつかの欠点があります。
スタックトレース内の匿名関数は、デバッグが難しくなり、意味のある関数名は表示されません。
関数は、その使用を参照する必要がある場合には、このような再帰などarguments.calleeを参照し、有効期限が切れています。そして、イベントリスナーがイベントトリガの後に自分自身をアンバンドリングが必要です。
コードの読みやすさに影響を与えます。
書き込み、名前を付ける推奨:
setTimeout( function timeoutHandler() {
console.log( "I waited 1 second!" );
}, 1000 );
即時実行関数式(生命維持)
var a = 2;
(function foo(a) {
a += 3;
console.log( a ); // 5
})(a);
console.log( a ); // 2
利点:
- パラメータとして外部のオブジェクト、あなたが適切な名前を感じるという名前の変数もの。コードスタイルの改善にご協力ください。
- 解決未定義の識別子のデフォルト値は、エラーが発生した例外によって覆われています。
undefined = true; // 给其他代码挖了一个大坑!绝对不要这样做!
(function IIFE( undefined ) {
var a;
if (a === undefined) {
console.log( "Undefined is safe here!" );
}
})();
- 第二を実行するための機能を、コードの実行順序を反転。
var a = 2;
(function IIFE( def ) {
def( window );
})(function def( global ) {
var a = 3;
console.log( a ); // 3 console.log( global.a ); // 2
});
1.5適用範囲
表面的には、JavaScriptの機能ブロックスコープとは関係ありません。
for (var i=0; i<10; i++) {
console.log( i );
}
ここではi
それが中(グローバルまたは機能)範囲外のバインドされます。
使用のブロックスコープ:ローカル変数宣言からはできるだけ近づけて使用されるべき、および局在化を最大にするために。
ブロックスコープは、最小特権の原理のためのツールは、機能拡張に隠された情報からブロックコードに隠された情報の前に拡張されます
ときvar
あなたは、変数を宣言するすべて同じであるところ、彼らは最終的には外部のスコープに属しますので、それを書きます。
例のスコープブロック:
with
構造は、スコープの鍵ブロックです。try/catch
catch
句は、変数だけで宣言されたブロックスコープ、作成しcatch
た内部有効に。let
キーワードが配置されている任意の変数のスコープにバインドすることができます。変数のスコープどこ暗黙のうちにその文をブロックします。const
キーワードはまた、可変ブロックスコープを作成するために使用することができるが、その値は(定数)に固定されています。
var foo = true;
if (foo) {
var a = 2;
const b = 3; // 包含在 if 中的块作用域常量
a = 3; // 正常!
b = 4; // 错误!
}
console.log( a ); // 3
console.log( b ); // ReferenceError
let
主な役割:
-
let
文はブロックスコープを強化するために行われることはありません。コードが実行される前に宣言、ステートメントが「存在しています。」しません - そして、閉鎖や関連メモリを再利用するためにガベージコレクションのメカニズム。
function process(data) {
// 在这里做点有趣的事情
}
// 在这个块中定义的内容可以销毁了!
{
let someReallyBigData = { .. };
process( someReallyBigData );
}
-
for循环
だけでなく、頭レッツi
にバインドされfor
、実際に、それはときに再割り当ての最後の繰り返しループの使用の価値ことを確実にするために、ループの各反復に再バインドされる、サイクルのブロック。
for (let i=0; i<10; i++) {
console.log( i );
}
console.log( i ); // ReferenceError
1.6アップグレード
例1.6.1:
a = 2;
var a;
console.log( a ); // 2
例1.6.2:
console.log( a ); // undefine
var a = 2;
あなたがそれを見るとき
var a = 2;
、あなたはこの文だと思うことがあります。しかし、実際には2つの宣言として見られますvar a;
とa = 2;
。最初の定義文はコンパイル時に行われます。二代入文は、実装フェーズを待っている場所に残されることになります。
このプロセスは、彼らがトップに「移動」されたコードに表示されている位置から変数と関数の宣言のようなものです。このプロセスは、リフトと呼ばれています。
関数の宣言と変数宣言が改善されます。しかし、詳細は関数が最初に上昇し、その後、変数されることは注目に値します。
例1.6.3:
foo(); // 1
var foo;
function foo() {
console.log( 1 );
}
foo = function() {
console.log( 2 );
};
例1.6.4:
foo(); // 3
function foo() {
console.log( 1 );
}
var foo = function() {
console.log( 2 );
};
function foo() {
console.log( 3 );
}
例1.6.5:
foo(); // "b"
var a = true; if (a) {
function foo() {
console.log("a"); }
}
else {
function foo() {
console.log("b");
}
}
繰り返しvarステートメントにもかかわらず、それは無視されますが、背中やフロントカバー缶に表示される関数の宣言されます。
2.閉鎖
JavaScriptでクロージャは、あなたはそれを認識し、受け入れることができるようにする必要があり、どこにでもあります。
クロージャは、生成されたコードを書くときにレキシカルスコープの自然の結果に基づいており、あなたもクロージャを作成するために、意識的にそれらを使用する必要はありません。
どこで、いつアクセス字句スコープ機能を覚えることができる場合は、関数は、現在のレキシカルスコープの外で実行された場合でも、閉鎖を作成します。
function foo() {
var a = 2;
function bar() {
console.log( a ); // 2
}
bar();
}
foo();
bar()
a
方法基準字句スコープルールを見つけることであり、これらのルールは、クロージャの一部のみです。しかし、以前の定義によると、これはクロージャではありません。
次のコード、閉鎖の明確なデモンストレーション:
function foo() {
var a = 2;
function bar() {
console.log( a );
}
return bar;
}
var baz = foo();
baz(); // 2 这就是闭包的效果。
機能bar()
レキシカルスコープがアクセスできるfoo()
内部範囲を。その後、我々はなりますbar()
値の型が渡されたとしての地位を機能します。
クロージャを理解します
ではfoo()
、実行、通常我々が期待するfoo()
範囲全体が内部破壊されました。実際内側範囲は(これは持続bar()
自体使用する)、したがって、リサイクルされていません。
おかげbar()
贈り物によって宣言された位置は、それがカバーしているfoo()
範囲が生存することができたように、クロージャのスコープ内でbar()
参照した後、任意の時点で。
bar()
依然としての範囲への参照を保持し、これはクロージャを参照すると呼ばれています。
function wait(message) {
setTimeout( function timer() {
console.log( message );
}, 1000 );
}
wait( "Hello, closure!" );
timer
それはカバーしているwait(..)
クロージャのスコープを、そのためにも、変数を保持message
参照を。
wait(..)
1000ミリ秒の実装後に、それは、内部スコープ消えないtimer
機能はまだ保持してwait(..)
クロージャのスコープを。
サイクルとクロージャ
例2.1:
for (var i=1; i<=5; i++) {
setTimeout( function timer() {
console.log( i ); // 6 6 6 6 6
}, i*1000 );
}
例2.2:
for (var i=1; i<=5; i++) {
(function() {
var j = i;
setTimeout( function timer() {
console.log( j ); // 1 2 3 4 5 6
}, j*1000 );
})();
}
例2.3:
for (var i=1; i<=5; i++) {
(function(j) {
setTimeout( function timer() {
console.log( j ); // 1 2 3 4 5
}, j*1000 );
})( i );
}
実施例2.1:実際には一つだけが存在するように、5つの機能の循環が各反復で別々に定義されているが、それらは共有グローバルスコープ内に封入されているが、作品の範囲に基づきますi
。遅延実行の機能ので、与えるために、最終呼び出しまでループを実行i
6の値。
実施例2.2:匿名関数は、独自のスコープ変数を有するj
各反復に格納するi
値。
実施例2.3:2.2例コードの修正。
反復回数内でご使用くださいIIFE
遅延新しいスコープのコールバック関数が各反復で囲むことができるように、各反復は、私たちの訪問のための正しい値に変数が含まれます、新しいスコープイテレーションごとに生成しているだろう。
2.1モジュール
function CoolModule() {
var something = "cool";
var another = [1, 2, 3];
function doSomething() {
console.log( something );
}
function doAnother() {
console.log( another.join( " ! " ) );
}
return {
doSomething: doSomething,
doAnother: doAnother
};
}
var foo = CoolModule();
foo.doSomething(); // cool
foo.doAnother(); // 1 ! 2 ! 3
このモードは、JavaScriptのモジュールに呼ばれています。私たちは、内部データ変数が隠されておくとプライベートの状態。これは、モジュールの戻り値のオブジェクトタイプとして見ることができる本質的にパブリックAPIです。
モード2つのに必要な条件モジュール:
少なくとも一度呼び出されなければなりません外部閉じた機能、存在する必要があります。
機能をブロックする内部機能がクロージャプライベート範囲に形成することができるように、少なくとも1つの内部関数を返す必要があり、専用アクセスであるか、または状態を変更することができます。
関数呼び出しから閉鎖機能のないオブジェクトは、データのみの属性は、実際のモジュールではない、返されました。
シングルトン
var foo = (function CoolModule() {
var something = "cool";
var another = [1, 2, 3];
function doSomething() {
console.log( something );
}
function doAnother() {
console.log( another.join( " ! " ) );
}
return {
doSomething: doSomething,
doAnother: doAnother
};
})();
foo.doSomething(); // cool
foo.doAnother(); // 1 ! 2 ! 3
ローダ/マネージャー
var MyModules = (function Manager() {
var modules = {};
function define(name, deps, impl) {
for (var i=0; i<deps.length; i++) {
deps[i] = modules[deps[i]];
}
modules[name] = impl.apply( impl, deps );
}
function get(name) {
return modules[name];
}
return {
define: define,
get: get
};
})();
コアコードですmodules[name] = impl.apply(impl, deps)
。機能モジュールを定義するにはパッケージ(任意の依存性を渡すことができる)、及びAPIモジュールである戻り値を、導入、モジュールを管理するために、リスト内の名前の下に格納されています。
モジュールを使用します:
MyModules.define( "bar", [], function() {
function hello(who) {
return "Let me introduce: " + who;
}
return {
hello: hello
};
});
var bar = MyModules.get( "bar" );
console.log(
bar.hello( "hippo" )
); // Let me introduce: hippo
概要
時間と学習ではなく、喜び。再び本を読み、メモを取る、そして再度見直し、理解するためにこの知識のいくつかを理解していない、ともいくつかのパスの未検出の知識を発見しました。
昔、そうやので、次のクラスは、各試験紙は、コック6回、結果はその後、最前線に配置されているたびに繰り返さなければなりません。彼の母親は、このアプローチからどこ教師学習に学んでいないが、私の先生でしたが、先生が、私は学習は、私は、そのような先生に会った非常に嬉しい、幸せなことだと感じています。
ます。https://www.jianshu.com/p/5ae0cc6bdbb4で再現