JavaScript スコープの深い理解
はじめに
JavaScriptはフルスタック言語で、特に2016年はJavaScriptが世界を席巻するという噂をよく聞きますし、2016年にはVueが作られるという噂もあります。 . 当時 iOS で TableView を知っていれば仕事を見つけることができたのと同じように、js を使って仕事を見つけることができます (tableView は Android の ListView に相当しますが、現在は基本的に RecyclerView を使用します)
2016 年は基本的に JavaScript に関連しており、Facebook と Alibaba の Weex が開発したモバイル クロスプラットフォーム React Native、ホット リペア テクノロジ JSPath、およびバックエンド Node.js (昨夜私がとても気に入っているテクノロジ スタック) などがあります。 gibhub に行って Vue の星の数を確認してみました。 jQuery を超えました。 星の数は何も証明しませんが、少なくともフロントエンドの考え方が以前のドキュメント操作からデータ駆動開発に変わったことはわかります。 (興味があれば、後で Android、iOS、Vue と組み合わせて使用することもできます。この考え方の変化を示す小さなデモが、EasyUI (学生) の代わりに Ele.me によって作成された Element を使用しようとさえし始めています。バックエンドをやったことがある人は、EasyUI が本当に AV 品質であることを知っているはずです...)
JS テクノロジーは際限なく出現しており、2016 年に JS を学ぶのはどのようなものかについて以前非常に人気のある記事があり、すぐに多くの人を怖がらせました。誰もがフレームワークと新しいテクノロジーに注目していましたが、ネイティブ JS は無視されていました。そこで、いくつかの基本的な JS の質問を通じて皆さんとコミュニケーションを図りたいと思います (皆さんが私を驚かせてくれることを願っていますが、マレーシアについては投げかけないでください。)航空会社...)
JavaScript ドメインの役割
以下は簡単な質問です:
<script> var str1 = "hello"; var str2 = "world"; function t1() { console.log(str1); console.log(str2); var str2 = "toby"; console.log(str2); } //这里会输出什么? t1(); </script>
これは非常に単純な JS スコープの質問ですが、単語を強調すればするほどシンプルであればあるほど、人々は警戒を緩めやすくなり、その結果、深く考えない生徒も出てきます 答えの出力は
● hello
● world
● toby
ですが、結果は出力
● hello
です●未定義
●toby
では、これは奇妙です、なぜ未定義があるのでしょうか? まず第一に、変数の検索は近接性の原則に従うため、js が最初に検索することを理解する必要があります。関数内に str2 がありますが、console.log (str2) に実行すると str2 が定義されていないため、unknown が表示されます
字句解析
理由は分かるはずなので、もう少し例を見てみましょう
例1
<script> function t(userName) { console.log(userName);//这里输出什么? function userName() { console.log('tom'); } } t('toby'); </script>
の出力 この例は、上の例とは異なるように見えます。学校の数学で、問題の種類が変わると混乱する生徒もいるかもしれませんが、実際の出力は
function userName() { console.log('tom'); }
です。実際、この種のスコープの問題は、 「式のセット」。この式は JS の字句解析です。JS の関数を実行する前に実行する必要があるタスクの 1 つは、パラメーターの分析、変数の宣言の分析、関数の分析です。宣言したら、この質問を使用して式を適用します
t('toby') を実行すると、2 つのステージが開始され、1 つは分析ステージです。分析後、実行フェーズに進みます
分析フェーズ:
● 関数が実行されると、Active Object (以下、AO オブジェクトと呼びます) が生成されます。このとき、関数のスコープ内にあるすべての変数が AO 上にあります。 as: t.AO = {}
● 分析パラメータ: パラメータを受け取り、パラメータ名が属性、パラメータ値が属性値です。パラメータがないため、分析結果はコード: t.AO で表されます。 = {userName: toby}
● var 宣言を解析する: t 関数内に var 宣言がありませんのでスキップします
● 関数宣言を解析する: この関数宣言は関数名と同じ名前の属性がある場合に特徴があります。 AO では、この関数によって上書きされます。関数も JS フィールドの変数の一種であるため、コードでは次のように表現されます。 t.AO = { userName : function userName() {console.log('tom') );}}
実行フェーズ:
実行 t('toby')の場合、console.log(userName)が実行されると、t.AO.userNameが呼び出されるため、最終的な出力結果は function userName() {console .log('tom');}
例 2
<script> function t(userName) { console.log(userName);//这里输出什么? var userName = function () { console.log('tom'); } } t('toby'); </script>
それでは、これは上の例とは異なるようで、また混乱に陥っていますか?式にしっかり従って、もう一度プロセスを実行してください (上記の例はより詳細に書かれており、次の分析は単純に書かれています)
分析の前に、まず 2 つの概念を理解する必要があります。1 つは関数宣言と呼ばれるもので、もう 1 つは関数宣言と呼ばれるものです。関数式と呼ばれます
//这个叫函数声明 function userName() { console.log('tom'); } //这个叫函数表达式 var userName = function () { console.log('tom'); }
分析ステージ:
●AOオブジェクトを作成、t.AO = {}
●分析パラメーター: t.AO = {userName : toby}
● 分析var声明: 在AO上,形成一个属性,以var的变量名为属性名,值为undefined,(因为是先分析,后执行,这只是词法分析阶段,并不是执行阶段,分析阶段值都是undefined,如果执行阶段有赋值操作,那值会按照正常赋值改变),也就是说代码应该表示为:t.AO = {userName : undefined},但是还有另外一个原则,那就是如果AO有已经有同名属性,则不影响(也就是什么事都不做),由于分析参数时,AO上已经有userName这个属性了,所以按照这个原则,此时什么事都不做,也就是说,此时按照分析参数时的结果t.AO = {userName : toby}
● 分析函数声明: 此时没有函数声明,略过
执行阶段:
调用t.AO.userName,所以,最后的输出结果是toby
例子3
<script> t(); t2(); function t() { console.log('toby');//这里会输出什么? } var t2 = function () { console.log('hello toby');//这里会输出什么? }; </script>
那么我们再来看一个例子,这下彻底回到高中时代,做了两个例子好像感觉掌握了,结果考试你给来看这个?
答案是,t()输出为toby,t2()则会报错.这又是为什么?
● t()可以调用是因为在词法分析的过程,就已经完成了t函数的分析,所以可以调用
● t2()不能调用是因为在词法分析的阶段,分析到有一个t2声明,在AO上只是形成了一个属性,但是值为undefined
例子4
<script> function t(userName) { console.log(userName);//这里输出什么? function userName() { console.log(userName);//这里输出什么? } userName(); } t('toby'); </script>
函数里面套函数,这次竟然又和前面不一样了...这次我不说答案了,直接先套公式走一波
t('toby')的分析和执行阶段
分析阶段:
● 创建AO对象,t.AO = {}
● 分析参数: t.AO = {userName : toby}
● 分析var声明: 有同名属性,不做任何事,还是t.AO = {userName : toby}
● 分析函数声明: 有同名属性,覆盖: t.AO = {userName : function userName() {console.log(userName);}}
执行阶段: t.AO.userName 输出为function userName() {console.log(userName);}}
userName()的分析和执行阶段
这里也要搞清楚两个概念
//执行userName()分析的是 function () { console.log(userName); }; //而不是 var userName = function () { console.log(userName); };
分析阶段:
● 创建AO对象,userName.AO = {}
● 分析参数: 无,略过
● 分析var声明: 无,略过
● 分析函数声明: 无,略过
执行阶段: 因为此时userName.AO = {}是个空对象,无法执行userName.AO.userName,所以会向上一层找,所以输出t.AO.userName的结果,也就是function userName() {console.log(userName);}}
例子5
<script> function t(userName) { console.log(userName);//这里输出什么? var userName = function () { console.log(userName);//这里输出什么? } userName(); } t('toby'); </script>
好吧,我保证这个是最后一道...这个输出结果是什么呢?我们只要坚定公式没问题,就一定能得出结果,那么再套公式走一波
t('toby')的分析和执行阶段
分析阶段:
● 创建AO对象,t.AO = {}
● 分析参数: t.AO = {userName : toby}
● 分析var声明: 有同名属性,不做任何事,还是t.AO = {userName : toby}
● 分析函数声明: 无,略过
执行阶段: 执行console.log(userName);时调用t.AO.userName 输出为toby,执行完后,代码继续往下执行,那么就到了进行var的赋值操作(var的分析和执行的区别看例子2中我有解释),此时t.AO = {userName : function userName() {console.log(userName);}}},代码继续往下执行,接着就执行到了userName()
userName()的分析和执行阶段
分析阶段:
● 创建AO对象,userName.AO = {}
● 分析参数: 无,略过
● 分析var声明: 无,略过
● 分析函数声明: 无,略过
执行阶段: 按照例子4我们知道userName.AO是个空对象,所以会往上调用t.AO.userName,所以输出为:function userName() {console.log(userName);}}}
总结
JavaScript作用域会先在自己的AO上找,找不到就到父函数的AO上找,再找不到再找上一层的AO,直到找到window.这样就形成一条链,这条AO链就是JavaScript中的作用域链.JavaScript中有两条很重要的链,一条是作用域链,一条是原型链,

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック









