一次让你了解全部JavaScript的作用域


Posted in Javascript onJune 24, 2019

前言

作用域决定了变量的生命周期和可见性,变量在作用域范围之外是不可见的。

JavaScript 的作用域包括:模块作用域,函数作用域,块作用域,词法作用域和全局作用域。

全局作用域

在任何函数、块或模块范围之外定义的变量具有全局作用域。可以在程序的任意位置访问全局变量。
当启用模块系统时,创建全局变量会变得困难,但仍然可以做到这一点。可以在 HTML 中定义一个变量,这个变量需要在函数之外声明,这样就可以创建一个全局变量:

<script>
let GLOBAL_DATA = { value : 1};
</script>
console.log(GLOBAL_DATA);

当没有模块系统时,创建全局变量会容易很多。在任何文件中的函数外声明的变量都是全局变量。
全局变量贯穿于程序的整个生命周期。

另一种创建全局变量的方法是在程序的任意位置使用 window 全局对象:

window.GLOBAL_DATA = { value: 1 };

这样 GLOBAL_DATA 变量会随处可见。

console.log(GLOBAL_DATA)

不过你也知道这种做法是不好的。

模块作用域

如果不启用模块,在所有函数之外声明的变量是全局变量。在模块中,在函数外部声明的变量都是隐藏的,除非显式导出,否则不可用于其他模块。

导出使函数或对象可用于其他模块。在这个例子中,我从模块文件 sequence.js 中导出了一个函数:

// in sequence.js
export { sequence, toList, take };

当前模块可以通过导入来使用其他模块的函数或对象成。

import { sequence, toList, toList } from "./sequence";

在某种程度上,我们可以认为模块是一个自动执行的函数,它将 import 的数据作为输入,然后返回 export 的数据。

函数作用域

函数作用域意味着在函数中定义的参数和变量在函数内的任何位置都可见,但是在函数外部不可见。

下面是一个自动执行的函数,被称为IIFE。

(function autoexecute() {
let x = 1;
})();
console.log(x);
//Uncaught ReferenceError: x is not defined

IIFE 的意思是立即调用函数表达式,是一个在定义后立即运行的函数。

用 var 声明的变量只有函数作用域。更重要的是,用 var 声明的变量被提升到其作用域的顶部。通过这种方式,可以在声明之前访问它们。看看下面的代码:

function doSomething(){
console.log(x);
var x = 1;
}
doSomething(); //undefined

这种事不会发生在 let 中。用 let 声明的变量只能在定义后访问。

function doSomething(){
console.log(x);
let x = 1;
}
doSomething();
//Uncaught ReferenceError: x is not defined

用 var声明的变量可以在同一作用域下多次重新声明:

function doSomething(){
var x = 1
var x = 2;
console.log(x);
}
doSomething();

用 let 或 const 声明的变量不能在同一作用域内重新声明:

function doSomething(){
let x = 1
let x = 2;
}
//Uncaught SyntaxError: Identifier 'x' has already been declared

也许我们可以不必关心这一点,因为 var 已经开始变得过时了。

块作用域

块作用域用花括号定义。它由 { 和 } 分隔。

用 let 和 const 声明的变量可以受到块作用域的约束,只能在定义它们的块中访问。

思考下面这段关于 let 块范围的代码:

let x = 1;
{ 
let x = 2;
}
console.log(x); //1

相反,var 声明不受块作用域的约束:

var x = 1;
{ 
var x = 2;
}
console.log(x); //2

另一个常见问题是在循环中使用类似 setTimeout() 的异步操作。下面的循环代码将显示五次数字 5。

(function run(){
for(var i=0; i<5; i++){
setTimeout(function logValue(){
console.log(i); //5
}, 100);
}
})();

带有 let 声明的 for 循环语句在每次循环都会创建一个新的变量并设置到块作用域。下一段循环代码将会显示 0 1 2 3 4 5。

(function run(){
for(let i=0; i<5; i++){
setTimeout(function log(){
console.log(i); //0 1 2 3 4
}, 100);
}
})();

词法作用域

词法作用域是内部函数访问定义它的外部作用域的能力。

看一下这段代码:

(function autorun(){
let x = 1;
function log(){
console.log(x);
};
function run(fn){
let x = 100;
fn();
}
run(log);//1
})();

log 函数是一个闭包。它从父函数 autorun() 引用 x 变量,而不是 run() 函数中的 x 变量。

闭包函数可以访问创建它的作用域,而不是它自己的作用域。

autorun() 的局部函数作用域是 log() 函数的词法作用域。

作用域链

每个作用域都有一个指向父作用域的链接。当使用变量时,JavaScript 会向下查看作用域链,直到它找到所请求的变量或者到达全局作用域(即作用域链的末尾)。

看下面这个例子:

let x0 = 0;
(function autorun1(){
let x1 = 1;
(function autorun2(){
let x2 = 2;
(function autorun3(){
let x3 = 3;
console.log(x0 + " " + x1 + " " + x2 + " " + x3);//0 1 2 3
})();
})();
})();

