先有鸡还是先有蛋

通过之前的文章,我们熟悉了作用域的基本概念。但是作用域中的变量,函数声明在什么地方查找,引用它们的时候又发生了什么。正是我们将要讨论的内容。

在我们的认知中JavaScript代码在执行的时候是由上到下一行一行执行的。但实际上并不完全正确。例如:

a = 1;
var a;
console.log(a);
复制代码

按照我们之前的认知由上到下,最后a输出undefined,因为var a声明在a = 1后面,但最后输出的结果是1

考虑另外一段代码:

console.log(a);
var a = 1;
复制代码

鉴于上一个代码片段所表现的特点,可能认为这个代码片段也会输出1,或者可能抛出异常错误。实际上输出的是undefined

那么到底是声明在前,还是赋值在前?

回顾JavaScript引擎

为了弄明白这个问题,我们需要再次回顾JavaScript引擎,引擎会在解释JavaScript代码之前首先对其进行编译。编译阶段中的一个很重要的工作就是找到所有的声明,并在合适的作用域中将它们关联起来。

执行环境

执行环境也可以叫执行上下文,每当JavaScript编译器工作时,都会创建一个执行环境或者说进入一个执行上下文中。它们定义了变量或函数访问其他数据的权限,决定了它们各自的行为。它们在逻辑上组成一个堆栈,堆栈底部永远是全局环境,而顶部就是当前环境。

例如:我们可以定义执行环境是一个数组:

stack = [];
复制代码

在初始化阶段,stack是这样的:

stack = [globalContext
];
复制代码

每次函数执行,进入function的时候,这个堆栈都会被压入。

function foo(){return 'hello';
}
foo();
复制代码

那么,stack将会发生改变:

stack = [<foo> functionContextglobalContext
];
复制代码

每个函数都有自己的执行环境,每次函数退出也就是执行到return的时候,都会退出当前的执行环境,相应的stack就会弹出,栈中的指针会移动位置。相关代码执行完毕后,stack只会包含全局环境,一直到整个程序结束。

变量对象

在进行JavaScript编程是总避免不了声明函数和变量,在每个执行环境中有一个变量对象,我们定义的所有变量和函数都保存在这个对象中。

变量对象(VO)存储一下内容:

函数声明(function)

变量声明(var)

我们可以用一个JavaScript对象来表示一个变量对象例如:

VO = {};
复制代码

如前面所说执行环境中有一个变量对象,它是执行环境的一个属性,例如:

context = {VO = {};
}
复制代码

当我们声明一个变量或一个函数的时候,例如:

var a = 1;function foo() {var b = 20;
};test();
复制代码

对应的变量对象是:

//全局环境的变量对象
globalContext: {vo: {a: 1,foo: function}
}
//foo函数环境的变量对象
fooContext: {vo: {b: 20}
}
复制代码

不同执行环境中的变量对象

抽象变量对象VO

全局执行环境变量对象GlobalContextVO (VO === this === global)

函数执行环境变量对象FunctionContextVO (VO === AO, 并且添加了arguments)

全局执行环境的变量对象

全局对象是在进入任何执行环境之前就已经创建了的对象;这个对象只存在一份,它的属性在程序中任何地方都可以访问,全局对象的生命周期终止于程序退出那一刻。

global = {Math: <...>,String: <...>......window: global //引用自身
};var a = 1;console.log(a); // 直接访问,在VO(globalContext)里找到:aconsole.log(window['a']); // 间接通过global访问:global === VO(globalContext): a
console.log(a === this.a); // truevar b = 'b';
console.log(window[b]); // 间接通过动态属性名称访问:b
复制代码

函数执行环境的变量对象

在函数执行环境中,变量对象是不能直接访问的,此时由活动对象(AO)代替变量对象。活动对象是在进入函数执行环境时被创建的,它通过函数的arguments属性初始化。

