ホームページ  >  記事  >  ウェブフロントエンド  >  JSの実行コンテキストと実行スタックの詳細説明

JSの実行コンテキストと実行スタックの詳細説明

青灯夜游
青灯夜游転載
2020-11-06 17:47:031912ブラウズ

JSの実行コンテキストと実行スタックの詳細説明

JavaScript 開発者、または JavaScript 開発者になりたい場合は、JavaScript プログラムの内部実行メカニズムを理解する必要があります。実行コンテキストと実行スタックは、JavaScript の重要な概念の 1 つであり、JavaScript の難しさの 1 つです。実行コンテキストと実行スタックを理解することは、ホイスティング メカニズム、スコープ、クロージャなどの他の JavaScript の概念を理解するのにも役立ちます。この記事では、これらの概念をできるだけわかりやすく紹介します。

推奨チュートリアル: 「JavaScript ビデオ チュートリアル

1. 実行コンテキスト (Execution Context)

1. 実行コンテキストとは

要するに、実行コンテキストは、現在の JavaScript コードが解析および実行される環境の抽象的な概念です。JavaScript で実行されるコードはすべて、実行コンテキストで実行されます。

2. 実行コンテキストの種類

実行コンテキストには 3 つのタイプがあります:

  • グローバル実行コンテキスト: これはデフォルトで最も基本的な実行コンテキストです。どの関数にも含まれていないコードは、グローバル実行コンテキストにあります。 1. グローバル オブジェクトを作成しますブラウザでは、このグローバル オブジェクトはウィンドウ オブジェクトです。 2. this ポインタがこのグローバル オブジェクトを指すようにします。プログラム内に存在できるグローバル実行コンテキストは 1 つだけです。
  • 関数実行コンテキスト: 関数が呼び出されるたびに、その関数に対して新しい実行コンテキストが作成されます。各関数には独自の実行コンテキストがありますが、関数が呼び出されたときにのみ作成されます。関数実行コンテキストはプログラム内にいくつでも存在できます。新しい実行コンテキストが作成されるたびに、一連のステップが特定の順序で実行されます。これについては、この記事で後ほど説明します。
  • Eval 関数の実行コンテキスト: eval 関数で実行されるコードも独自の実行コンテキストを取得しますが、eval 関数は Javascript 開発者によって一般的に使用されないため、ここでは説明しません。

2. 実行コンテキストのライフ サイクル

実行コンテキストのライフ サイクルには 3 つの段階があります: 作成段階→実行段階→リサイクル段階、この記事創造段階に焦点を当てます。

1. 作成フェーズ

関数が呼び出されるとき、その内部コードが実行される前に、次の 3 つのことが行われます:

  • Create変数オブジェクト: まず関数のパラメータを初期化し、関数宣言と変数宣言を進めます。以下、これについて詳しく説明する。
  • スコープ チェーンの作成 (スコープ チェーン): 実行コンテキストの作成フェーズ中に、変数オブジェクトの後にスコープ チェーンが作成されます。スコープ チェーン自体には変数オブジェクトが含まれます。スコープ チェーンは変数を解決するために使用されます。変数を解決するよう求められると、JavaScript は常にコードのネストの最も内側のレベルから開始します。変数が最も内側のレベルで見つからない場合は、変数が見つかるまで 1 つ上のレベルの親スコープにジャンプします。
  • これがどこを指しているのかを判断します。これには、以下で詳しく説明する多くの状況が含まれます。

JS スクリプトを実行する前に、コードを解析する必要があります (つまり、JS はスクリプト言語です)解釈して実行する) を解析するときに、グローバル実行コンテキストが最初に作成され、実行されるコード内のすべての変数と関数の宣言が最初に取り出されます。変数は一時的に未定義に割り当てられ、関数は宣言されて使用できるようになります。このステップが完了すると、正式な実行プロセスを開始できます。