内部函数 autorun3() 可以访问本地 x3 变量。还可以从外部函数访问变量 x1 和 x2 和全局变量 x0 。

如果找不到变量,它将在严格模式下返回错误。

"use strict";
x = 1;
console.log(x)
//Uncaught ReferenceError: x is not defined

非严格模式也被称为“草率模式”,它会草率的创建一个全局变量。

x = 1;
console.log(x); //1

总结

在全局作用域中定义的变量可在程序的任何位置使用。

在模块中,在函数外部声明的变量都是隐藏的,除非被显式导出,否则不可用于其他模块。

函数作用域意味着函数中定义的参数和变量在函数的任意位置都可见

用 let 和 const 声明的变量具有块作用域。 var 没有块作用域。

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

Javascript 相关文章推荐
脚本安需导入(装载)的三种模式的对比
Jun 24 Javascript
通过jQuery打造支持汉字,拼音,英文快速定位查询的超级select插件
Jun 18 Javascript
图片在浏览器中底部对齐 解决方法之一
Nov 30 Javascript
绑定回车enter事件代码
May 18 Javascript
底部悬浮通栏可以关闭广告位的实现方法
Jun 01 Javascript
JavaScript创建对象_动力节点Java学院整理
Jun 27 Javascript
JavaScript之iterable_动力节点Java学院整理
Jun 29 Javascript
利用C/C++编写node.js原生模块的方法教程
Jul 07 Javascript
JavaScript 值类型和引用类型的初次研究(推荐)
Jul 19 Javascript
Node.js实现批量下载图片简单操作示例
Jan 18 Javascript
JS使用正则表达式实现常用的表单验证功能分析
Apr 30 Javascript
详解Js模块化的作用原理和方案
Apr 29 Javascript
通过循环优化 JavaScript 程序
Jun 24 #Javascript
在React中写一个Animation组件为组件进入和离开加上动画/过度效果
Jun 24 #Javascript
node中实现删除目录的几种方法
Jun 24 #Javascript
什么时候不能在 Node.js 中使用 Lock Files
Jun 24 #Javascript
vue-cli脚手架引入弹出层layer插件的几种方法
Jun 24 #Javascript
浅谈一个webpack构建速度优化误区
Jun 24 #Javascript
vue项目中运用webpack动态配置打包多种环境域名的方法
Jun 24 #Javascript
You might like
PHP两种快速排序算法实例
2015/02/15 PHP
微信支付开发告警通知实例
2016/07/12 PHP
PHP实现十进制数字与二十六进制字母串相互转换操作示例
2018/08/10 PHP
比较全的JS checkbox全选、取消全选、删除功能代码
2008/12/19 Javascript
基于javascipt-dom编程 table对象的使用
2013/04/22 Javascript
AngularJS使用ng-repeat指令实现下拉框
2016/08/23 Javascript
Node.js通过身份证号验证年龄、出生日期与性别方法示例
2017/03/09 Javascript
nodejs mysql 实现分页的方法
2017/06/06 NodeJs
Vue组件实例间的直接访问实现代码
2017/08/20 Javascript
详解bootstrap用dropdown-menu实现上下文菜单
2017/09/22 Javascript
浅谈React碰到v-if
2018/11/04 Javascript
vue 中使用 watch 出现了如下的报错的原因分析
2019/05/21 Javascript
vue 微信扫码登录(自定义样式)
2020/01/06 Javascript
python实现感知器
2017/12/19 Python
Python实现类似比特币的加密货币区块链的创建与交易实例
2018/03/20 Python
Python实现手写一个类似django的web框架示例
2018/07/20 Python
在python环境下运用kafka对数据进行实时传输的方法
2018/12/27 Python
在Python中通过getattr获取对象引用的方法
2019/01/21 Python
PyCharm 2019.3发布增加了新功能一览
2019/12/08 Python
浅谈优化Django ORM中的性能问题
2020/07/09 Python
Python+unittest+requests+excel实现接口自动化测试框架
2020/12/23 Python
python 制作网站小说下载器
2021/02/20 Python
h5实现获取用户地理定位的实例代码
2017/07/17 HTML / CSS
法国综合购物网站:RueDuCommerce
2016/09/12 全球购物
医学类导师推荐信范文
2013/11/19 职场文书
总经理助理的职责
2014/03/14 职场文书
经理任命书模板
2014/06/06 职场文书
竞选大队干部演讲稿
2014/09/11 职场文书
2014年挂职干部工作总结
2014/12/06 职场文书
面试通知单大全
2015/04/20 职场文书
社区义诊通知
2015/04/24 职场文书
2015年采购员工作总结
2015/04/27 职场文书
运动会三级跳加油稿
2015/07/21 职场文书
复制别人的成功真的会成功吗?
2019/10/17 职场文书
html5表单的required属性使用
2021/07/07 HTML / CSS
浅谈JavaScript浅拷贝和深拷贝
2021/11/07 Javascript