作用域和作用域链

作用域和作用域链

什么是作用域?

作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。

具体分类:

名称说明
window/global Scope全局作用域
function Scope函数作用域
Block Scope块作用域(ES6)
eval Scopeeval作用域(已经弃用)

示例代码:

// 全局作用域
var a = 'andy';
function test() {
    // 局部作用域
    var b = "Tim";
    console.log(b);
}
test(); // 输出 'Tim'
console.log(a); // 输出 'andy'

if (true) {
    // 这个 'if' 块语句没有创建一个块级作用域

    // name 变量处于全局作用域,因为由var关键字声明
    var name = 'Hammad';
    // likes 变量处于块级作用域因为由let关键字声明
    let likes = 'Coding';
    // skills 变量处于块级作用域因为由const关键字声明
    const skills = 'JavaScript and PHP';
}

console.log(Hammad); // 输出 'Hammad'
console.log(likes); // Uncaught ReferenceError: likes is not defined
console.log(skills); // Uncaught ReferenceError: skills is not defined

理解前提
  • function的AO理解为“独立的仓库(作用域)”
  • 原理:利用AO、GO来解决作用域、作用域链相关所产生的一切问题
  • 函数也是一种对象类型,一种引用类型,有引用值
  • 函数有属性:test.name test.length test.prototype
  • 对象的有些属性是我们无法访问的,这些属性就是JS引擎内部固有的隐式属性(内部私有属性)
  • AO:函数的执行期上下文
  • GO:全局的执行期上下文
原理说明

(1)作用域:[[scope]](隐式属性)

  • 1、函数创建时,生成的一个JS内部的隐式属性。
  • 2、函数存储作用域链的容器,作用域链中存储AO、GO。
  • 3、当函数执行完成以后,旧的AO就销毁,当重新执行时,会重新生成新的AO。所以,AO是一个即时的存储容器。

(2)作用域链
把AO、GO从上到下排列起来,形成链式关系就是作用域链(scope chain)。

执行过程

(1)示例代码

function a() {
    function b() {
        var b = 2; 
    }
    var a = 1;
    b();
}
var c = 3;
a();

(2)图解
当a函数被定义时:
image

当a函数被执行时(前一刻):
image

当b函数被定义时:
image

当b函数被执行时(前一刻):
image

当b函数被执行结束后:
image

回归b函数被定义时的状态:
image

当a函数被执行结束时:
image

回归a函数被定义时的状态:
image

(3)总结-作用域的基础过程

  • 1、全局执行前一刻生成GO,同时函数声明已经定义
  • 2、全局执行(赋值),因为赋值是在执行的时候触发的。比如函数表达式赋值
  • 3、永远都是上级执行,下级被定义:全局执行的时候,全局函数已经被定义,全局函数执行的时候,内部的函数被定义
  • 4、当函数被定义的时候,已经形成了作用域[[scope]]、作用域链(scope chain),并放入GO
  • 5、当函数在执行的那一刻才会生成自己的AO
  • 6、函数在被定义的时候拿的是上级的作用域环境
  • 7、当函数执行完成以后,旧的AO就销毁,当重新执行时,会重新生成新的AO

(4)结论

  • 1、每一个函数在的作用域上都有GO
  • 2、函数的AO存在作用域链的最顶端
  • 3、每个函数都有一个AO和GO,并且AO在GO的上面
示例代码二
function a () {
    function b () {
        function c () {

        }
        c();
    }
    b();
}
a();

执行过程分析:
image

作用域与执行上下文

许多开发人员经常混淆作用域和执行上下文的概念,误认为它们是相同的概念,但事实并非如此。

我们知道JavaScript属于解释型语言,JavaScript的执行分为:解释和执行两个阶段,这两个阶段所做的事并不一样:

解释阶段:

  • 词法分析
  • 语法分析
  • 作用域规则确定

执行阶段:

  • 创建执行上下文
  • 执行函数代码
  • 垃圾回收

JavaScript解释阶段便会确定作用域规则,因此作用域在函数定义时就已经确定了,而不是在函数调用时确定,但是执行上下文是函数执行之前创建的。执行上下文最明显的就是this的指向是执行时确定的。而作用域访问的变量是编写代码的结构确定的。

作用域和执行上下文之间最大的区别是:
执行上下文在运行时确定,随时可能改变;作用域在定义时就确定,并且不会改变

一个作用域下可能包含若干个上下文环境。有可能从来没有过上下文环境(函数从来就没有被调用过);有可能有过,现在函数被调用完毕后,上下文环境被销毁了;有可能同时存在一个或多个(闭包)。同一个作用域下,不同的调用会产生不同的执行上下文环境,继而产生不同的变量的值

给大家推荐一个好用的BUG监控工具Fundebug,欢迎免费试用!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值