专栏首页前端一会JS作用域和作用域链

JS作用域和作用域链

JS中的作用域就是在一定的空间范围内对数据进行读写操作。

在JS中一个变量的作用域(scope)是程序中定义这个变量的区域。

变量有全局变量和局部变量两种。

全局变量的作用域是全局性的,即在JavaScript代码中,该全局变量处处都有定义。

局部变量的作用域是局部性的,如函数体内声明的变量就只在函数体内部有定义,函数的参数也是局部变量,也只在函数体内部有定义。

注:这边的“定义”,我认为有“创建与下定义”的意思,比如定义一个函数,定义一个方法,都是先创建一个函数,再给它里面添加一些东西。

下面就要借助JS的作用域链来更好的理解作用域了。

在此之前,先要明确个概念,即执行环境和作用域是两个完全不同的概念。

函数的每次调用都有与之紧密相关的作用域和执行环境。从根本上来说,作用域是基于函数的,而执行环境是基于对象的(例如:全局执行环境即window对象)。

换句话说,作用域涉及到所被调用函数中的变量访问,并且不同的调用场景是不一样的。执行环境始终是this关键字的值,它是拥有当前所执行代码的对象的引用。每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。虽然我们编写的代码无法访问这个对象,但解析器在处理数据时会在后台使用它。

当JavaScript解释器初始化执行代码时,它首先默认进入全局执行环境,从此刻开始,函数的每次调用都会创建一个新的执行环境。

每个函数都有自己的执行环境。当执行流进入一个函数时,函数的环境就会被推入一个环境栈中(execution stack)。在函数执行完后,栈将其环境弹出,把控制权返回给之前的执行环境。ECMAScript程序中的执行流正是由这个便利的机制控制着。

执行环境可以分为创建和执行两个阶段。在创建阶段,解析器首先会创建一个变量对象(variable object,也称为活动对象 activation object),它由定义在执行环境中的变量、函数声明、和参数组成。在这个阶段,作用域链会被初始化,this的值也会被最终确定。在执行阶段,代码被解释执行。

<script type="text/javascript">

function Fn1(){

function Fn2(){

alert(document.body.tagName);//BODY

//other code...

}

Fn2();

}

Fn1();

</script>

当javascript代码被浏览器载入后,默认最先进入的是一个全局执行环境。当在全局执行环境中调用执行一个函数时,程序流就进入该被调用函数内,此时JS引擎就会为该函数创建一个新的执行环境,并且将其压入到执行环境堆栈的顶部。浏览器总是执行当前在堆栈顶部的执行环境,一旦执行完毕,该执行环境就会从堆栈顶部被弹出,然后,进入其下的执行环境执行代码。这样,堆栈中的执行环境就会被依次执行并且弹出堆栈,直到回到全局执行环境。

此外还要注意一下几点:

  • 单线程
  • 同步执行
  • 唯一的全局执行环境
  • 局部执行环境的个数没有限制
  • 每次某个函数被调用,就会有个新的局部执行环境为其创建,即使是多次调用的自身函数(即一个函数被调用多次,也会创建多个不同的局部执行环境)。

当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain)。作用域链的用途是保证对执行环境有权访问的所有变量和函数的有序访问。

作用域链包含了执行环境栈中的每个执行环境对应的变量对象。通过作用域链,可以决定变量的访问和标识符的解析。

注意:全局执行环境的变量对象始终都是作用域链的最后一个对象。

在访问变量时,就必须存在一个可见性的问题(内层环境可以访问外层中的变量和函数,而外层环境不能访问内层的变量和函数)。更深入的说,当访问一个变量或调用一个函数时,JavaScript引擎将不同执行环境中的变量对象按照规则构建一个链表,在访问一个变量时,先在链表的第一个变量对象上查找,如果没有找到则继续在第二个变量对象上查找,直到搜索到全局执行环境的变量对象即window对象。这也就形成了Scope Chain的概念。

需要注意的是:内部环境可以通过作用域链访问所有的外部环境,但是外部环境不能访问内部环境中的任何变量和函数。

标识符解析(变量名或函数名搜索)是沿着作用域链一级一级地搜索标识符的过程。搜索过程始终从作用域链的前端开始,然后逐级地向后(全局执行环境)回溯,直到找到标识符为止。

此外还要讲下JS作用域中的块级作用域。

JS中是没有块级作用域这个概念的。

什么是块级作用域呢?

