JavaScript 作用域scope简单汇总


Posted in Javascript onOctober 23, 2019

什么是作用域

程序的执行,离不开作用域,也必须在作用域中才能将代码正确的执行。

所以作用域到底是什么,通俗的说,可以这样理解:作用域就是定义变量的位置,是变量和函数的可访问范围,控制着变量和函数的可见性和生命周期。

而JavaScript中的作用域,在ES6之前和ES6之后,有两种不同的情况。

ES6之前,JavaScript作用域有两种:函数作用域和全局作用域。

ES6之后,JavaScript新增了块级作用域。

作用域的特性

在JavaScript变量提升的讨论中,我们其实是缺少了一个作用域的概念的,变量提升其实也是针对在同一作用域中的代码来说的。

对编译器的了解,让我们明白,对于一段代码【var a = 10】变量的赋值操作,其实是包含了两个过程:

1、变量的声明和隐式赋值(var a = undefined),这个阶段在编译时

2、变量的赋值(a = 10),这个阶段在运行时

先看一下如下代码:

var flag = true;

if(flag) {
  var someStr = 'flag is true';
}

function doSomething() {
  var someStr = 'in doSomething';
  var otherStr = 'some other string';
  console.log(someStr);
  console.log(flag);
}

doSomething();

for(var i = 0; i < 10; i++) {
  console.log(i);
}

console.log(i);

{
  var place = 'i do not want to be visited';
}

那么这一些代码在编译之后,执行之前,根据变量提升的机制,我们可以知道应该是下面这个样子:

function doSomething() { // 函数优先提升
  // 提升隐式赋值
  var someStr = undefined; 
  var otherStr = undefined; 

  someStr = 'in doSomething';
  otherStr = 'some other string';

  console.log(someStr);
  console.log(flag);
}

// 隐式赋值和提升
var flag = undefined; 
var someStr = undefined;
var i = undefined;
var place = undefined;

flag = true;

if(flag) {
  someStr = 'flag is true';
}

for(i = 0; i < 10; i++) {
  console.log(i);
}

doSomething();

console.log(i);

{
  place = 'i do not want to be visited';
}

因为变量的提升特性,以及无块级作用域的概念,所以代码中在同一个作用域中变量和函数的定义,在编译阶段都会提升到顶部。

通过上述代码,我们大体上可以得出作用域的特性:

第一、内部作用域和外部作用域是嵌套关系。外部作用域完全包含内部作用域。

第二、内部作用域可访问外部作用域的变量,但是外部作用域不能访问内部作用域的变量,(链式继承,向上部作用域查找)。

第三、变量提升是在同一个作用域内部出现的。

第四、作用域用于编译器在编译代码时候,确定变量和函数声明的位置。

块级作用域

上述代码,在ES6+的环境中运行,也是和ES6之前是相同的结果,但是ES6不是引用了块级作用域吗,为什么大括号块内的代码还是会出现和之前一样的编译方式呢?

那么,ES6中的块级作用域到底是什么?

let & const

利用var定义的变量,具有提升的性质,可能会影响代码的执行结果。

这是var定义变量的缺陷,那么如何规避这种缺陷呢?在ES6中,设计出来了let和const来重新定变量。

但是,由于JavaScript标准定义的非常早,1995年5月JavaScript方案定义,1996年微软提供了JavaScript解决方案JScript。而网景公司为了同微软竞争,神情了JavaScript标准,于是,1997年6月第一个国际标准ECMA-262便颁布了。

C语言标准化的过程却是将近二十年后才颁布。

所以,我们以后设计的语言既要兼容var也要有自己的块级作用域,让var和let以及const在引擎做到兼容。

所以,我们定义块级作用域的标准,只能从定义变量的方式入手,而不是直接一个{}块就可以解决。

先让我们看一下下面代码:

var name = 'someName';

function doSomething(){
  console.log(name);
  if(true) {
    var name = 'otherName';
  }
}
doSomething();
结果:undefined

产生这个结果的原因是我们函数内部的变量提升,覆盖了外部作用域的变量,也就是说,其实打印出来的值是doSomething函数中的变量声明的值。

但是这样却并不符合块级作用域的预期,如果有许多类似代码,理解起来也会相当困难。如果将代码用ES6方式改写:

let name = 'someName';

function doSomething(){
  console.log(name);
  if(true) {
    let name = 'otherName';
  }
}

doSomething();

结果:'someName'

从运行结果看,我们真正的做到了块级作用域应该有的效果,那么let和const又是如何支持块作用域的呢?

执行上下文

先想想一下JavaScript中的一个作用域两个执行上下文中的编译过程中的环境:

变量环境:编译阶段var声明存放的位置(一个大对象)。

词法环境:我们代码书写的位置,也是let和const的初始化位置(代码按词法环境顺序执行,按照{}划分的栈结构)。

而在编译阶段,我们将var定义的变量全都在编译过程在变量环境初始化为undefined,但是用let和const定义的变量,其实他们并未在变量环境初始化,而是在词法环境初始化,也就是执行代码位置初始化。

词法环境的特点:按照{}划分的一个栈结构。

变量查找方式

JavaScript中变量查找的方式:沿着词法环境的栈顶向下查找,找不到的变量去变量环境中查找,这样就形成了先查找代码块中的变量,再查找提升之后的变量环境,这样就形成了块级作用域的概念。

上面的代码形成两种环境的情况如下:

一、全局环境的执行上下文

变量环境:函数声明function doSomething() { … }

词法环境栈:执行到let name = ‘someName';让语句name = ‘someName'入栈。

二、doSomething的执行上下文(被全局环境包裹)

变量环境:无

词法环境栈情况:执行到let name = ‘otherName',语句的时候,name = ‘other'才会入栈;

