一次让你了解全部JavaScript的作用域
前端  /  管理员 发布于 4年前   229
前言
作用域决定了变量的生命周期和可见性,变量在作用域范围之外是不可见的。
JavaScript 的作用域包括:模块作用域,函数作用域,块作用域,词法作用域和全局作用域。
全局作用域
在任何函数、块或模块范围之外定义的变量具有全局作用域。可以在程序的任意位置访问全局变量。
当启用模块系统时,创建全局变量会变得困难,但仍然可以做到这一点。可以在 HTML 中定义一个变量,这个变量需要在函数之外声明,这样就可以创建一个全局变量:
console.log(GLOBAL_DATA);
当没有模块系统时,创建全局变量会容易很多。在任何文件中的函数外声明的变量都是全局变量。
全局变量贯穿于程序的整个生命周期。
另一种创建全局变量的方法是在程序的任意位置使用 window 全局对象:
window.GLOBAL_DATA = { value: 1 };
这样 GLOBAL_DATA 变量会随处可见。
console.log(GLOBAL_DATA)
不过你也知道这种做法是不好的。
模块作用域
如果不启用模块,在所有函数之外声明的变量是全局变量。在模块中,在函数外部声明的变量都是隐藏的,除非显式导出,否则不可用于其他模块。
导出使函数或对象可用于其他模块。在这个例子中,我从模块文件 sequence.js 中导出了一个函数:
// in sequence.jsexport { 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 = 1var x = 2;console.log(x);}doSomething();
用 let 或 const 声明的变量不能在同一作用域内重新声明:
function doSomething(){let x = 1let 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 没有块作用域。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
123 在
Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..原梓番博客 在
在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..博主 在
佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..1111 在
佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..路人 在
php中使用hyperf框架调用讯飞星火大模型实现国内版chatgpt功能示例中评论 教程很详细,如果加个前端chatgpt对话页面就完美了..Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号