If you are a JavaScript developer, or want to become a JavaScript developer, then you must know the internal execution mechanism of a JavaScript program. Execution context and execution stack are one of the key concepts in JavaScript and one of the difficulties in JavaScript. Understanding execution context and execution stack also helps to understand other JavaScript concepts such as hoisting mechanism, scope and closure. This article introduces these concepts in as easy-to-understand a way as possible.
Recommended tutorial: "JavaScript Video Tutorial"
In short, execution context is the abstract concept of the environment in which the current JavaScript code is parsed and executed. Any code running in JavaScript is run in the execution context
There are three types of execution context:
The life cycle of execution context includes three stages: Creation stage→Execution stage→Recycling stage, this article focuses on Creation phase.
When the function is called, but before any of its internal code is executed, the following three things will be done:
Before a JS script is executed, the code must be parsed (so JS is a scripting language that interprets and executes) , when parsing, a global execution context will be created first, and all variable and function declarations in the code that will be executed will be taken out first. Variables are temporarily assigned to undefined, and functions are declared and ready for use. After this step is completed, the formal execution process can then begin.
In addition, before a function is executed, a function execution context will also be created, which is similar to the global context, but the function execution context will have this arguments and function parameters.
Execution variable assignment, code execution
The execution context is popped from the stack and waits for the virtual machine to recycle the execution context
Most programming languages declare variables first and then use them, but in JS, things are a little different. Same:
console.log(a); // undefined var a = 10;
The above code normally outputs undefined
instead of reporting an error Uncaught ReferenceError: a is not defined
, this is because of statement hoisting, which is equivalent to the following code :
var a; //声明 默认值是undefined “准备工作” console.log(a); a = 10; //赋值
We all know that there are two ways to create a function, one is through function declarationfunction foo(){}
The other is through function expressionvar foo = function(){}
, so what is the difference between the two in function promotion?
console.log(f1); // function f1(){} function f1() {} // 函数声明 console.log(f2); // undefined var f2 = function() {}; // 函数表达式
Next we use an example to illustrate this problem:
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();
In the above example, an error is reported when foo() is called, but bar can be called normally.
We said before that variables and functions will rise. When encountering the function expression var foo = function(){}
, var foo
will first rise to At the top of the function body, however, the value of foo at this time is undefined, so an error is reported when foo()
is executed.
而对于函数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 */
这是因为当函数执行的时候,首先会形成一个新的私有的作用域,然后依次按照如下的步骤执行:
先搞明白一个很重要的概念 —— 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('btn1'); let obj = { name: 'kobe', age: 39, getName: function () { btn1.onclick = () => { console.log(this);//obj }; } }; obj.getName(); </script>
接下来我们逐一解释上面几种情况
函数多了,就有多个函数执行上下文,每次调用函数创建一个新的执行上下文,那如何管理创建的那么多执行上下文呢?
JavaScript 引擎创建了执行上下文栈来管理执行上下文。可以把执行上下文栈认为是一个存储函数调用的栈结构,遵循先进后出的原则。
从上面的流程图,我们需要记住几个关键点:
我们再来看个例子:
var color = "blue"; function changeColor() { var anotherColor = "red"; function swapColors() { var tempColor = anotherColor; anotherColor = color; color = tempColor; } swapColors(); } changeColor();
上述代码运行按照如下步骤:
更多编程相关知识,请访问:编程学习网站!!
The above is the detailed content of Detailed explanation of execution context and execution stack in JS. For more information, please follow other related articles on the PHP Chinese website!