Js 作用域及相关概念

作用域

词法作用域和动态作用域

我们需要先了解一个概念便是Js采用的是词法作用域,而不是动态作用域。

  1. 词法作用域:

词法作用域就是定义在词法阶段的作用域,是由写代码时将变量和块作用域写在哪里来决定的。无论函数在哪里被调用,也无论它如何被调用,它的词法作用域都只由函数被声明时所处的位置决定。

  1. 动态作用域:

动态作用域并不关心函数和作用域是如何声明以及在任何处声明的,只关心它们从何处调用。换句话说,作用域链是基于调用栈的,而不是代码中的作用域嵌套。

Js中的作用域

  1. 全局作用域
  2. 函数作用域
  3. 块级作用域(ES6所提出的概念,新增了let和const来解决var的问题)
  var count = 100; //全局作用域
  function foo() {
    var count = 0; //函数作用域
    return count; 
  }
  if (count == 1) {
    //块级作用域
    let a = 1; 
    a = a + count
  }

作用域:作用域是一套规则,用来确定在何处以及如何查找标识符。

作用域链本质是一个指向变量对象的指针列表。

作用域链和自由变量

作用域链

  • 基本概念:Js的函数作用域是可以互相嵌套的,各个作用域的嵌套关系便组成了一条作用域链。
  • 作用域的作用:作用域链的主要作用便是进行标识符的查询。当一个函数内部对一个变量进行操作,这时候Js内部便会从当前函数的作用域开始一层一层向外查找该标识符,直到查询到作为作用域的终点的全局执行环境。
    1. 如果自身作用域中声明该变量,则无需使用作用域链。
    2. 如果自身作用域中未声明该变量,则需要使用作用域链进行查找。这时,我们再看下一个概念自由变量

自由变量

概念:在当前作用域中存在但未在当前作用域中声明的变量叫自由变量

执行环境和变量对象

在网上看到了这样一句话,可以供大家参考一下:

当函数被创建,就有了作用域,当被调用时,就有了作用域链,当被继承时就有了原型链,当需要获取作用域链或原型链上的变量或值时,就有了闭包。

  • 当一个函数被声明时会创建该函数的作用域,同时会创建一个预先包含全局变量对象的作用域链。该作用域链被保存在内部的[[Scope]]属性中。
  • 当一个函数被调用的时候会为该函数创建一个执行环境(执行上下文),然后通过复制函数的[[Scope]]属性中的对象构建起环境作用域链的前端。执行上下文的周期又分为两部分:
  1. 创建部分:创建变量对象(环境中定义的所有变量和函数都保存在这个对象中。),确定它的作用域链,确定它的this的指向。
  2. 执行部分:确定变量对象的值。然后将函数引用,执行其他代码。
    执行上下文的生命周期
  • 变量对象的创建过程:
  1. 建立一个argunments对象,寻找当前上下文中的参数,并以其参数名以及参数值创建一个属性。
  2. 寻找当前上下文当中的function声明,在变量对象中,以函数名为属性名,创建以个属性,值为函数的引用地址,如果函数名重复的话,后面的覆盖前面的。
  3. 寻找当前上下文当中的var声明,在变量对象中,以变量名为其属性名,创建以个属性,值为undefined。
  • 执行环境和变量对象的区别:执行环境和变量对象。执行环境会随着函数的调用和返回,不断的重建和销毁。但变量对象在有变量引用的情况下,将留在内存中不被销毁。

执行流

  • 概念:代码的执行顺序叫做执行流,程序源代码并不是按照代码的书写顺序一行一行往下执行,而是和函数的调用顺序有关。
  • 示例:
var a = 1;
var b = 2;
function fn (x) {
    console.log(x);
    function add(y) {
        var c = 3;
        var count = y + c;
        return count;
    }
    var d = bar(10) // 第十行
    console.log(d)
}
fn(20)

如上代码,请执行顺序为:

第一行->第二行->第三行->最后一行->第四行->第五行->第十行->进入add()函数里执行->第十一行->第十二行

执行环境栈

我们知道,当我们每调用一个函数便会创建一个执行环境,当我们调用的函数多了,便会创建多个执行环境。Js引擎创建了执行环境栈来保存这些执行环境。
可以把执行上下文栈认为是一个存储函数调用的栈结构,遵循先进后出的原则。

下面来看一张图(该图从网上获取):
执行环境栈
从上面的流程图,我们需要记住几个关键点:

  1. JavaScript 执行在单线程上,所有的代码都是排队执行。
  2. 一开始浏览器执行全局的代码时,首先创建全局的执行上下文,压入执行栈的顶部。
  3. 每当进入一个函数的执行就会创建函数的执行上下文,并且把它压入执行栈的顶部。当前函数执行完成后,当前函数的执行上下文出栈,并等待垃圾回收。
  4. 浏览器的 JS 执行引擎总是访问栈顶的执行上下文。
  5. 全局上下文只有唯一的一个,它在浏览器关闭时出栈。

参考文章:

路飞学院-学习文档

Fundebug博客

参考书籍:
JavaScript高级程序设计

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值