VO(functionContext) === AO;AO = {arguments: <Args>
};// Arguments对象是活动对象,属性如下:
// callee — 指向当前函数的引用
// length — 真正传递的参数个数function foo(a, b, c) {// 声明的函数参数数量arguments (a, b, c)console.log(foo.length); // 3// 真正传进来的参数个数(only x, y)console.log(arguments.length); // 2// 参数的callee是函数自身console.log(arguments.callee === foo); // true// 参数共享console.log(a === arguments[0]); // trueconsole.log(a); // 10arguments[0] = 20;console.log(a); // 20a = 30;console.log(arguments[0]); // 30// 不过,没有传进来的参数c,和参数的第3个索引值是不共享的c = 40;console.log(arguments[2]); // undefinedarguments[2] = 50;console.log(c); // 40}foo(10, 20);
复制代码

作用域链

当代码在一个执行环境中,引擎会创建变量对象的一个作用域链。作用域链的用途是保证对执行环境有权限的变量和函数的有序访问。作用域链的前端,始终都是当前执行的代码所在环境的变量对象。如果这个环境是函数,则活动对象(VO)作为变量对象(即arguments对象)。作用域链中的下一个变量对象来自包含环境,而再下一个变量对象则来自下一个包含环境,一直延续到全局执行环境;全局执行环境的变量对象始终都是作用域链中的最后一个对象。

作用域链与一个执行环境相关,变量对象的作用域链用于在标识符解析中变量查找。

函数执行环境的作用域链在函数调用时创建的,包含活动对象和这个函数内部的[[scope]]属性,例如:

var a = 1;function foo() {var b = 2;console.log(a + b);
}foo(); // 3
复制代码

函数创建时:

fooContext.AO = {b: undefined // undefined – 进入上下文的时候是2 – 活动对象
};复制代码

函数foo如何访问到变量a?理论上函数应该能访问一个更高一层执行环境的变量对象。实际上它正是这样,这种机制是通过函数内部的[[scope]]属性来实现的。[[scope]]是所有父变量对象的层级链,处于当前函数执行环境之上,在函数创建时存于其中,直至函数销毁。函数foo的[[scope]]如下:

foo.[[Scope]] = [globalContext.VO // === Global
];
复制代码

函数调用时:

Scope = AO|VO + foo.[[Scope]]
复制代码

进入foo执行环境创建AO/VO之后,在执行环境中创建一个作用域链(Scope属性)。这样foo函数就能访问全局执行环境中的变量a

变量提升

通过前面对于引擎的了解,变量提升是Javascript中执行环境和变量对象的工作方式的一种认知。它处于代码的编译阶段,JavaScript仅提升声明,而不提升初始化。

函数声明和变量声明会被提升

当我们的代码运行时,首先在执行环境的变量对象中声明变量和函数,然后才是代码执行阶段。当我们看到var a = 1时,实际上JavaScript会将其看成两部分:var = aa = 1

var a;
a = 1;//全局环境的变量对象
globalContext: {vo: {a: 1}
}
复制代码

函数优先

函数声明会首先被提升,然后才是变量,例如:

foo(); // 1
var foo;
function foo() { console.log( 1 );
}
foo = function() { console.log( 2 );
};
复制代码

参考

  • JavaScript高级程序设计
  • 你不知道的JavaScript

转载于:https://juejin.im/post/5cb5efc9f265da03b4460890

