

简单理解 JavaScript 的词法作用域
source link: https://www.fly63.com/article/detial/12069
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

简单理解 JavaScript 的词法作用域
扫一扫分享
关于作用域的有关知识点有全局作用域、局部作用域、函数作用域、块级作用域、词法作用域、作用域链。
作用域就像是一个教室,上课时教室里面的人互相可见,A 教室里的人不可以看见 B 教室里的人。作用域决定了代码生效的区域以及资源(变量、函数)可见的区域。
function fun() {
let a = 20;
}();
console.log(a); // Uncaught ReferenceError: a is not defined
无法获得fun函数中定义的变量a。
全局作用域
全局作用域的范围比其他的作用域的范围更大,关系就像是一切 JavaScript 对象的顶层都是 Object。<script>或.js可以算作是一个全局作用域,定义在全局作用域的变量叫全局变量。
在全局作用域声明的变量,其他的作用域都可以访问:
let a = 20;
function fun() {
console.log(a) // => 20
}
fun();
局部作用域
定义在局部作用域里面的变量就是局部变量。局部变量只可以在局部作用域生效,局部作用域可以访问到全局作用域的变量,或是比局部作用域大一点的父作用域(嵌套作用域)。
局部作用域有块级作用域、函数作用域。
块级作用域
在 ES5 及以前,块级作用域受var影响是无效的,具体请看ES6 关键字 let 和 ES5 及以前关键字 var 的区别。
{
var x = 10;
}
console.log(x) // => 10
for (var i = 0; i < 10; i++) {
// ...
}
console.log(i); // 10
最后打印for语句的变量 i,得到 10,实际上在语句内部打印最终循环的结果是 9。ES6 之后的关键字let声明的变量,外部想要使用块级作用域的变量x就会报错:
{
let x = 10;
}
console.log(x); // Uncaught ReferenceError: x is not defined
for (let i = 0; i < 10; i++) {
// ...
}
console.log(i); // Uncaught ReferenceError: i is not defined
函数作用域
关于函数作用域,有一个面试题,需要结合下面的词法作用域进行分析。
let a = 123;
function fun1() {
console.log(a);
}
function fun2() {
let a = 456;
fun1();
}
fun2();
最终的结果是 123。这是因为词法作用域已经决定了函数fun1引用的外部作用域的变量是全局作用域中的变量 a,而非函数fun2定义的局部变量a。
词法作用域
函数引用变量的静态性
词法作用域(静态作用域)是一种就近原则,也就是在我们写下代码的时候就已经决定了函数引用的变量应该是按照就近原则来引用的:
词法作用域的静态性原则,规定函数引用变量必须按照代码书写的顺序来,即便是函数被其他函数调用了,这个函数的作用域也不会发生变化,也不会因此变成了嵌套函数。
函数自身局部作用域内没有定义变量 a,而在全局作用域中,定义了变量 a,根据就近原则,所以引用的是a = 123。
let a = 123;
function fun() {
console.log(a); // => 123
}
如果函数体内有一个变量 a,结果就是:
let a = 123;
function fun() {
let a = 456;
console.log(a); // => 456
}
总而言之,函数引用变量时是按照一种自上而下,顺序来的。函数引用一个变量,前提是变量不能在函数声明之后出现。
fun();
function fun() {
console.log(a); // Uncaught ReferenceError: Cannot access 'a' before initialization
}
let a = 123;
let a = 123;
fun();
function fun() {
console.log(a); // 123
}
这里有一个悬念,为什么调用函数可以在函数声明之前?
函数调用的动态性
函数要成功引用作用域外的变量必须是变量声明在函数之前,但是函数调用可以在函数声明之前,但也必须是在变量声明之后。
在函数调用时体现出作用域的动态性,函数引用变量就体现出作用域的静态性。
function f() { g(); }
function g() {}
f();
当我们调用 f(),它会调用 g()。在执行期间,g 被 f 调用代表了一种动态的关系。
当在函数使用一个变量的时候,首先 Javascript 会尝试在当前作用域下去寻找该变量,如果没找到,再到它的上层作用域寻找,以此类推直到找到该变量或是已经到了全局作用域。
如果在全局作用域里仍然找不到该变量,它就会在全局范围内隐式声明该变量(非严格模式下)或是直接报错。

把作用域比喻成一个建筑,这份建筑代表程序中的嵌套作用域链,第一层代表当前的执行作用域,顶层代表全局作用域。
根据词法作用域静态性的原则,函数引用变量不会因为调用顺序和位置从而改变当前的作用域。所以查找变量的位置按照代码书写的位置来看。
现在可以回答那一道面试题了函数作用域:
let a = 123;
function fun1() {
console.log(a);
}
function fun2() {
let a = 456;
fun1();
}
fun2();
函数fun2声明了一个与全局作用域同名的变量 a,根据词法作用域的静态性原则,函数调用不会改变函数的作用域。函数fun1作用域内没有定义变量a,它的上级作用域就是全局作用域,而全局作用域中声明了变量 a,所以最终打印结果是 123。
- 《JavaScript 权威指南》- 第 3 章 变量作用域;
- 《深入理解 JavaScript》- 第 16 章 变量:作用域、环境和闭包;
- web前端面试 - 面试官系列 - 面试官:说说你对作用域链的理解。
Recommend
-
45
堵车节第一天,我没有出门。把以前一直只限于知道,却不清晰理解的这几个概念完完整整地梳理了一番。内容参考自wiki页面,然后加上自己一些理解。 词法作用域和动态作用域 不管什么语言,我们总要学习作用域(
-
60
1. 什么是作用域 作用域是你的代码在运行时,某些特定部分中的变量,函数和对象的可访问性。换句话说,作用域决定了变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。 2. JavaScript中的作用域 在 JavaScript 中有两种作用域
-
16
在上一篇文章 深入理解JavaScript 执行上下文 中提到 只有理解了执行上下文,才能更好地理解 JavaScript 语言本身,比如变量提升,作用域,闭包等,本篇文章就来说一下 JavaS...
-
28
哈喽!大家好!我是木瓜太香,今天我们来聊一个 vue 的样式作用域的问题,通常我们开发项目的时候是要在 style 上加上 scoped 来起到规定组件作用域的效果的,所以了解他们的规则也是很有必要的,可以让你更清晰的了解你的项目样式是怎么...
-
7
JavaScript词法作用域 词法作用域是什么? js词法作用域是什么? 个人理解:它是一种规则,规定了我们的js程序按照特定方式去查找变量。词法作用域又叫静态作用域,函数的作用域在函数定义的时候就规定了。这个规则其实粗...
-
6
javascript的词法作用域 浏览:2737次 出处信息 大家应该写过下面类似的代码吧,其实这里我想要表达的是有时候...
-
3
从javascript代码解析过程理解执行上下文与作用域提升发布于 今天 15:25 javascript代码解析过程执行上下文和作用域是javascript中非常重要的部分,要弄清...
-
9
词法结构(Lexical Structure)是程序语言的一套基础性规则,用来描述如何使用这门语言来编写程序 JavasSript 程序是用 Unicode 字符集 编写的,Unicode 是 ASCII 和 Latin-1...
-
3
Go 语言入门很简单:Go 中的作用域和变量隐藏 推荐 原创 宇宙之一粟 2022...
-
3
深入理解this作用域问题上传日期:2022.08.30this对象是在运行时基于函数的执行环境绑定的:在全局函数中,this等于window ,而当函数被作为某个对象调用时,this等于那个对象。不过,匿名函数具有全局性,因此this对象同常指向w...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK