上一篇文章中分析了 JS 中的数据类型和变量。这一篇文章将分析作用域,以及回答上一篇文章中变量提升的原因。

什么是作用域

作用域是一套规则,保存着变量,等待被引擎所查找。

var a = 1;
console.log(a);  // => 1
console.log(b);  // => ReferenceError

当打印 a 时,引擎就去作用域中查找 a,找到把结果返回。如果查找失败,那么就会报错。

词法作用域

JS 采用的词法作用域,也可以说是静态作用域。简单来说,词法作用域是由写代码时将变量写在哪里决定的。

先看一段代码:

var a = 1;function fn() {var a = 2;return a;
}fn();  // => 2

当执行函数 fn 时,会返回 2,而不是 1。

作用域查找

JS 引擎会进行两种查找,LHS 和 RHS。怎么理解?L 和 R 可以说代表左和右。什么的左和右?赋值操作的。

这里的赋值操作不一定出现 =,比如参数传递也是一个赋值操作。

当变量出现在赋值操作的左边时,引擎就会对这个变量进行 LHS 查找;当出现在右边时(这个还可以理解为取得变量的源值),就会进行 RHS 查找。

function foo(a) {console.log(a);
}foo(2);

对于变量 a 来说,引擎会进行两次查找,1 次 LHS,1 次 RHS。

调用 foo(),并传入参数 2,这时存在着一个赋值操作即 a = 2,进行一次 LHS 查找。打印 a 时,需要获取 a 的源值,所以进行一次 RHS 查找。

如果查询失败呢?

对于 LHS 来说,给未声明的赋值就会查询失败。

a = 2;

但是我们知道,上面的代码在非严格模式下并不会报错,而变量 a 会被自动创建。

而对于 RHS 来说,直接使用未声明的变量就会报 ReferenceError。

console.log(a); // => ReferenceError

另外,RHS 虽然查询成功,但是却对查询结果进行非法操作,就会报 TypeError。

var foo = 1;
foo(); // => TypeError

作用域链

前面说,作用域是根据名称查找变量的一套规则。而在实际情况中,经常出现多个作用域嵌套的情况。

function foo(a) {console.log(a + b);
}
var b = 2;
foo(2); // => 4

当引擎对 b 进行 RHS 查找时,在当前作用域无法找到,引擎就会在外层作用域中查找,直到找到这个变量,或者直到抵达最外层作用域(全局作用域)为止。

LHS 查找也是如此。

把这样一层一层嵌套的作用域,叫做作用域链。

函数作用域

函数作用域是指,属于这个函数的全部变量都可以在这个函数的范围内使用及复用。

function foo() {var a = 1;
}console.log(a); // => ReferenceError

也就是说,函数外部将无法访问函数内部的变量。

但是这却是非常有用的。我们可以利用函数隐藏内部实现,使其外部无法访问、修改等。

立即执行函数表达式

利用函数作用域,可以将外部作用域无法访问的内容包装起来。但是,带来了额外的一个问题,函数名本身“污染”了所在的作用域。

这时,就提出了 IIFE(立即执行函数表达式)。

(function foo() {// ...
}());

即包装了内部函数,又避免了引入函数名。因为这个函数名无法被外部作用域所访问。

IIFE 的进阶用法是给其传入参数:

(function fn(global) {// ...
})(window);

这样的好处是可以缩短查询时的作用域链。

块作用域

ES6,通过 let 和 const 引入了块作用域。

if (true) {let a = 1;
}
console.log(a); // => ReferenceError

变量提升

上一篇文章中中提到了变量提升。

在 JS 中,var a = 1; 这行代码其实会被看成 var aa = 2,并在两个阶段去执行。

在编译阶段,执行声明操作;在执行阶段,执行赋值操作。

所有的变量声明都会被提升到作用域的顶部,这个过程叫做“提升”。

函数声明也会发生提升,并且函数声明会先于变量提升:

var foo = 1;
function foo () {}typeof foo; // => 'number'

注意,只有函数声明会被提升,而函数表达式不会被提升。

var foo = 1;
var foo = function () {}typeof foo; // => 'function'

小结

这篇文章梳理了 JavaScript 中作用域的基本知识。

接下来会介绍执行上下文和闭包这两个概念,它们与作用域息息相关。

关于

这是我的公众号,记录着我的前端博客,没事儿也分享一些电影、书籍。

欢迎一起交流学习。

