JavaScript 개발자이거나 JavaScript 개발자가 되고 싶다면 JavaScript 프로그램의 내부 실행 메커니즘을 알아야 합니다. 실행 컨텍스트와 실행 스택은 JavaScript의 핵심 개념 중 하나이며 JavaScript의 어려움 중 하나입니다. 실행 컨텍스트 및 실행 스택을 이해하면 호이스팅 메커니즘, 범위 및 클로저와 같은 다른 JavaScript 개념을 이해하는 데도 도움이 됩니다. 이 기사에서는 이러한 개념을 최대한 이해하기 쉬운 방식으로 소개합니다.
추천 튜토리얼: "JavaScript 동영상 튜토리얼"
간단히 말해서 실행 컨텍스트는 현재 JavaScript 코드가 구문 분석되고 실행되는 환경입니다. JavaScript에서 실행되는 코드는 실행 컨텍스트에서 실행됩니다.
실행 컨텍스트에는 세 가지 유형이 있습니다.
실행 컨텍스트의 수명주기는 생성 단계 → 실행 단계 → 재활용 단계 의 세 단계로 구성됩니다.
함수가 호출되면 내부 코드가 실행되기 전에 다음 세 가지 작업이 수행됩니다.
JS 스크립트가 실행되기 전에 코드를 구문 분석해야 합니다(따라서 JS는 구문 분석하는 동안 해석하고 실행하는 스크립트 언어입니다). 전역 실행이 먼저 생성됩니다. 컨텍스트를 위해 먼저 코드에서 실행될 모든 변수 및 함수 선언을 꺼냅니다. 변수는 임시로 정의되지 않은 상태로 할당되고, 함수는 선언되어 사용할 준비가 됩니다. 이 단계가 완료되면 공식적인 실행 프로세스가 시작될 수 있습니다.
또한, 함수가 실행되기 전에 함수 실행 컨텍스트도 생성되는데, 이는 전역 컨텍스트와 유사하지만 함수 실행 컨텍스트에는 이러한 인수와 함수 매개변수가 있습니다.
변수 할당 및 코드 실행 수행
실행 컨텍스트가 스택에서 꺼내지고 가상 머신이 실행 컨텍스트를 재활용할 때까지 기다립니다.
대부분의 프로그래밍 언어는 변수를 먼저 선언한 다음 사용하지만 JS에서는 상황이 조금 다릅니다.
console.log(a); // undefined var a = 10;
위 코드는 일반적으로 정의되지 않은</code를 출력합니다. > <code>Uncaught ReferenceError: a is not Defined
오류를 보고하는 대신 이는 다음 코드와 동일한 선언 호이스팅 때문입니다. undefined
而不是报错Uncaught ReferenceError: a is not defined
,这是因为声明提升(hoisting),相当于如下代码:
var a; //声明 默认值是undefined “准备工作” console.log(a); a = 10; //赋值
我们都知道,创建一个函数的方法有两种,一种是通过函数声明function foo(){}
另一种是通过函数表达式var foo = function(){}
,那这两种在函数提升有什么区别呢?
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
上升到函数体顶部,然而此时的 foo 的值为 undefined,所以执行foo()
alert(a); //输出:function a(){ alert('我是函数') } function a() { alert("我是函数"); } // var a = "我是变量"; alert(a); //输出:'我是变量'
function foo(){}
var foo = function(){}
, 그러면 이 두 가지 유형의 함수 승격 간의 차이점은 무엇입니까? 🎜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 */
var foo = function(){}
을 만나면 var foo
가 먼저 함수 본문으로 상승합니다. 다만, 상단에는 현재 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 */
这是因为当函数执行的时候,首先会形成一个新的私有的作用域,然后依次按照如下的步骤执行:
先搞明白一个很重要的概念 —— 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();
上述代码运行按照如下步骤:
更多编程相关知识,请访问:编程学习网站!!
위 내용은 JS의 실행 컨텍스트 및 실행 스택에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!