typedef struct は、構造体の使用を簡素化するために構造体型のエイリアスを作成するために C 言語で使用されます。構造体の別名を指定することで、新しいデータ型を既存の構造体に別名付けします。利点としては、可読性の向上、コードの再利用、型チェックなどが挙げられます。注: エイリアスを使用する前に構造体を定義する必要があります。エイリアスはプログラム内で一意であり、宣言されているスコープ内でのみ有効である必要があります。

Java における変数の期待値の例外は、変数の初期化、null 値の使用、およびローカル変数のスコープの認識によって解決できます。

JavaScript クロージャーの利点には、変数スコープの維持、モジュール化コードの有効化、遅延実行、およびイベント処理が含まれますが、欠点としては、メモリ リーク、複雑さの増加、パフォーマンスのオーバーヘッド、およびスコープ チェーンの影響が挙げられます。

C++ の #include プリプロセッサ ディレクティブは、外部ソース ファイルの内容を現在のソース ファイルに挿入し、その内容を現在のソース ファイル内の対応する場所にコピーします。主に、コード内で必要な宣言を含むヘッダー ファイルをインクルードするために使用されます。たとえば、標準入出力関数を組み込むための #include <iostream> などです。

C++ スマート ポインターのライフ サイクル: 作成: スマート ポインターは、メモリが割り当てられるときに作成されます。所有権の譲渡: 移動操作を通じて所有権を譲渡します。リリース: スマート ポインターがスコープ外に出るか、明示的に解放されると、メモリが解放されます。オブジェクトの破壊: ポイントされたオブジェクトが破壊されると、スマート ポインターは無効なポインターになります。

できる。 C++ では、ネストされた関数の定義と呼び出しが可能です。外部関数は組み込み関数を定義でき、内部関数はスコープ内で直接呼び出すことができます。ネストされた関数により、カプセル化、再利用性、スコープ制御が強化されます。ただし、内部関数は外部関数のローカル変数に直接アクセスすることはできず、戻り値の型は外部関数の宣言と一致している必要があります。内部関数は自己再帰的ではありません。

Vue では、let と var の間で変数を宣言するときのスコープに違いがあります。 スコープ: var にはグローバル スコープがあり、let にはブロック レベルのスコープがあります。ブロックレベルのスコープ: var はブロックレベルのスコープを作成しません。let はブロックレベルのスコープを作成します。再宣言: var は同じスコープ内の変数の再宣言を許可しますが、let は許可しません。

JavaScript では、this のポインティング タイプには、1. グローバル オブジェクト、2. 関数呼び出し、4. イベント ハンドラー、5. アロー関数 (this の外側の継承) が含まれます。さらに、bind()、call()、および apply() メソッドを使用して、これが何を指すかを明示的に設定できます。
