一次让你了解全部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 相关文章推荐
Prototype使用指南之base.js
Jan 10 Javascript
基于jQuery制作迷你背词汇工具
Jul 27 Javascript
jQuery EasyUI API 中文文档 - ComboGrid 组合表格
Oct 13 Javascript
jQueryUI中的datepicker使用方法详解
May 25 Javascript
JS简单获取及显示当前时间的方法
Aug 03 Javascript
轻松掌握JavaScript中介者模式
Aug 26 Javascript
使用Bootstrap打造特色进度条效果
May 02 Javascript
Node接收电子邮件的实例代码
Jul 21 Javascript
react项目实践之webpack-dev-serve
Sep 14 Javascript
Vue中通过Vue.extend动态创建实例的方法
Aug 13 Javascript
layer.prompt使文本框为空的情况下也能点击确定的方法
Sep 24 Javascript
如何使用JavaScript检测空闲的浏览器选项卡
May 28 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 变量未定义等错误的解决方法
2011/01/12 PHP
检查php文件中是否含有bom的函数
2012/05/31 PHP
php 删除一维数组中某一个值元素的操作方法
2018/02/01 PHP
php实现对文件压缩简单的方法
2019/09/29 PHP
laravel 实现用户登录注销并限制功能
2019/10/24 PHP
Jquery 选中表格一列并对表格排序实现原理
2012/12/15 Javascript
有关json_decode乱码及NULL的问题
2015/10/13 Javascript
JavaScript禁止复制与粘贴的实现代码
2016/05/16 Javascript
JS+Canvas绘制时钟效果
2020/08/20 Javascript
js实现统计字符串中特定字符出现个数的方法
2016/08/02 Javascript
webpack踩坑之路图片的路径与打包
2017/09/05 Javascript
js中bool值的转换及“&amp;&amp;”、“||”、 “!!”详解
2017/12/21 Javascript
看看“疫苗查询”小程序有温度的代码
2018/07/31 Javascript
Nuxt升级2.0.0时出现的问题(小结)
2018/10/08 Javascript
vue 实现动态路由的方法
2020/07/06 Javascript
python和C语言混合编程实例
2014/06/04 Python
Python使用matplotlib实现在坐标系中画一个矩形的方法
2015/05/20 Python
Python中属性和描述符的正确使用
2016/08/23 Python
python虚拟环境virualenv的安装与使用
2016/12/18 Python
python常用知识梳理(必看篇)
2017/03/23 Python
Python装饰器用法实例总结
2018/02/07 Python
使用PyInstaller将python转成可执行文件exe笔记
2018/05/26 Python
解决win64 Python下安装PIL出错问题(图解)
2018/09/03 Python
利用python修改json文件的value方法
2018/12/31 Python
Python实现计算对象的内存大小示例
2019/07/10 Python
Python Tkinter Entry和Text的添加与使用详解
2020/03/04 Python
几款Python编译器比较与推荐(小结)
2020/10/15 Python
Mankind西班牙男士护肤品网站:购买皮肤护理、护发和剃须
2017/04/27 全球购物
英国购买威士忌网站:Master of Malt
2019/09/26 全球购物
上海微创软件面试题
2012/06/14 面试题
Windows和Linux动态库应用异同
2016/07/28 面试题
公司新员工的演讲稿注意事项
2014/01/01 职场文书
社会保险接收函
2014/01/12 职场文书
教师批评与自我批评心得体会
2014/10/16 职场文书
python 对图片进行简单的处理
2021/06/23 Python
Django模型层实现多表关系创建和多表操作
2021/07/21 Python