任何一对花括号({和})中的语句集都属于一个块,在这之中定义的所有变量在代码块外都是不可见的,我们称之为块级作用域。

函数作用域就好理解了,定义在函数中的参数和变量在函数外部是不可见的。

但JS由于没有块级作用域,所以在块外仍旧可以访问。

functin test(){

for(var i=0;i<3;i++){}

alert(i);

}

test(); //结果是弹出3,在块外,块中定义的变量i仍然是可以访问的。

JS并不支持块级作用域,它只支持函数作用域,而且在一个函数中的任何位置定义的变量在该函数中的任何地方都是可见的。

那么JS又该怎么拥有块级作用域呢?

根据“在一个函数中定义的变量,当这个函数调用完之后,变量会被销毁”的特性,来模拟出JS的块级作用域。

function test(){

(function (){

for(var i=0;i<4;i++){

}

})();

alert(i);

}

test(); //弹出i未定义的错误

这里是把for语句放到闭包中,然后调用这个函数,当函数调用完毕后,变量i自动销毁,所以在块外就无法访问了。

其实也就相当于在for语句外面加了一层函数作用域,而JS的函数作用域执行完毕后是里面的变量就是销毁的哦!异曲同工啊。

有关闭包,这个单独一个章节来研究,这里不展开来说了。

最后再来个小例子收工。

<script type="text/javascript">

(function (){

a = 5; //注意,这是个全局变量,在有局部变量时,优先优先局部变量哦,作用域链的顺序。

alert(window.a); //undefined

var a = 1; //这里发生变量声明提升

alert(a); // 1

})();

</script>

本文分享自微信公众号 - 前端小二(frontendxiao2),作者:Bourn

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-05-26

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 《深入浅出Node.js》:Node异步编程基础–函数式编程

    Node是首个将异步大规模带到应用层面的平台,它从内存运行机制到API设计,都大量使用异步,它的优势在于高性能,但缺点在于异步编程的流程控制其实是有悖于自然语言...

    前端_AWhile
  • javascript设计模式四:迭代器模式

    迭代器模式分为内部迭代器和外部迭代器,内部迭代器就是在函数内部定义好迭代的规则,它完全接手整个迭代的过程,外部只需一次初始调用。

    前端_AWhile
  • 《JavaScript函数式编程》的读后总结一

    在JS中,函数是一等公民。这该怎么理解?为什么说它是“一等”的呢?其实这体现在函数可以去任何值可以去的地方,很少有限制。

    前端_AWhile
  • 《JavaScript高级程序设计》学习笔记(3)——变量、作用域和内存问题

    欢迎关注本人的微信公众号“前端小填填”,专注前端技术的基础和项目开发的学习。 本节内容对应《JavaScript高级程序设计》的第四章内容。 1、函数:通过函数...

    mukekeheart
  • Python一

    Python1989年,目前3.6N版本。 语言的划分: 编译型:一次性,编译成二进制,CPU运行。 缺点:开发效率低,不能跨平台,(比如执行过程中发现BUG,...

    py3study
  • 如何用spss做一般(含虚拟变量)多元线性回归

    回归一直是个很重要的主题。因为在数据分析的领域里边,模型重要的也是主要的作用包括两个方面,一是发现,一是预测。而很多时候我们就要通过回归来进行预测。关...

    小莹莹
  • 怎么理解变量

    我们都知道计算机很厉害,可以干很多事情,可以玩游戏,可以解题,可以播放音乐,可以画画,可以控制火箭卫星等等。为什么计算机可以干这么多不同的事情,它又是如何做到的...

    叶子陪你玩
  • 终极 shell 脚本 快速入门指南 (二) 之变量

    上一篇 终极 shell 脚本 快速入门指南 (一) 我们已经搞定 hello world 了。现在让我们更深入♂地了解 shell 脚本吧。

    白玉无冰
  • 变量的作用域

    打算装起来体验一下最初发布的版本, 但是发现只有 Windows 版本, 所以我就装了个 Windows10的虚拟机, 就在我打算安装的时候, 发现:

    烟草的香味
  • 【临床研究】一个你无法逃避的问题:多元回归分析中的变量筛选

    临床模型研究,说到底是做一个模型,那么模型应该如何纳入自变量,纳入哪些自变量,这都是至关重要的问题。线性回归,逻辑回归和Cox比例风险回归模型是被广泛使用的多元...

    用户6317549

扫码关注云+社区

领取腾讯云代金券