理解 JavaScript 作用域相关推荐

  1. 深入理解javascript作用域系列第四篇——块作用域

    前面的话 尽管函数作用域是最常见的作用域单元,也是现行大多数javascript最普遍的设计方法,但其他类型的作用域单元也是存在的,并且通过使用其他类型的作用域单元甚至可以实现维护起来更加优秀.简洁的 ...

  2. JavaScript 开发进阶:理解 JavaScript 作用域和作用域链(上)

    作用域是JavaScript最重要的概念之一,想要学好JavaScript就需要理解JavaScript作用域和作用域链的工作原理.今天这篇文章对JavaScript作用域和作用域链作简单的介绍,希望 ...

  3. 深入理解javascript作用域系列第三篇

    前面的话 一般认为,javascript代码在执行时是由上到下一行一行执行的.但实际上这并不完全正确,主要是因为声明提升的存在.本文是深入理解javascript作用域系列第三篇--声明提升(hois ...

  4. JavaScript 开发进阶:理解 JavaScript 作用域和作用域链

    作用域是JavaScript最重要的概念之一,想要学好JavaScript就需要理解JavaScript作用域和作用域链的工作原理.今天这篇文章对JavaScript作用域和作用域链作简单的介绍,希望 ...

  5. [未完整]JavaScript 开发进阶:理解 JavaScript 作用域和作用域链

    转载:http://www.cnblogs.com/lhb25/archive/2011/09/06/javascript-scope-chain.html 作用域是JavaScript最重要的概念之 ...

  6. 深入理解Javascript作用域和作用域链

    什么是作用域 作用域是代码运行时某些特定的部分中变量.函数和对象的可访问性,换句话说,作用域决定了代码块中变量和其他资源的可见性 作用域共有两种工作模型 词法作用域(静态作用域) 动态作用域 词法作用 ...

  7. js面试与笔试---理解 JavaScript 作用域和作用域链

    任何程序设计语言都有作用域的概念,简单的说,作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期.在JavaScript中,变量的作用域有全局作用域和局部作用域两种. 1.   ...

  8. 几道题目理解JavaScript作用域、作用域链、预解析规则、表达式

    先看题目 1.结果是 undefined console.log(a)var a = 1 复制代码 2.报错 Uncaught ReferenceError: Cannot access 'a' be ...

  9. 理解 JavaScript 作用域和作用域链

    http://www.cnblogs.com/lhb25/archive/2011/09/06/javascript-scope-chain.html 转载于:https://www.cnblogs. ...

最新文章

  1. opensips中db_default_url解析存在的bug
  2. [号外] Blazor wasm 其实也挺快!
  3. div中同时存在文本和数字超过两行出省略号
  4. webpack基本打包配置流程
  5. python动态导入类或函数_Python 动态从文件中导入类或函数的方法
  6. c# oracle异常,C# 连接Oracle数据库异常总结
  7. Picture Control控件图象保存为bmp,jpg,emf,tif,gif
  8. 【线性代数】详解正定矩阵、实对称矩阵、矩阵特征值分解、矩阵 SVD 分解
  9. rk3399 rt5640 录音调试记录
  10. css样式实现居中对齐
  11. windows下CMD常用命令
  12. 多通道ECG心率监测系统
  13. ​杭州阿里、海康、网易等组成 HR 联盟,以后你还敢跳槽吗?
  14. 关于Linux中的SIGABRT信号
  15. Vi编辑异常退出解决
  16. 浅谈解析库XPath,bs4和pyquery
  17. 如何设置Windows XP自动登录
  18. 移动机器人技术(9)-- 全向移动机器人Modeling and Control
  19. FCF中地址控制域设定值对帧中所包含地址的影响
  20. java基于安卓Android微信小程序的的桃源婚恋交友APP

热门文章

  1. C语言三目运算符 - C语言零基础入门教程
  2. BugkuCTF-Crypto题散乱的密文
  3. 2020计算机语言排行 rust,RedMonk 2020 年 Q3 编程语言排行:Rust 首次进入前 20
  4. mysql datetime不支持小数_如何从python向MYSQL中插入空小数/datetime?
  5. lammps计算聚合物例子_LAMMPS模拟聚合物结构,非晶态聚合物变形行为的模拟,纳米线变形模拟,单轴张力模拟,晶格参数计算...
  6. php 数组排序 按值,php – 按值排序数组
  7. java欧冠抽签,欧冠抽签吐槽:最大的“礼包”被C罗拿走!梅西出局概率超50%?...
  8. 窄带语谱图c语言算法,MELP语音编码算法实现及算法改进
  9. 骁龙660是32位还是64位_微软公布v2004最低处理器要求,放弃32位系统,你的CPU还能支持吗?...
  10. matlab 转换图片格式,Matlab实现图片格式转换 pgm转jpg等