JS的执行上下文,变量声明提升,函数声明提升

一、什么是执行上下文(execution content)

一句话:执行上下文就是当前JS代码被解析和执行时存在的环境。(ECMAScript中定义的抽象概念)

二、执行上下文的类型

  1. 全局执行上下文:这是默认的,最基础的执行上下文,只有一个
  • 不在函数内部的代码都位于全局执行上下文中

  • 创建一个全局对象,其实就是我们的window对象

  • 将this指向这个全局对象

  1. 函数执行上下文:每次函数被调用的时候,就会创建一个新的执行上下文
  • 每个函数都会有自己的执行上下文
  • 一个程序中可以存在任意数量的函数执行上下文
  • 每一个函数执行上下文被创建的时候,它都会按照特定的顺序执行一系列的步骤。
  1. eval函数执行上下文:运行在eval函数中的代码会获得自己的执行上下文。(很少用到,不做讨论)

三、执行上下文的生命周期(以函数执行上下文为例)

创建阶段 => 执行阶段 => 回收阶段

  1. 创建阶段

当函数被调用,但是未执行内部的任何代码之前。

这个阶段会做出以下几件事:

  • 创建变量对象:首先会初始化函数的参数arguments,提升函数声明和变量声明。

  • 创建作用域链:在执行上下文创建阶段,作用域链是在变量对象之后创建的,作用域链本身包含变量对象,作用域链用来解析变量。

  • 确定this指向

    • function函数是在被调用时确定this指向
    • 箭头函数在声明时就已经确定了this指向,箭头函数的this由其所处的上下文决定
  1. 执行阶段
  • 变量赋值,执行代码
  1. 回收阶段
  • 执行上下文出栈,JS自动执行垃圾回收机制。

四、变量声明提升

我们看下边这段代码:

在变量还没有声明的时候进行打印,会打印出什么呢?

<script>
       console.log(a);
       var a = 10;
</script>

结果:
在这里插入图片描述
我们看到不但没有报错 Uncaught ReferenceError: Cannot access ‘a’ before initialization(变量a未定义),还打印出了undefined。

事实上,在执行代码之前,我们的浏览器会先解析一遍我们的脚本,完成一个初始化的步骤,它遇到var变量时就会先初始化变量为undefined。

这就是变量提升(hoisting),它是指,浏览器在遇到JS执行环境的初始化时,引起的变量提前定义。

如何避免变量提升呢?

使用let,const 关键字声明变量,尽量使用从上图,避免使用var

<script>
       console.log(a);//报错:Uncaught ReferenceError: Cannot access 'a' before initialization
       let  a = 10;

       /*--------------------分割线---------------*/

       console.log(b);//报错:Uncaught ReferenceError: Cannot access 'a' before initialization
       const b = 20;
    </script>

五、函数声明提升

  • 声明函数的方法(箭头函数属于第二种声明方式)

    • 函数声明方式:function foo(){}
    • 函数表达式方式:var foo = function(){}或var foo = ()=>{}

我们一起看一段代码:

 <script>
    console.log(f1); //ƒ f1(){}
    function f1(){}
    /*--------------分界线-------------*/
    console.log(f2);//undefined
    var f2 = function(){}
 </script>

同样我们看到,在声明函数之前打印该函数,打印出了内容,这是因为函数提升,就是初始化时,引擎把函数声明整个提升到了当前作用域的顶部。在实际打印之前,函数已经被声明。

至于第一个与第二个函数打印结果不同,我们结合第四点,变量提升,第二个函数使用函数表达式的方式声明,将函数赋值给了一个变量,这个变量在提升时被初始化为undefined。

而第一个函数使用函数声明方式声明,打印的是该函数本身。

注意:

  • 当函数和变量同名时,函数声明的优先级高于变量声明的优先级,因此变量声明会被函数声明给覆盖掉,但是可以重新赋值。
<script>
	alert(a)//弹出function a(){alert('我是函数');}
	var a = "我是变量"
	function a(){alert('我是函数');}
</script>
  • 如果在同一个作用域中存在多个同名函数声明,后面出现的将会覆盖前面的函数声明。
function hoistFunction() {
    function foo() {
        console.log(1);
    }

    foo(); // 2

    function foo() {
        console.log(2);
    }
}

hoistFunction();
  • 函数声明比函数表达式声明优先级高。
function hoistFunction() {
            foo() // 2
            
            var foo = function () {
                console.log(1);
            };

            foo() // 1

            function foo() {
                console.log(2);
            }

            foo(); // 1
        }

        hoistFunction();
//解释JS中,函数是一等公民,函数声明的优先级最高,会被提升到当前作用域的最顶端,所以第一次调用时实际执行了下面的函数声明,第二次调用时,由于前边的函数表达式与之前的函数声明同名,故将其覆盖,以后的调用也将会打印出相同的结果。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一个简单的JavaScript代码,用来演示执行上下文的实际应用,并且我会在代码中加上注释来解释执行上下文的一些基本概念: ``` var a = 10; // 全局变量 a 被声明并赋值为 10 function foo(x, y) { // 声明一个名为 foo 的函数,并定义两个参数 x 和 y var b = 5; // 函数内部变量 b 被声明并赋值为 5 console.log(x + y + a + b); // 输出 x + y + a + b 的结果 } foo(3, 4); // 调用函数 foo,并传入参数 3 和 4 ``` 上述代码中,首先声明并赋值了一个全局变量 a。接着,定义了一个名为 foo 的函数,并在函数内部声明并赋值了一个变量 b。最后调用函数 foo,传入参数 3 和 4。 当代码执行到第一行时,JavaScript引擎会创建一个全局执行上下文对象,并将变量 a 添加到该对象的变量对象中。接着,当执行函数 foo 时,JavaScript引擎会创建一个新的执行上下文对象,并将其添加到执行栈的顶部。在函数 foo 的执行上下文对象中,JavaScript引擎会将参数 x 和 y 添加到其变量对象中,并将变量 b 的声明也添加到变量对象中。 当函数 foo 执行到 console.log 语句时,JavaScript引擎会在当前执行上下文对象的变量对象、其父级执行上下文对象的变量对象等作用域链中查找变量 x、y、a 和 b 的值,并将它们相加后输出。 最后,当函数 foo 执行完毕后,JavaScript引擎会将其执行上下文对象从执行栈中弹出,并将控制权返回给全局执行上下文对象,代码执行结束。 希望这个例子能够帮助您更好地理解JavaScript执行上下文

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值