最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 从 JS 执行机制理解变量提升等特性

    正文概述 掘金(小林别闹)   2021-05-09   437

    对于 Javascript 的执行机制的认识还比较浅,恰好最近有看到李兵老师浏览器的课程(得付费哟),了解了 JavaScript 的执行机制,对于变量提升,暂时性死区的理解能够加深,也能够初步复刻出代码的执行。

    showName()
    console.log(myName)
    var myName = '小林别闹'
    console.log(yourName)
    let yourName = '小林加油'
    function showName() {
        console.log('函数showName被执行');
    }
    

    从 JS 执行机制理解变量提升等特性

    如果您也对上边代码的运行结果感到疑惑或者感兴趣,那么请阅读下去吧

    1.编译

    首先可以明确,代码先编译再执行

    编译的过程比较复杂,俺暂时也不是很清楚,感兴趣的朋友可以去学一下,然后告诉我(笑),引用一段李兵老师的话

    我们可以暂时将这段代码的编译结果可以分为两部分:执行上下文可执行代码

    另外 var myName = '小林别闹' 这个语句就可以分为声明和赋值两部分

    var myName    //声明部分
    myName = '小林别闹'  //赋值部分
    

    1.1执行上下文

    执行上下文有三种(开始学习的时候误以为块作用域都有执行上下文):

    • 全局执行上下文:只有一个,程序首次运行时创建,一直压在调用栈的底部,直到程序运行结束

    • 函数执行上下文:函数被调用时创建,每次调用都会为该函数创建一个新的执行上下文,不管这个函数是不是重复调用

    • Eval 函数执行上下文:运行eval函数中的代码时创建的执行上下文,少用且不建议使用,俺也不太清楚

    执行上下文可以暂时理解为包括****变量环境词法环境两个对象

    文章开头的代码编译后的执行上下文大致如图:

    从 JS 执行机制理解变量提升等特性

    编译的时候,JavaScript 引擎把由 var 声明的变量和函数的声明部分,提到了变量环境对象中,给定默认值 undefined。JavaScript 引擎发现了一个通过 function 定义的函数,所以它将函数定义存储到堆 (HEAP)中,并在环境对象中创建一个 showName 的属性

    将由 letconst 声明的变量的声明部分提到了词法环境对象词法环境由一个栈结构管理。同样给定默认值 undefined。当执行上下文没有切换时,当遇到新的块作用域,用 letconst 声明的变量会创建新的词法环境。新创建的词法环境会压入这个栈中,并伴随相关语句的结束弹出销毁。

    1.2可执行代码

    编译完后就是执行了,是一行一行的执行的哦,执行代码大致长这样:

    showName()//函数声明提升到变量环境中了,所以是可以执行的,输出那句'函数showName被执行'
    console.log(myname)//var 定义的变量也提升到变量环境中,并默认为undefined,所以输出undefined
    myname = '小林别闹'//将变量环境中myname的值改成 "小林别闹"
    console.log(yourName)//let 定义的变量再词法环境中,默认为undefined,但是javascript不允许 let 定义的变量在声明前使用,所以会报错
    

    这样对于文章开头代码的执行结果是不是清楚了呢

    2.调用栈

    函数只有在调用的时候才会被编译。调用一个函数就会有一个新的执行上下文,通过一个栈结构来管理这些上下文,进而管理函数之间的调用关系。这个栈就叫调用栈,文章开头的代码其实就有两个执行上下文showName 函数执行完毕,该执行上下文就从调用栈内出来。

    从 JS 执行机制理解变量提升等特性

    2.1栈溢出

    栈的大小是有限的,超过栈的大小就会造成栈溢出,比如说递归调用

    // function foo(){
    //    return setTimeout(foo, 0);
    //}
    function foo(){
        return foo();
    }
    foo()
    

    从 JS 执行机制理解变量提升等特性

    为啥 setTimeout 能够避免栈溢出呢,这是因为 setTimeout 是异步的,会被交给 Web API,时间触发时,回调函数被送到任务队列,事件循环不断地监视任务队列,并按它们排队的顺序一次处理一个回调。每当调用堆栈为空时,事件循环获取回调并将其放入堆栈中进行处理。请记住,如果调用堆栈不是空的,则事件循环不会将任何回调推入堆栈

    还可以,设置一个变量depth用来表示调用的层数,当超过一定层数时,则终止递归

    3.作用域链

    在编写代码的时候,如果你使用了一个在当前作用域中不存在的变量,这时 JavaScript 引擎就需要按照作用域链在其他作用域中查找该变量

    这是因为 JavaScript 语言的作用域链是由词法作用域决定的,而词法作用域是由代码结构来确定的,也就是代码中函数声明的位置来决定的

    在执行上下文中寻找变量按照的是先词法环境,再变量环境,调用栈的顺序就是函数的调用顺序,执行上下文之间,查找变量就沿着作用域链来查找,作用域的顺序和调用顺序无关,仅和声明顺序有关

    var a = 'A'
    let b = "B"
    let c = "C"
    function foo() {
        console.log( a ); 
    }
    function bar() {
        var a = "a";
        foo();
        console.log(c)
        let c = "c" 
    }
    bar();
    
    

    结果如图:

    从 JS 执行机制理解变量提升等特性 foobar 函数都声明在全局作用域,它们沿着作用域链找变量的时候当然就找到了全局执行上下文这里了,但是在bar 函数里面通过 let 声明了 c 所以 bar 找到的是自己的词法环境里面的 c 但是在赋值前调用,Javascript 引擎不允许,所以报错了。下面的图应该画错了(哭),bar函数执行上下文的词法环境里面,c 应该还是 undefined ,因为报错了,下面的代码应该不会执行下去了。

    从 JS 执行机制理解变量提升等特性

    顺带提一下,执行上下文里边东西挺多的,可以参考下图:

    从 JS 执行机制理解变量提升等特性

    outer就是一个外部引用,用来指向外部的执行上下,这个外部是沿着作用域链的,和调用没关系哦

    this 应该不陌生了,非严格模式下,普通函数中的this都是window,当然你可以这样记住:this总是指向调用者,咱们的箭头函数都没有this,都是外部的

    参考:

    8个问题看你是否真的懂 JS

    浏览器工作原理与实践


    起源地下载网 » 从 JS 执行机制理解变量提升等特性

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元