一文搞懂JS系列(一)之编译原理,作用域,作用域链,变量提升

写在最前面:这是我即将开始写的一个系列,主要是在框架横行的时代,虽然上班用的是框架,但是对于面试,以及技术进阶,JS基础知识的铺垫是锦上添花,也是不得不学习的一块知识,虽然开汽车的不需要很懂汽车,只需要掌握汽车的常用功能即可。但是如果你懂汽车,那你也能更好地开车,同理。当然,一篇文章也不会光光只讲一个知识点,一般会将有关联的知识点串联起来,一边记录自己的学习,一边分享自己的学习,互勉!如果可以的话,也请给我点个赞,你的点赞也能让我更加努力地更新!

概览

  • 食用时间: 5-10分钟
  • 难度: 简单,别跑,看完再走

JS编译原理

我们先来看一行代码

var name='jack';

在我们眼中,这是一行代码,一个语句就能搞定的事情呀,但是,在JS的眼里,这句话的代码应该是下面这种方式呈现的

var name;    //编译阶段处理
name='jack';    //执行阶段处理

是不是发现这和你原本认识的JS不太一样,那是因为JS编译主要分为两个阶段,编译阶段和执行阶段,让我们首先来看下这两个阶段分别做了那些事情:

  • 编译阶段

    这个阶段的主角就是所谓的编译器,这个编译器会找遍当前作用域,看看是不是已经存在一个叫 name 的变量。如果已经存在,那么就什么都不做,直接忽略 var name 这个声明,继续编译下去;如果没有,则在当前作用域里新增一个叫 name 的变量。然后,编译器会为引擎生成运行时所需要的代码,程序就进入了执行阶段

  • 执行阶段

    这个阶段的主角就是大家所熟悉的JS引擎啦,JS引擎在运行的时候,也会先找遍当前作用域,看看是否有一个叫 name 的变量,如果有的话,那么刚刚好,直接赋值,如果没有的话,那就是当前作用域没有,那怎么办,那就考虑探出头去,去外面( 父级作用域 )看看有没有,没有的话,再去外面查找,一层又一层( 当然如果是还有父层的话 ),如果最终还是找不到的话,那么JS引擎也表示无能为力,那就抛个异常给别人看看吧,表示自己已经努力过了。

    上面提到的去外面查找,一层又一层,从当前作用域再到父级作用域,再到父级的父级作用域,以此类推,就是所谓的作用域链了,就跟链条一样,一节有一节往上,是不是形容地可以说是很贴切了。总结而言就是,作用域套作用域,也就有了所谓的作用域链

作用域

  • 定义

    大家都知道,变量最基本的能力就是能够存储变量当中的值、并且允许我们对这个变量的值进行访问和修改,而对于变量存储,访问的一套规则,就是所谓的作用域

  • 分类

    • 全局作用域

    在任何函数外或者代码块之外的顶层作用域就是全局作用域,而里面的变量就是全局变量

    var name='jack';   //全局作用域
    
    function showName(){    //函数作用域
      console.log(name);
    }
    
    {
      name='test';    //块级作用域
    }
    
    showName();    //test
    

    可以看到全局变量,无论是在全局作用域,函数作用,还是块级作用域中都可以正常访问

    • 函数作用域

    在函数内的作用域就是函数作用域

    function showName(){
      var name='jack';    //函数作用域
    }
    
    showName();   //方法调用
    
    {
      console.log(name);  //块级作用域,Uncaught ReferenceError: name is not defined
    }
    
    console.log(name);  //全局作用域,Uncaught ReferenceError: name is not defined
    

    可以看到函数内部变量,在全局作用域以及块级作用域中,都无法访问,只有在函数内部,才能访问的到,所以函数内的变量也被称为局部变量

    • 块作用域

    ES6 中新出的两个新关键字 letconst中,自带块级作用域,块级作用域相当于是只在这块代码块中生效,如果它被大括号 {} 所包围,那么大括号中就是一段代码块,代码块中使用 letconst 声明的变量也被称为局部变量

     {
       let name='jack';
     }
    
     console.log(name);    //Uncaught ReferenceError: name is not defined
    
     function showName{
       console.log(name);
     }
    
     showName();    //Uncaught ReferenceError: name is not defined
    

    可以看到块级作用域中的变量,出了那个代码块,就找不到了

其实上面的三种情况,结合JS编译原理和作用域链向外不向内查找,思考一下,也不难理解

作用域链

回到作用域链,其实在上面已经解释的差不多了,作用域和作用域的嵌套,就产生了作用域链,另外要记住的一个特性就是作用域链的查找,向外不向内,想想探出头去,而不是看着锅里,就可以了

变量提升

先来看一段代码

name='jack';
console.log(name);    //jack
var name;

你会发现,这段代码不会发生报错,并且能正常地运行,结合上面所说的JS编译原理,你就能想到,在JS的眼中,它的代码实际上是这样子的,这就是所谓的代码提升,说白了那就是代码的声明提到代码的最前面

var name;
name='jack';
console.log(name);    //jack

其实这个变量提升应该是照道理接着编译原理写下来的,为什么放到了最后呢,因为如果你忘了,正好往上翻一下,重新回温一遍JS编译原理

紧接着上面,让我们来看下不吃变量提升这一套的 letconst ,先来看一段代码

name='jack';
console.log(name)    //Uncaught ReferenceError: Cannot access 'name' before initialization
let name;

黑人问号 ??? ,说好的变量提升呢,记住 letconst 的一个特点,禁用变量提升,这也是 ES6 故意为之的,将生命前不可用做到了强约束,总结而言,** var 存在变量提升, letconst 不存在变量提升**

既然已经提到了 const ,顺带提一下它声明了以后必须赋值的操作

const name;    //Uncaught SyntaxError: Missing initializer in const declaration

暂时性死区

紧接着上面,让我们来看下什么叫做暂时性死区,先来看一段代码

var name='jack';

{
  name='bob';
  let name;    //Uncaught ReferenceError: Cannot access 'name' before initialization
}

记住 ES6 中的一个特性,如果区块中存在 letconst 命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。因为JS清楚地感知到了 name 是用 let 声明在当前这个代码块内的,所以会给这个变量 name 加上了暂时性死区的限制,它就不往外探出头了。

那么,如果我们把上面的let name;去掉,程序也将正常运行, name 的值也被成功修改为了bob,就是正常地按照作用域链的规则,向外探出头去了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,313评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,369评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,916评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,333评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,425评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,481评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,491评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,268评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,719评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,004评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,179评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,832评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,510评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,153评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,402评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,045评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,071评论 2 352