さらに、関数が実行される前に、関数実行コンテキストも作成されます。これはグローバル コンテキストに似ていますが、関数実行コンテキストにはこの引数と関数パラメーターが含まれます。

2. 実行フェーズ

実行変数の割り当て、コードの実行

3. リサイクル フェーズ

実行コンテキストはスタックからポップされ、実行コンテキストをリサイクルするための仮想マシン

3. 変数のプロモーションの詳細とこのポイント

##1. 変数宣言のプロモーション

##ほとんどのプログラミング言語は、最初に変数を宣言してから、同じ:

console.log(a); // undefined
var a = 10;

上記のコードは、通常、エラー

Uncaught ReferenceError: a が定義されていません## を報告する代わりに、

unknown を出力します。 #、これはステートメントの巻き上げが原因であり、次のコードと同等です:

var a; //声明 默认值是undefined “准备工作”
console.log(a);
a = 10; //赋值
2. 関数宣言のプロモーション

関数を作成するには 2 つの方法があることは誰もが知っています。関数宣言を通して

function foo(){}

もう 1 つは関数式を通してです var foo = function(){} では、関数の昇格における 2 つの違いは何ですか? ?

console.log(f1); // function f1(){}
function f1() {} // 函数声明
console.log(f2); // undefined
var f2 = function() {}; // 函数表达式
次に例を使用してこの問題を説明します。
function test() {
    foo(); // Uncaught TypeError "foo is not a function"
    bar(); // "this will run!"
    var foo = function() {
        // function expression assigned to local variable 'foo'
        alert("this won't run!");
    };
    function bar() {
        // function declaration, given the name 'bar'
        alert("this will run!");
    }
}
test();

上の例では、foo() が呼び出されたときにエラーが報告されますが、bar は通常どおり呼び出すことができます。

変数と関数が上昇すると前に述べましたが、関数式

var foo = function(){}

に遭遇すると、

var foo は最初に At the まで上昇します。ただし、この時点の foo の値は未定義であるため、foo() を実行するとエラーが報告されます。

而对于函数bar(), 则是提升了整个函数,所以bar()才能够顺利执行。

有个细节必须注意:当遇到函数和变量同名且都会被提升的情况,函数声明优先级比较高,因此变量声明会被函数声明所覆盖,但是可以重新赋值。

alert(a); //输出:function a(){ alert('我是函数') }
function a() {
    alert("我是函数");
} //
var a = "我是变量";
alert(a); //输出:'我是变量'

function 声明的优先级比 var 声明高,也就意味着当两个同名变量同时被 function 和 var 声明时,function 声明会覆盖 var 声明

这代码等效于:

function a() {
    alert("我是函数");
}
var a; //hoisting
alert(a); //输出:function a(){ alert('我是函数') }
a = "我是变量"; //赋值
alert(a); //输出:'我是变量'

最后我们看个复杂点的例子:

function test(arg) {
    // 1. 形参 arg 是 "hi"
    // 2. 因为函数声明比变量声明优先级高,所以此时 arg 是 function
    console.log(arg);
    var arg = "hello"; // 3.var arg 变量声明被忽略, arg = 'hello'被执行
    function arg() {
        console.log("hello world");
    }
    console.log(arg);
}
test("hi");
/* 输出:
function arg(){
    console.log('hello world') 
    }
hello 
*/

这是因为当函数执行的时候,首先会形成一个新的私有的作用域,然后依次按照如下的步骤执行:

  • 如果有形参,先给形参赋值
  • 进行私有作用域中的预解释,函数声明优先级比变量声明高,最后后者会被前者所覆盖,但是可以重新赋值
  • 私有作用域中的代码从上到下执行

3. 确定 this 的指向

先搞明白一个很重要的概念 —— this 的值是在执行的时候才能确认,定义的时候不能确认! 为什么呢 —— 因为 this 是执行上下文环境的一部分,而执行上下文需要在代码执行之前确定,而不是定义的时候。看如下例子:

// 情况1
function foo() {
  console.log(this.a) //1
}
var a = 1
foo()

// 情况2
function fn(){
  console.log(this);
}
var obj={fn:fn};
obj.fn(); //this->obj

// 情况3
function CreateJsPerson(name,age){
//this是当前类的一个实例p1
this.name=name; //=>p1.name=name
this.age=age; //=>p1.age=age
}
var p1=new CreateJsPerson("尹华芝",48);

// 情况4
function add(c, d){
  return this.a + this.b + c + d;
}
var o = {a:1, b:3};
add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16
add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34

// 情况5
<button id="btn1">箭头函数this</button>
<script type="text/javascript">
    let btn1 = document.getElementById(&#39;btn1&#39;);
    let obj = {
        name: &#39;kobe&#39;,
        age: 39,
        getName: function () {
            btn1.onclick = () => {
                console.log(this);//obj
            };
        }
    };
    obj.getName();
</script>

接下来我们逐一解释上面几种情况

  • 对于直接调用 foo 来说,不管 foo 函数被放在了什么地方,this 一定是 window
  • 对于 obj.foo() 来说,我们只需要记住,谁调用了函数,谁就是 this,所以在这个场景下 foo 函数中的 this 就是 obj 对象
  • 在构造函数模式中,类中(函数体中)出现的 this.xxx=xxx 中的 this 是当前类的一个实例
  • call、apply 和 bind:this 是第一个参数
  • 箭头函数 this 指向:箭头函数没有自己的 this,看其外层的是否有函数,如果有,外层函数的 this 就是内部箭头函数的 this,如果没有,则 this 是 window。

JSの実行コンテキストと実行スタックの詳細説明

四、执行上下文栈(Execution Context Stack)

函数多了,就有多个函数执行上下文,每次调用函数创建一个新的执行上下文,那如何管理创建的那么多执行上下文呢?

JavaScript 引擎创建了执行上下文栈来管理执行上下文。可以把执行上下文栈认为是一个存储函数调用的栈结构,遵循先进后出的原则

JSの実行コンテキストと実行スタックの詳細説明

从上面的流程图,我们需要记住几个关键点:

  • JavaScript 执行在单线程上,所有的代码都是排队执行。
  • 一开始浏览器执行全局的代码时,首先创建全局的执行上下文,压入执行栈的顶部。
  • 每当进入一个函数的执行就会创建函数的执行上下文,并且把它压入执行栈的顶部。当前函数执行完成后,当前函数的执行上下文出栈,并等待垃圾回收。
  • 浏览器的 JS 执行引擎总是访问栈顶的执行上下文。
  • 全局上下文只有唯一的一个,它在浏览器关闭时出栈。

我们再来看个例子:

var color = "blue";
function changeColor() {
    var anotherColor = "red";
    function swapColors() {
        var tempColor = anotherColor;
        anotherColor = color;
        color = tempColor;
    }
    swapColors();
}
changeColor();

上述代码运行按照如下步骤:

  • 当上述代码在浏览器中加载时,JavaScript 引擎会创建一个全局执行上下文并且将它推入当前的执行栈
  • 调用 changeColor 函数时,此时 changeColor 函数内部代码还未执行,js 执行引擎立即创建一个 changeColor 的执行上下文(简称 EC),然后把这执行上下文压入到执行栈(简称 ECStack)中。
  • 执行 changeColor 函数过程中,调用 swapColors 函数,同样地,swapColors 函数执行之前也创建了一个 swapColors 的执行上下文,并压入到执行栈中。
  • swapColors 函数执行完成,swapColors 函数的执行上下文出栈,并且被销毁。
  • changeColor 函数执行完成,changeColor 函数的执行上下文出栈,并且被销毁。

JSの実行コンテキストと実行スタックの詳細説明

更多编程相关知识,请访问:编程学习网站!!

以上がJSの実行コンテキストと実行スタックの詳細説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はcnblogs.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。