资讯专栏INFORMATION COLUMN

全面了解JS作用域

Eidesen / 766人阅读

摘要:块作用域在中没有块作用域也就是说在中声明的变量会泄露到外面作用域而中新增的可以将变量绑定到所在的任意作用域通常是内部,换句话说,为其声明的变量隐式的劫持了所在的块作用域。总结作用域其实是有执行上下文中的变量对象和作用域链共同构成的。

执行上下文(也称执行环境)堆栈

执行上下文是javascript最重要的一个概念,执行上下文定义了变量或函数有权访问的其他数据,决定了它们各自的行为。而在javascript中有三种执行上下文: 全局执行上下文, 函数执行上下文, eval执行上下文。代码在其执行上下文中执行。在javascript中只有一个全局执行环境(根据宿主环境的不同,全局执行环境的对象也不一样)。可以有许多函数和eval执行环境的实例,每次调用一个函数或eval,都会进入对应执行环境执行代码。注意一个函数可能会产生无限上下文集合,因为每次函数调用自身都会产生一个新的执行上下文

执行上下文可以激活另一个执行上下文,例如函数调用另一个函数(或者全局执行上下文调用全局函数)等等,逻辑上就成了一个堆栈。这被称为执行上下文堆栈。
当执行流进入一个函数时,函数的上下文就会被推入一个栈中,如果在当前函数中调用另一个函数,当前函数就会暂停执行,并将执行流传递给被调用函数(被调用函数同事可能是其他函数的调用者),被调用者被推入堆栈。当被调用者的上下文结束后,将执行流交还给调用者,调用者的继续运行代码,直到结束后,栈将上下文弹出。

执行上下文

每个执行上下文可以抽象成一个对象,都包含了一组属性。

变量对象(variable object)

每个执行上下文都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象。但是不包含函数表达式和this(因为他不是一个变量)。

</>复制代码

  1. var foo = 10;
  2. function bar(){};
  3. (function baz(){})
  4. console.log(baz); //eror

全局上下文的变量对象

如果执行上下文是一个函数,则将其活动对象作为变量对象,除了变量和函数声明之外,他还存储形式参数和arguments对象。

</>复制代码

  1. function foo(x, y) {
  2. var z = 30;
  3. function bar() {}
  4. (function baz() {});
  5. }
  6. foo(10, 20);

我们发现baz不在活动对象里。

作用域链(scope chain)

作用域链本质上就是根据名称查找变量(标识符名称)的一套规则。规则非常简单,在自己的变量对象里找不到变量,就上父级的变量对象查找,当抵达最外层的全局上下文中,无论找到还是没找到,查找过程都会停止。查找会在找到第一个匹配的变量时停止,被称为遮蔽效应

</>复制代码

  1. var x = 10;
  2. (function foo(){
  3. var y = 10;
  4. (function bar(){
  5. var z = 10;
  6. console.log(x+y+z)![scope-chain.png][6]
  7. })
  8. })

如下图:

作用域知识点
词法作用域

词法作用域就是定义在词法阶段的作用域。换句话说,词法作用域是由你在写代码时将变量和块作用域写在哪里决定的,无论函数在哪里被调用,也无论他如何被调用,他的词法作用域只由函数被声明位置决定

欺骗词法

在javascript中的eval函数可以接受一个字符串为参数,并将其中的内容视为在书写时就存在于程序中这个位置的代码。

</>复制代码

  1. function foo(str, a){
  2. eval(str); //欺骗
  3. console.log(a, b);
  4. }
  5. var b = 2;
  6. foo("var b = 3;", 1); //1, 3
函数作用域

函数作用域有两种方式

</>复制代码

  1. //函数声明
  2. function foo(){
  3. var a = 3;
  4. console.log(a);
  5. }

</>复制代码

  1. //函数表达式
  2. (function foo(){
  3. var a = 2;
  4. console.log(a);
  5. })

两者的区别在于它们的名称标识符会被绑定到何处,第一段代码中会被绑定到所在作用域中,第二段代码被绑定在函数表达式自身的函数中而不是所在作用域中。

