Maison >interface Web >js tutoriel >Explication détaillée du contexte d'exécution et de la pile d'exécution dans JS

Explication détaillée du contexte d'exécution et de la pile d'exécution dans JS

青灯夜游
青灯夜游avant
2020-11-06 17:47:031976parcourir

Explication détaillée du contexte d'exécution et de la pile d'exécution dans JS

Si vous êtes un développeur JavaScript, ou souhaitez devenir développeur JavaScript, alors vous devez connaître le mécanisme d'exécution interne d'un programme JavaScript. Le contexte d'exécution et la pile d'exécution sont l'un des concepts clés de JavaScript et l'une des difficultés de JavaScript. Comprendre le contexte d'exécution et la pile d'exécution permet également de comprendre d'autres concepts JavaScript tels que le mécanisme de levage, la portée et la fermeture. Cet article présente ces concepts de la manière la plus simple à comprendre possible.

Tutoriel recommandé : "Tutoriel vidéo JavaScript"

1. Contexte d'exécution (Contexte d'exécution)

1. 🎜>En bref, le contexte d'exécution est le concept abstrait de l'environnement dans lequel le code JavaScript actuel est analysé et exécuté. Tout code exécuté en JavaScript s'exécute dans le contexte d'exécution

Types de contexte d'exécution

.

Il existe trois types de contexte d'exécution :

Contexte d'exécution global : Il s'agit du contexte d'exécution par défaut et le plus basique. Le code qui n'est dans aucune fonction se trouve dans le contexte d'exécution global. Cela fait deux choses : 1. Créer un objet global dans le navigateur, cet objet global est l'objet fenêtre. 2. Pointez le pointeur this vers cet objet global. Un seul contexte d'exécution global peut exister dans un programme.
  • Contexte d'exécution de fonction : Chaque fois qu'une fonction est appelée, un nouveau contexte d'exécution est créé pour la fonction. Chaque fonction possède son propre contexte d'exécution, mais celui-ci n'est créé que lorsque la fonction est appelée. N'importe quel nombre de contextes d'exécution de fonctions peut exister dans un programme. Chaque fois qu'un nouveau contexte d'exécution est créé, il effectue une série d'étapes dans un ordre spécifique, qui seront abordées plus loin dans cet article.
  • Contexte d'exécution de la fonction Eval : Le code exécuté dans la fonction eval obtient également son propre contexte d'exécution, mais comme la fonction eval n'est pas couramment utilisée par les développeurs Javascript, elle ne sera pas abordée ici.
  • 2. Le cycle de vie du contexte d'exécution

Le cycle de vie du contexte d'exécution comprend trois phases :

Phase de création → Phase d'exécution → Phase de recyclage

, qui l'article se concentre sur la phase de création. 1. Phase de création

Lorsqu'une fonction est appelée, mais avant que l'un de ses codes internes ne soit exécuté, les trois choses suivantes seront effectuées :

Créer un objet variable : initialisez d'abord les paramètres de la fonction et promouvez la déclaration de fonction et la déclaration de variable. Ceci sera expliqué en détail ci-dessous.
  • Créer une chaîne de portée : lors de la phase de création du contexte d'exécution, la chaîne de portée est créée après l'objet variable. La chaîne de portée elle-même contient des objets variables. La chaîne de portée est utilisée pour résoudre les variables. Lorsqu'on lui demande d'analyser une variable, JavaScript démarre toujours au niveau le plus interne de l'imbrication du code. Si la variable n'est pas trouvée au niveau le plus interne, il passera au niveau supérieur de la portée parent jusqu'à ce qu'il trouve la variable.
  • Déterminez où cela pointe : y compris de nombreuses situations, qui seront expliquées en détail ci-dessous
  • Avant qu'un script JS ne soit exécuté, le code doit être analysé (JS est donc un langage de script qui interprète et exécute) , lors de l'analyse, un contexte d'exécution global sera créé en premier et toutes les déclarations de variables et de fonctions dans le code qui sera exécuté seront supprimées en premier. Les variables sont temporairement affectées à undefined et les fonctions sont déclarées et prêtes à l'emploi. Une fois cette étape terminée, le processus d’exécution formel peut alors commencer.

De plus, avant qu'une fonction ne soit exécutée, un contexte d'exécution de fonction sera également créé, qui est similaire au contexte global, mais le contexte d'exécution de fonction aura ces arguments et paramètres de fonction.

2. Phase d'exécution

Exécuter l'affectation des variables et l'exécution du code

3. Phase de recyclage

Le contexte d'exécution sort de la pile et attend le virtuel. machine pour recycler le contexte d'exécution

3. Détails de la promotion des variables et ce pointage

1 Promotion de la déclaration des variables

La plupart des langages de programmation déclarent d'abord les variables, puis les utilisent. , mais en JS, les choses sont un peu différentes. Idem :

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

Le code ci-dessus génère normalement

au lieu de signaler une erreur

Cela est dû au levage de déclaration, ce qui équivaut à ce qui suit. code : undefined

var a; //声明 默认值是undefined “准备工作”
console.log(a);
a = 10; //赋值
Uncaught ReferenceError: a is not defined 2. Levage de la déclaration de fonction

Nous savons tous qu'il existe deux façons de créer une fonction, l'une par déclaration de fonction

et l'autre par fonction expression function foo(){}, alors ces deux méthodes sont différentes dans la promotion de fonction Quelle est la différence ?

console.log(f1); // function f1(){}
function f1() {} // 函数声明
console.log(f2); // undefined
var f2 = function() {}; // 函数表达式
var foo = function(){}Ensuite, nous utilisons un exemple pour illustrer ce problème :

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();

Dans l'exemple ci-dessus, une erreur est signalée lorsque foo() est appelé, mais bar peut être appelé normalement.

Nous avons déjà dit que les variables et les fonctions augmenteront. Lorsqu'on rencontrera une expression de fonction

,

montera d'abord au sommet du corps de la fonction. Cependant, la valeur de foo à ce moment n'est pas définie. , donc exécution var foo = function(){}Signaler une erreur. var 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。

Explication détaillée du contexte dexécution et de la pile dexécution dans JS

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

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

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

Explication détaillée du contexte dexécution et de la pile dexécution dans 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 函数的执行上下文出栈,并且被销毁。

Explication détaillée du contexte dexécution et de la pile dexécution dans JS

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

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer