ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScript の言語機能に関する簡単な説明

JavaScript の言語機能に関する簡単な説明

高洛峰
高洛峰オリジナル
2016-11-28 09:56:35842ブラウズ

最初にコード スニペットを見てください:

var f = function foo(){
Return typeof foo; // foo は内部スコープで有効です
};
// foo は外部で使用される場合には見えません
typeof foo; "unknown"
f(); // "function"

ここで言いたいのは、関数式内の foo は関数内でのみ参照でき、関数外では参照できないということです。

json

多くの JavaScript 開発者は、誤って JavaScript オブジェクト リテラル (JSON オブジェクト) と呼んでいます。 JSON はデータ交換形式を記述するように設計されており、JavaScript のサブセットである独自の構文も備えています。

{ "prop": "val" } このような宣言は、使用されるコンテキストに応じて、JavaScript オブジェクト リテラルまたは JSON 文字列の場合があります。文字列コンテキスト (一重引用符または二重引用符で囲まれるか、テキスト ファイルから読み取られる) で使用される場合、それは JSON 文字列です。オブジェクト リテラル コンテキストで使用される場合、それはオブジェクト リテラルです。

// これは JSON 文字列です
var foo = '{ "prop": "val" }';
// これはオブジェクト リテラルです
var bar = { "prop": "val" };

また知っておくべきことの 1 つは、JSON.parse は JSON 文字列をオブジェクトに逆シリアル化するために使用され、JSON.stringify はオブジェクトを JSON 文字列にシリアル化するために使用されるということです。古いバージョンのブラウザはこのオブジェクトをサポートしていませんが、json2.js を通じて同じ機能を実現できます。

Prototype

function Animal (){
// ...
}
function cat (){
// ...
}
cat.prototype = new Animal();//このメソッドは継承します関数内のコンストラクター。
cat.prototype = Animal.prototype;//このメソッドはコンストラクターを継承しません。
//もう 1 つ注意すべき重要な点は、独自のプロトタイプ チェーンを保守する必要があるということです。初心者はこれを常に忘れてしまいます。
cat.prototype.constructor = cat;

(新しいオブジェクトを割り当てることによって) 関数のプロトタイプ プロパティを完全に変更すると、作成するオブジェクトにはコンストラクター プロパティが含まれないため、元のコンストラクターへの参照が失われます。


function A() {}
A.prototype = {
x: 10
};
var a = new A();
alert(a.x) // 10
alert(a.constructor === A) ) ; // false!

MDN のコンストラクターの説明を見てみましょう: プロトタイプ: インスタンスのプロトタイプを作成したオブジェクト関数への参照を返します。 したがって、関数へのプロトタイプ参照は手動で復元する必要があります。

function A() {}
A.prototype = {
コンストラクター: A,
x: 10
};
var a = new A();
alert(a.x) // 10
alert(a.constructor) === A); // true

ただし、プロトタイプ属性を送信しても、すでに作成されているオブジェクトのプロトタイプには影響しません (コンストラクターのプロトタイプ属性が変更された場合にのみ影響を受けます)。つまり、新しく作成されたオブジェクトのみが影響を受けます。新しいプロトタイプがある場合、作成されたオブジェクトは依然として元の古いプロトタイプを参照します (このプロトタイプは変更できなくなります)。

function A() {}
A.prototype.x = 10;
var a = new A();
alert(a.x); // 10
A.prototype = {
コンストラクター: A,
x: 20
y: 30
};
// オブジェクト a は、暗黙的な [[Prototype]] 参照を通じて原油のプロトタイプから取得された値です
alert(a.x) // 10
alert(a.y) // unknown
; var b = new A();
// ただし、新しいオブジェクトは新しいプロトタイプから取得された値です
alert(b.x); // 20
alert(b.y) // 30

したがって、「プロトタイプを動的に変更すると、 「オブジェクトはすべて新しいプロトタイプを持つ」は間違いです。新しいプロトタイプは、プロトタイプが変更された後に新しく作成されたオブジェクトにのみ有効になります。ここでの主なルールは、オブジェクトのプロトタイプはオブジェクトの作成時に作成され、その後は新しいオブジェクトに変更することはできません。同じオブジェクトを参照している場合は、コンストラクター内の明示的なプロトタイプを介して参照できます。オブジェクトの作成後は、プロトタイプの属性のみを追加または変更できます。

変数オブジェクト 関数実行コンテキストでは、VO (変数オブジェクト) に直接アクセスすることはできません。このとき、活性化オブジェクト (Activation オブジェクト) が VO の役割を果たします。 アクティブ オブジェクトは、関数コンテキストに入るときに作成され、関数の argument 属性を通じて初期化されます。 argument 属性の値は Arguments オブジェクトです。

function foo(x, y, z) {

// 宣言された関数パラメーターの数 argument (x, y, z)
alter(foo.length); // 3
// 実際に渡されるパラメーターの数 (x, y のみ)
alter(arguments.length) // 2
// パラメーターの呼び出し先は関数自体です
alter(arguments.callee ==) = foo); // true
}