块作用域

在javascript中没有块作用域,也就是说在{...}中声明的变量会泄露到外面作用域

</>复制代码

  1. if(true){
  2. var foo = "dog"
  3. }
  4. console.log(foo); //dog
  5. function dosomething(i){
  6. console.log(i);
  7. }
  8. for(var i = 0; i < 10; i++){
  9. dosomething(i);
  10. }
  11. console.log(i);

而ES6中新增的let可以将变量绑定到所在的任意作用域(通常是{...}内部),换句话说,let为其声明的变量隐式的劫持了所在的块作用域。

</>复制代码

  1. if(true){
  2. var foo = "dog"
  3. }
  4. console.log(foo); //dog
  5. function dosomething(i){
  6. console.log(i);
  7. }
  8. for(let i = 0; i < 10; i++){
  9. dosomething(i);
  10. }
  11. console.log(i); //error
总结

作用域其实是有执行上下文中的变量对象和作用域链共同构成的。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/84920.html

相关文章

  • 深入贯彻闭包思想,全面理解JS闭包形成过程

    摘要:下面我们就罗列闭包的几个常见问题,从回答问题的角度来理解和定义你们心中的闭包。函数可以通过作用域链相互关联起来,函数内部的变量可以保存在其他函数作用域内,这种特性在计算机科学文献中称为闭包。 写这篇文章之前,我对闭包的概念及原理模糊不清,一直以来都是以通俗的外层函数包裹内层....来欺骗自己。并没有说这种说法的对与错,我只是不想拥有从众心理或者也可以说如果我们说出更好更低层的东西,逼格...

    snowell 评论0 收藏0
  • 前端知识点整理

    摘要:难怪超过三分之一的开发人员工作需要一些知识。但是随着行业的饱和,初中级前端就业形势不容乐观。整个系列的文章大概有篇左右,从我是如何成为一个前端工程师,到各种前端框架的知识。 为什么 call 比 apply 快? 这是一个非常有意思的问题。 作者会在参数为3个(包含3)以内时,优先使用 call 方法进行事件的处理。而当参数过多(多余3个)时,才考虑使用 apply 方法。 这个的原因...

    Lowky 评论0 收藏0
  • 前端知识点整理

    摘要:难怪超过三分之一的开发人员工作需要一些知识。但是随着行业的饱和,初中级前端就业形势不容乐观。整个系列的文章大概有篇左右,从我是如何成为一个前端工程师,到各种前端框架的知识。 为什么 call 比 apply 快? 这是一个非常有意思的问题。 作者会在参数为3个(包含3)以内时,优先使用 call 方法进行事件的处理。而当参数过多(多余3个)时,才考虑使用 apply 方法。 这个的原因...

    snowLu 评论0 收藏0
  • JS基础知识:变量对象、作用链和闭包

    摘要:前言这段时间一直在消化作用域链和闭包的相关知识。而作用域链则是这套规则这套规则的具体运行。是变量对象的缩写那这样放有什么好处呢我们知道作用域链保证了当前执行环境对符合访问权限的变量和函数的有序访问。 前言:这段时间一直在消化作用域链和闭包的相关知识。之前看《JS高程》和一些技术博客,对于这些概念的论述多多少少不太清楚或者不太完整,包括一些大神的技术文章。这也给我的学习上造成了一些困惑,...

    Keven 评论0 收藏0
  • javascript引擎执行的过程的理解--语法分析和预编译阶段

    摘要:所以觉得把这个执行的详细过程整理一下,帮助更好的理解。类似的语法报错的如下图所示三预编译阶段代码块通过语法分析阶段之后,语法都正确的下回进入预编译阶段。另开出新文章详细分析,主要介绍执行阶段中的同步任务执行和异步任务执行机制事件循环。 一、概述 js是一种非常灵活的语言,理解js引擎的执行过程对于我们学习js是非常有必要的。看了很多这方便文章,大多数是讲的是事件循环(event loo...

    molyzzx 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<