JavaScript代码执行方式

执行doSomething的时候,还未执行let name = ‘otherName',所以,此时doSomething的词法环境中并未有name = ‘otherName',这个时候查找,只能向外部作用域查找(全局作用域)

此时查找到全局作用域name = ‘someName'所以此时就打印了someName

代码接着执行到了if语句内部,才会将name = ‘otherName'入栈,但是此时因为语句已经执行完毕,所以也就无关痛痒了。

JavaScript也就通过这种方式,实现了块级别作用域。

总结

JavaScript中的作用域总的来说,分为块级作用域、函数作用域、全局作用域。

而每个作用域都会创建自身的执行上下文,每一个执行上下文又分为了变量环境和词法环境两部分。

块级作用域的实现,其实是根据定义的let和const声明的变量放置在词法环境栈中这一特性来实现。

这一特性被社区的人叫做‘暂时性死区',但是在JavaScript标准中并未有这个概念。

只有理解了作用域的概念,才能真正明白JavaScript的执行机制,才能减少我们因为变量定义等发生的错误。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
jQuery.Autocomplete实现自动完成功能(详解)
Jul 13 Javascript
DIY jquery plugin - tabs标签切换实现代码
Dec 11 Javascript
JavaScript高级程序设计 读书笔记之九 本地对象Array
Feb 27 Javascript
分析Node.js connect ECONNREFUSED错误
Apr 09 Javascript
用JS做的简单的可折叠的两级树形菜单
Sep 21 Javascript
JavaScript中的立即执行函数表达式介绍
Mar 15 Javascript
Node.JS中事件轮询(Event Loop)的解析
Feb 25 Javascript
JavaScript基础之this详解
Jun 04 Javascript
深入理解ES6的迭代器与生成器
Aug 19 Javascript
Cropper.js 实现裁剪图片并上传(PC端)
Aug 20 Javascript
浅谈React + Webpack 构建打包优化
Jan 23 Javascript
JS实现运动缓冲效果的封装函数示例
Feb 18 Javascript
node.js使用fs读取文件出错的解决方案
Oct 23 #Javascript
jquery 键盘事件 keypress() keydown() keyup()用法总结
Oct 23 #jQuery
JavaScript提升机制Hoisting详解
Oct 23 #Javascript
使用p5.js实现动态GIF图片临摹重现
Oct 23 #Javascript
p5.js临摹动态图形的方法
Oct 23 #Javascript
Vue 实现点击空白处隐藏某节点的三种方式(指令、普通、遮罩)
Oct 23 #Javascript
p5.js实现动态图形临摹
Oct 23 #Javascript
You might like
php防注入,表单提交值转义的实现详解
2013/06/10 PHP
php使用PDO从数据库表中读取数据的实现方法(必看)
2017/06/02 PHP
PHP+Ajax实现的无刷新分页功能详解【附demo源码下载】
2017/07/03 PHP
Laravel学习教程之View模块详解
2017/09/18 PHP
javascript中用星号表示预录入内容的实现代码
2011/01/08 Javascript
在myeclipse中如何加入jquery代码提示功能
2014/06/03 Javascript
JavaScript中统计Textarea字数并提示还能输入的字符
2014/06/10 Javascript
JS实现自动固定顶部的悬浮菜单栏效果
2015/09/16 Javascript
JavaScript实现斗地主游戏的思路
2016/02/29 Javascript
Jquery ui datepicker设置日期范围,如只能隔3天【实现代码】
2016/05/04 Javascript
JavaScript仿淘宝页面图片滚动加载及刷新回顶部的方法解析
2016/05/24 Javascript
JavaScript利用正则表达式替换字符串中的内容
2016/12/12 Javascript
Bootstrap导航中表单简单实现代码
2017/03/06 Javascript
微信小程序登录态控制深入分析
2017/04/12 Javascript
JS设置手机验证码60s等待实现代码
2017/06/14 Javascript
Vue.js中组件中的slot实例详解
2017/07/17 Javascript
浅谈js基础数据类型和引用类型,深浅拷贝问题,以及内存分配问题
2017/09/02 Javascript
javascript获取指定区间范围随机数的方法
2017/09/08 Javascript
ng-repeat指令在迭代对象时的去重方法
2018/10/02 Javascript
JavaScript+HTML5 canvas实现放大镜效果完整示例
2019/05/15 Javascript
vue实现路由懒加载的3种方法示例
2020/09/01 Javascript
vue 判断两个时间插件结束时间必选大于开始时间的代码
2020/11/04 Javascript
jQuery实现动态向上滚动
2020/12/21 jQuery
[10:18]2018DOTA2国际邀请赛寻真——Fnatic能否笑到最后?
2018/08/14 DOTA
Python批量修改图片分辨率的实例代码
2019/07/04 Python
弄懂这56个Python使用技巧(轻松掌握Python高效开发)
2019/09/18 Python
python使用hdfs3模块对hdfs进行操作详解
2020/06/06 Python
使用OpenCV实现道路车辆计数的使用方法
2020/07/15 Python
HTML5公共页面提取作为公用代码的方法
2020/06/30 HTML / CSS
娱乐地球:Entertainment Earth
2020/01/08 全球购物
DC Shoes俄罗斯官网:美国滑板鞋和服饰品牌
2020/08/19 全球购物
你所在的项目是如何确定版本号的
2015/12/28 面试题
校园演讲稿汇总
2014/05/21 职场文书
婚纱店策划方案
2014/05/22 职场文书
Vue elementUI表单嵌套表格并对每行进行校验详解
2022/02/18 Vue.js
《进击的巨人》新联动CM 兵长强势出击兽巨人
2022/04/05 日漫