実行コンテキストに入るとき (コード実行前)、VO にはすでに次の属性が含まれています: 1. 関数のすべての仮パラメーター (関数実行コンテキストにいる場合)。すべての関数宣言 (FunctionDeclaration, FD );

すべての変数宣言 (var, VariableDeclaration);

もう 1 つの典型的な例:



alert(x) // function
var x = 10;

alert(x); / 10

x = 20 ;
関数 x() {};
alert(x) // 20
;

仕様によれば、コンテキストに入るときに関数宣言が入力され、コンテキストに入るときに変数宣言「x」もあり、上で述べたように、変数宣言は関数宣言と仮パラメータ宣言の後に続きます。その後、このコンテキスト入力フェーズ中に、変数宣言は、VO 内に既に存在する同じ名前の関数宣言または仮パラメーター宣言を妨げません。単純な属性と比較して、変数には {DontDelete} という属性があります。この属性の意味は、変数属性を delete 演算子を使用して直接削除できないことです。


a = 10;
alert(window.a); // 10
alert(window.a); // 未定義
var b = 20;
alert(window.a); b); // 20
alert(delete b); // false
alert(window.b); // 20 のままです。 b はプロパティではなく変数です!
var a = 10; // グローバル コンテキストの変数
(function () {
var b = 20; // 関数コンテキストのローカル変数
})();
alert(a ); // 10
alert(b); // グローバル変数 "b" は宣言されていません。

this 関数コンテキストでは、これは呼び出し元によって提供され、関数の呼び出し方法によって決定されます。呼び出し括弧 () の左側が参照型の値である場合、これは参照型値の基本オブジェクトに設定されます。それ以外の場合 (参照型とは異なるその他のプロパティ)、この値は null になります。 。ただし、this の値が null である実際の状況はありません。this の値が null の場合、その値は暗黙的にグローバル オブジェクトに変換されるからです。

(function () {
alert(this); // null => global
})();

この例では、関数オブジェクトはありますが、参照型のオブジェクトはありません (識別子ではなく、プロパティ アクセサーでもありません)、したがって、この値は最終的にグローバル オブジェクトに設定されます。

var foo = {
bar: function () {
alert(this);
}
};
foo.bar() // 参照、OK => foo
(foo.bar)(); // 参照、OK => foo
(foo.bar = foo.bar)(); // global
(false || foo.bar)(); // global
(foo.bar, foo.bar); ); // global

問題は、次の 3 つの呼び出しでは、特定の操作を適用した後、呼び出し括弧の左側の値が参照型ではなくなることです。

最初の例は明らかです - 明らかな参照型です。その結果、これが基本オブジェクト、つまり foo になります。

2 番目の例では、グループ演算子は適用されません。GetValue など、参照型からオブジェクトの実際の値を取得する上記のメソッドを考えてください。同様に、グループ操作の戻りでも参照型を取得します。これが、 this 値がベース オブジェクト (foo) に再度設定される理由です。
3 番目の例では、グループ演算子とは異なり、代入演算子は GetValue メソッドを呼び出します。返される結果は関数オブジェクト (参照型ではありません) です。つまり、これは null に設定され、結果はグローバル オブジェクトになります。
4 番目と 5 番目も同様です。コンマ演算子と論理演算子 (OR) が GetValue メソッドを呼び出し、それに応じて参照が失われ、関数が取得されます。そして、再度グローバルに設定します。
ご存知のとおり、ローカル変数、内部関数、仮パラメータは、特定の関数のアクティベーション オブジェクトに格納されます。

function foo() {

function bar() {
alert(this); // global
}
bar(); // AO.bar() と同じ
}
アクティブなオブジェクトは常にこれとして返されます。値は null - (つまり、擬似コードの AO.bar() は null.bar() と同等です)。ここで、これをグローバル オブジェクトに設定して、上で説明した例に戻ります。

スコープ チェーン

関数コンストラクターを通じて作成された関数のスコープ属性は、常に唯一のグローバル オブジェクトです。

1 つの重要な例外には、関数コンストラクターを介して作成された関数が含まれます。

var x = 10;

function foo() {
var y = 20;
function barFD() { // 関数宣言
alert(x);
alert(y);
}
var barFn = Function('アラート(x); アラート(y);');
barFD() // 10, 20
barFn() // 10, "y" が定義されていません
}

また:

var x = 10, y = 10;

with ({x: 20}) {

var x = 30, y = 30;
//x = 30 ここでは x = 20 をカバーします。 // 30
alert(y); // 30
}
alert(y); // 30

コンテキストに入ると何が起こるか?識別子「x」と「y」が変数オブジェクトに追加されました。さらに、コードの実行フェーズ中に次の変更を加えます:

x = 10, y = 10;
オブジェクト {x:20} がスコープの先頭に追加されます
with 内で var 宣言が検出されますが、入力時にすべての変数が解決されているため、もちろん何も作成されません。コンテキストの追加
2 番目のステップでは、変数 "x" のみが変更され、実際にはオブジェクト内の "x" が解析されてスコープ チェーンの先頭に追加され、"x" は 20 でしたが 30 になります。同じように、変数オブジェクト「y」の変更もあります。解析された後、その値は 10 から 30 に変更されます
さらに、with 宣言が完了すると、その特定のオブジェクトがスコープ チェーンから削除されます。変数 "x "--30 もそのオブジェクトから削除されます)、つまり、スコープ チェーンの構造が強化される前の状態に戻ります。
最後の 2 つのアラートでは、現在の変数オブジェクトの "x" は同じままですが、"y" の値は 30 に等しくなりますが、これは with ステートメントの実行中に変更されました。
関数

括弧に関する質問

次の質問を見てみましょう: 「関数を作成した直後に呼び出す場合、なぜ関数を括弧で囲む必要があるのですか?」 』の答えは、表現文の制限はこうです。

標準によれば、式ステートメントはコードブロックと区別することが難しいため、中括弧{で始めることはできません。同様に、関数宣言と区別することが難しいため、関数キーワードで始めることもできません。つまり、作成直後に実行される関数を定義する場合は、次のように呼び出します:

function () {

...
}();
//
function foo() { という名前であっても
...
}();

上記の 2 つの定義では、インタープリタは解釈時にエラーを報告しますが、多くの理由が考えられます。これがグローバル コード (つまり、プログラム レベル) で定義されている場合、関数キーワードで始まるため、インタープリターはそれを関数宣言として扱います。最初の例では、関数宣言に名前がないため、SyntaxError が発生します。 (関数宣言には名前が必要であると前述しました)。 2 番目の例では、foo という名前の関数宣言が正常に作成されていますが、それでも構文エラー、つまり式のないグループ化演算子エラーが発生します。これは実際には、関数呼び出しで使用される括弧ではなく、関数宣言後のグループ化演算子です。したがって、次のコードを宣言するとします。

// "foo" はコンテキストに入るときに作成される関数宣言です
alert(foo) // function
function foo(x) {
alert(x);
} (1); // これは単なるグループ化演算子であり、関数呼び出しではありません。
foo(10); // これは実際の関数呼び出しであり、結果は 10 です

式を作成する最も簡単な方法は、グループ化演算子の括弧を使用することです。そのため、インタプリタは を解釈します。曖昧さはなくなります。コード実行フェーズ中に、この関数は作成され、すぐに実行され、その後自動的に破棄されます (参照がない場合)

(function foo(x) {
alert(x);
})(1); / これは呼び出しであり、グループ化演算子ではありません

上記のコードは、式を括弧で囲み、(1) を通じて呼び出すものです。すぐに実行される次の関数の場合、関数がすでに式の位置にあり、パーサーは関数の実行フェーズで作成される必要がある FE を処理していることを認識しているため、括弧で囲む必要がないことに注意してください。 、関数が作成された直後に呼び出されます。

var foo = {

bar: function (x) {
return x % 2 != 0 ? 'はい' : 'いいえ';
}(1)
};
alert(foo.bar); / 'yes'

ご覧のとおり、foo.bar は関数ではなく文字列であり、この関数は条件パラメーターに基づいてこのプロパティを初期化するためにのみ使用されます。関数は作成され、すぐに呼び出されます。

したがって、「括弧について」という質問に対する完全な答えは次のとおりです:

関数が式の位置にない場合、グループ化演算子の括弧が必要です。つまり、関数を手動で FE に変換します。
パーサーが FE を扱っていることを認識している場合は、括弧を使用する必要はありません。

自由変数:

function testFn() {

var localVar = 10;//innerFn 関数の場合、localVar は自由変数です。
function innerFn(innerParam) {
alert(innerParam + localVar);
}
return innerFn;
}

クロージャーの静的スコープ:

var z = 10;

function foo() {
alert( z) ;
}
foo(); // 10 – 静的スコープと動的スコープを使用する場合
(function () {
var z = 20;
foo(); // 10 – 静的スコープを使用する場合、20 – 動的スコープを使用する
})();
// fooをパラメータとして使う場合も同様です
(function (funArg) {
var z = 30;
funArg(); // 10 – 静的スコープ、30 – 動的スコープ
})(ふー);
理論: スコープ チェーンのため、すべての関数はクロージャです (関数の種類に関係なく、匿名関数、FE、NFE、FD はすべてクロージャです)。実用的な観点から: 以下の関数はクロージャとみなされます: * 作成されたコンテキストが破棄されても存在します (たとえば、内部関数が親関数から返される)

* 自由変数が参照されますコード内

最後に:

ECMAScript は、プロトタイプベースの委任継承をサポートするオブジェクト指向言語です。


声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。