浏览器中的JavaScript代码执行机制

1.变量提升

JavaScript代码在执行的时候,利用var声明的变量是会提升到代码的开头并且赋值为unddefined。
使用函数声明创建的函数也会被提升,称为函数提升。
上述两个机制的存在使得我们可以在变量和函数声明之前使用它们。
JavaScript代码会经过JavaScript引擎的编译之后再执行。代码经过编译之后,会生成包含变量对象和词法环境的执行上下文以及可执行代码。变量对象中保存的就是被提升的变量声明和函数声明。如果遇到var声明的变量,变量对象中就会生成一个属性名为变量名、属性值为undefined的属性;如果遇到函数声明,就会生成一个方法名为函数名,方法为函数本身的方法(函数存储在堆内存中,方法名保留的是地址值。)
简单总结执行机制。

  1. 编译。进行变量和函数声明的提升,存储在变量对象中。
  2. 执行。顺序执行可执行的代码。

2.调用栈

JavaScript引擎在执行代码的时候,会创建执行上下文。主要有全局执行上下文,函数执行上下文和eval函数的执行上下文。首先在执行JavaScript代码前,就会创建一个全局执行上下文压入栈,然后根据函数的调用顺序依次将执行上下文压入栈。函数内部代码被执行完毕,对应的执行上下文就会被弹出栈。
执行上下文中包含变量对象(VO)和词法环境两个部分,执行上下文是在JavaScript引擎编译阶段创建的,也就是代码执行前。
一段代码

    var a = 2
    function add(b,c){
        return b+c
    }
    function addAll(b,c){
        var d = 10
        result = add(b,c)
        return  a+result+d
    }
    addAll(3,6)

在开发者工具中给代码打上断点之后,在右边的调用栈堆里面能看见目前调用栈中的执行上下文有哪些。



或者给代码加上一个console.trace也可以。

    var a = 2
    function add(b,c){
        console.trace()//查看函数调用关系
        return b+c
    }
    function addAll(b,c){
        var d = 10
        result = add(b,c)
        return  a+result+d
    }
    addAll(3,6)

控制台输出结果:



栈溢出问题,执行上下文的调用栈是有大小的。如果创建一个递归函数并且不设置终止条件,最终就会发生栈溢出。为避免栈溢出,需要尽量将递归任务分解成其他的小型任务来执行。

3.块级作用域

ES6之前JavaScript只有全局作用域和函数作用域,ES6通过let和const实现了块级作用域。简单来说一对大括号就是一个块级作用域。ES6如何同时支持变量提升和块级作用域?
前面提到,函数调用时会创建执行上下文,执行上下文中有两个部分,变量对象和词法环境。支持块级作用域实际上就是通过词法环境实现的。比如下面这个代码:

function foo(){
    var a = 1
    let b = 2
    {
      let b = 3
      var c = 4
      let d = 5
      console.log(a)
      console.log(b)
    }
    console.log(b) 
    console.log(c)
    console.log(d)
}   
foo()

来分析一下这段代码的如何执行,以及执行上下文的情况。

  • 1.调用函数,创建执行上下文。变量对象中有a,c两个变量,值都是undefined(var声明的变量的变量提升导致的)。词法环境中有一个变量b,但是由于let会生成暂时性死区,所以虽然b已经存在了,但是在声明b之前的语句去访问b浏览器都会报错。
  • 2.执行大括号内部代码,此时a=1,b=2。此时由于进入了一个新的作用域块,所以词法环境中会将这个块压入栈,这个新的块中也有两个变量b,d,同样在它们声明之前不可访问。
  • 3.console.log(a)。执行这句代码的时候,就涉及到了如何在词法环境和变量对象中寻找变量了。首先会查找位于词法环境的栈顶的块中的变量,然后沿着这个顺序一直向下查找到位于栈底的块,如果都没有找到则会进入变量对象中去寻找变量。也就是说,优先在词法环境中寻找,其后再到变量对象中寻找。

4.作用域和闭包

作用域。始终记住作用域是静态的,并且作用域在函数被定义的时候就已经是确定了的,不会再发生改变了。
闭包。记住闭包产生的两个条件,函数嵌套以及内部的函数引用了外部函数的变量。有了闭包,即使外部函数已经执行完毕,它的执行上下文也已经被弹出了执行栈,但是内部的变量还是会被保留下来,当内部函数被调用,这些被保存的变量随时可以被这个内部函数调用。就像一个专属于内部函数的背包一样。
闭包的存在使得变量查找的作用域链发生了一定变化。首先会在内部函数自身的执行上下文中寻找,之后进入闭包中寻找,最后进入全局作用域寻找。

5.this

  1. 函数直接以函数形式被调用的时候,this指向全局对象window
  2. call apply bind可以修改函数的this指向,使其指向参数中的对象
  3. 作为对象的方法被调用的时候,指向调用该方法的对象
  4. 构造函数形式被调用,this指向利用构造函数创建的对象
  5. 箭头函数的this是静态的,指向其被创建时所在的对象
    嵌套函数,内部的函数不会继承外部函数的this指向

6.小结

本节重点:

  1. JavaScript代码也是需要先编译后执行的,编译的工作由JavaScript引擎来完成。
  2. 代码经过编译后会生成执行上下文和可执行代码,执行上下文中由变量对象和词法环境。也解释了为什么JavaScript会存在变量提升,其实是JavaScript引擎编译的锅。
  3. var声明和函数声明的变量和函数会被存储在变量环境中,变量的初始化值是undefined,函数则是函数的定义。let,const声明的变量被存储在词法环境中,形成暂时性死区,也就是在声明变量前不能对他们进行访问。
  4. 每进入一个新的代码块。词法环境会创建一个新的块压入栈,里面是这个代码块中使用let和const声明的变量。
  5. JavaScript寻找变量的方式。首先在词法环境栈顶的块中寻找,然后依次向下到栈底的块,最后再到变量对象中寻找。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,519评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,842评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,544评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,742评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,646评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,027评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,513评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,169评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,324评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,268评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,299评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,996评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,591评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,667评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,911评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,288评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,871评论 2 341

推荐阅读更多精彩内容