学习作用域中的“名词”相关推荐

  1. PHP引用全局作用域中可用的全部变量是,php全局变量之学习笔记

    今天我们参考php官方对php全局变量介绍来理解一下php全局变量一些用法与自己对它的一些思考吧,现在分享出来希望对各位同学有帮助. 一.核心要点思考: 1. 哪些全局变量是过期的及如何处理? 如:u ...

  2. Java开发中各类名词解释大全

    在Java开发里面出现了很多名次,包括以后学习Java中也会出现很多常用到的名词,对初学者来说可能不知道是什么意思,或者是对这些刺耳的理解不是特别透彻,这里我就我自己的理解来解释下这些词的意思. 包 ...

  3. JS进阶学习(作用域、函数进阶、解构赋值、原型链)

    文章目录 1.面相对象编程介绍 2.ES6中的类和对象 3.类的继承 ES6中的类和对象 三个注意点 作用域 局部作用域 全局作用域 作用域链 JS垃圾回收机制(GC) JS垃圾回收机制-算法说明 闭 ...

  4. 如何理解并学习javascript中的面向对象(OOP)

    本文不适合javascript初学者看(javascript水平还停留在函数级别的朋友,看了会觉得很晕的).如果你想让你的javascript代码变得更加优美,性能更加卓越.或者,你想像jQuery的 ...

  5. 机器学习中的名词解释(一):监督学习、无监督学习、半监督学习、自监督学习(通俗理解)

    机器学习中有几个带有"监督"二字的名词,易混淆,写篇博客解释一下下~ 1.监督学习(Supervised Learning):是指从标注数据中学习预测模型的机器学习方法,其本质是学 ...

  6. 浅谈列控系统的阶梯式分级速度控制中专有名词的表述问题

    浅谈列控系统的阶梯式分级速度控制中专有名词的表述问题 一.问题提出 个人在学习列车运行控制系统的过程中发现,不同的文献中,在介绍和论述列车运行控制系统的阶梯式分级速度控制方式时,总会提到下面专有名词中 ...

  7. C++核心准则SF.7:不要在头文件中的全局作用域中使用using namespace指令

    SF.7: Don't write using namespace at global scope in a header file SF.7:不要在头文件中的全局作用域中使用using namesp ...

  8. 学习Spring中遇到关于BeanFactory及测试类的问题

    最近在学习Spring,使用的是Spring 5.0.1 学习书本中使用的是4.0 学习书本中使用以下来加载配置文件及设置 Resource resource = new ClassPathResou ...

  9. 【组队学习】【24期】集成学习(中)

    集成学习(中) 开源内容: https://github.com/datawhalechina/team-learning-data-mining/tree/master/EnsembleLearni ...

最新文章

  1. 基于webpack的前端工程化开发解决方案探索(一):动态生成HTML
  2. Medusa(美杜莎)和Hydra(九头蛇)快速入门手册:01
  3. .NET设计模式(18):迭代器模式(Iterator Pattern)(转)
  4. 【加】德鲁·卡宾森 - 质量效应2:升天(2013年6月7日)
  5. iOS之深入解析WKWebView的WebKit源码调试与分析
  6. Java中9种IO的读取方式
  7. IDEA 自动生成 serialVersionUID
  8. sp根据备份文件来创建DB
  9. sqlserver 判断不为空_SQL server 学习
  10. mysql如何管理事务管理_浅谈MySQL事务管理(基础)
  11. Auto CAD指定线段长度和角度的方法
  12. 电脑网络没有问题,就是电脑连不上网的解决办法
  13. 大数据对人们生活的积极影响_大数据对生活带来的影响
  14. golang 生成二维码名片 海报
  15. Vuex的基本使用及总结(超详细)
  16. 使用 JDB 调试 Android 应用程序
  17. asp.net网站服务器,vs2010制作简单的asp.net网站
  18. Truelore星桥,为投标全过程保驾护航
  19. 丘成桐中学生计算机科学竞赛,丘成桐中学生科学竞赛国内外赛区介绍
  20. SAP MM部分常用增强-ME21N-MIGO-MB1A

热门文章

  1. NLP公开课 | 竹间智能翁嘉颀:人机交互未来如何改变人类生活
  2. 【AAAI Oral】利用深度增强学习自动解数学题,准确率提升15%
  3. 512 个 AI 职位、11 万美元年薪,盘点 2018 最佳人工智能公司
  4. 人大附中高中生学Python获数据挖掘竞赛一等奖,将去旷视科技实习
  5. 如何设计一个复杂的业务系统?从对领域设计、云原生、微服务、中台的理解开始...
  6. ElasticSearch 面试 4 连问,你顶得住么?
  7. 聊一聊 软件系统中的“热力学第二定律”
  8. IntelliJ IDEA 新版介绍
  9. 飞桨领航团 x Datawhale联合Meetup来了!杭州的小伙伴可以见面了!
  10. 强化学习教程来啦!贡献者来自中科院、清华、北大3位男神!