09-JavaScript执行机制:块级作用域——var缺陷以及为什么要引入let和const?
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了09-JavaScript执行机制:块级作用域——var缺陷以及为什么要引入let和const?,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含4157字,纯文字阅读大概需要6分钟。
内容图文
由于 JavaScript 存在变量提升这种特性,从而导致了很多与直觉不符的代码,这也是 JavaScript 的一个重要设计缺陷。
- 分析为什么在 JavaScript 中会存在变量提升,以及变量提升所带来的问题
- 介绍如何通过块级作用域并配合 let 和 const 关键字来修复这种缺陷
作用域(scope)
作用域是指在程序中定义变量的区域,该位置决定了变量的生命周期。通俗地理解,作用域就是变量与函数的可访问范围,即作用域控制着变量和函数的可见性和生命周期。
在 ES6 之前,ES 的作用域只有两种:
- 全局作用域中的对象在代码中的任何地方都能访问,其生命周期伴随着页面的生命周期。
- 函数作用域就是在函数内部定义的变量或者函数,并且定义的变量或者函数只能在函数内部被访问。函数执行结束之后,函数内部定义的变量会被销毁。
ES6 之前是不支持块级作用域的
在ES3开始,
try /catch
分句结构中也具有块作用域。
块级作用域
块级作用域就是使用一对大括号包裹的一段代码,比如函数、判断语句、循环语句,甚至单独的一个{}
都可以被看作是一个块级作用域。
//if块
if(1){}
//while块
while(1){}
//函数块
function foo(){}
//for循环块
for(let i = 0; i<100; i++){}
//单独一个块
{}
变量提升所带来的问题
1. 变量容易在不被察觉的情况下被覆盖掉
var myname = "极客时间"
function showName(){
console.log(myname);
if(0){
var myname = "极客邦"
}
console.log(myname);
}
showName()
开始执行 showName 函数时的调用栈
先使用函数执行上下文里面的变量,输出两个undefined
2. 本应销毁的变量没有被销毁
function foo(){
for (var i = 0; i < 7; i++) {
}
console.log(i);
}
foo()
在创建执行上下文阶段,变量 i 就已经被提升了,所以当 for 循环结束之后,变量 i 并没有被销毁。最后打印出来的是 7。
这同样也是由变量提升?导致的,在创建执?上下?阶段,变量i就已经被提升了,所以当for循环结束之 后,变量i并没有被销毁。 这依旧和其他?持块级作?域的语?表现是不?致的,所以必然会给?些?造成误解。
ES6 是如何解决变量提升带来的缺陷
ES6 引入了 let 和 const 关键字,从而使 JavaScript 也能像其他语言一样拥有了块级作用域。
let 和 const 的用法:
let x = 5
const y = 6
x = 7
y = 9 //报错,const声明的变量不可以修改
从这段代码你可以看出来,两者之间的区别是,使?let关键字声明的变量是可以被改变的,?使?const声 明的变量其值是不可以被改变的。但不管怎样,两者都可以?成块级作?域,为了简单起?,在下?的代码 中,我统?使?let关键字来演?。 那么接下来,我们就通过实际的例?来分析下,ES6是如何通过块级作?域来解决上?的问题的? 你可以先参考下?这段存在变量提升的代码:
ES6 是如何通过块级作用域来解决上面的问题的?
1、存在变量提升的代码:
function varTest() {
var x = 1;
if (true) {
var x = 2; // 同样的变量!
console.log(x); // 2
}
console.log(x); // 2
}
在编译阶段,会生成varTest 函数的执行上下文:
只生成了一个变量 x,函数体内所有对 x 的赋值操作都会直接改变变量环境中的 x 值。
2、把 var 关键字替换为 let 关键字,改造后的代码如下:
function letTest() {
let x = 1;
if (true) {
let x = 2; // 不同的变量
console.log(x); // 2
}
console.log(x); // 1
}
let 关键字是支持块级作用域的,所以在编译阶段,JavaScript 引擎并不会把 if 块中通过 let 声明的变量存放到变量环境中,这也就意味着在 if 块通过 let 声明的关键字,并不会提升到全函数可见。
JavaScript 是如何支持块级作用域的
JavaScript 引擎是通过变量环境实现函数级作用域的,那么 ES6 又是如何在函数级作用域的基础之上,实现对块级作用域的支持呢?
先看一下下面这段代码
function foo(){
var a = 1
let b = 2
{
let b = 3
var c = 4
let d = 5
console.log(a)
console.log(b)
}
console.log(b)
console.log(c)
console.log(d)
}
foo()
执行结果
执行流程
第一步是编译并创建执行上下文
foo 函数的执行上下文
- 函数内部通过 var 声明的变量,在编译阶段全都被存放到变量环境里面。
- 通过 let 声明的变量,在编译阶段会被存放到词法环境(
Lexical Environment
)中。 - 在函数的作用域块内部,通过 let 声明的变量并没有被存放到词法环境中。
第二步继续执行到代码块里面时
在词法环境内部,维护了一个小型栈结构,栈底是函数最外层的变量,进入一个作用域块后,就会把该作用域块内部的变量压到栈顶;当作用域执行完成之后,该作用域的信息就会从栈顶弹出,这就是词法环境的结构。(变量是指通过 let 或者 const 声明的变量。)
当执行到作用域块中的console.log(a)这行代码时,就需要在词法环境和变量环境中查找变量 a 的值了,具体查找方式是:
- 沿着词法环境的栈顶向下查询,如果在词法环境中的某个块中查找到了,就直接返回给 JavaScript 引擎
- 如果没有查找到,那么继续在变量环境中查找。
查找过程:
当作用域块执行结束之后,其内部定义的变量就会从词法环境的栈顶弹出
块级作用域就是通过词法环境的栈结构来实现的,而变量提升是通过变量环境来实现,通过这两者的结合,JavaScript 引擎也就同时支持了变量提升和块级作用域了
内容总结
以上是互联网集市为您收集整理的09-JavaScript执行机制:块级作用域——var缺陷以及为什么要引入let和const?全部内容,希望文章能够帮你解决09-JavaScript执行机制:块级作用域——var缺陷以及为什么要引入let和const?所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。