-
Notifications
You must be signed in to change notification settings - Fork 45
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Day16 - 为什么一定要有块级作用域? #59
Comments
块级作用域:块语句由一对大括号界定,使用 let和const声明的变量是有块级作用域的,在块级作用域内能强制执行更新变量 作用域链:是由当前环境与上层环境的一系列变量对象组成,它保证了当前执行环境对符合访问权限的变量和函数的有序访问 静态作用域又叫做词法作用域,函数的作用域在函数定义的时候就决定了 动态作用域:是在运行时根据程序的流程信息来动态确定的 |
词法阶段时编译器的第一个工作阶段。词法作用域是定义在词法阶段的作用域,词法作用域是由你在写代码时将变量和快作用域写在哪里来决定的,也就是说函数的作用域在函数定义的时候就决定了 块级作用域是在ES6中新增的作用域概念,例如两个大括号,if,for语句内的作用域都是块级作用域 作用域链是变量在当前作用域中如果没有被找到,就会去他的外部作用域去寻找,直到找到全局作用域,这一个过程形成了一个链条,称为作用域链 静态作用域的意思是函数在定义的时候就已经决定了它的作用域,而动态走用语指的是函数在被调用的时候才会决定他的作用域 |
环境作用域(Environment Record)是ECMA的一种规范类型,用来定义特殊标识符所指代的变量或函数的空间,基于词法嵌套结构。 环境作用域中包含了声明性、对象、全局环境记录 而新增的模块环境作用域和原来的function环境作用域类型,同属于声明性环境作用域。 块级作用域指的就是模块环境作用域, 作用是:消除了变量提升等问题,完善了语言基础设计,规范了语法解析规则,使js日益变为一个规范化的语言。 |
没有块级作用域,这带来很多不合理的场景
参考https://es6.ruanyifeng.com/#docs/let#%E5%9D%97%E7%BA%A7%E4%BD%9C%E7%94%A8%E5%9F%9F |
为什么需要块级作用域
|
js 并不是必须有块作用域不可,在es6之前就是没有的, |
|
由于JS历史发展原因,ES5存在变量提升等相关问题,而且会有存在变量污染以及内存泄漏等问题,同时也不便于大家的理解,这时引入块级作用域就是为了解决这些问题而产生的。 |
块级作用域是一个用来对之前的最小授权原则进行扩展的工具,将代码从在函数中隐藏信息扩展为在块中隐藏信息,从而使得变量只能在块作用域的内部使用,对保证变量不会被混乱得复用以及提升代码的可维护性有很大帮助。 |
由于JavaScript 存在变量提升这种特性,从而导致了很多与直觉不符的代码,这也是 JavaScript 的一个重要设计缺陷,而ES6只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景。 |
在Es5中,JavaScript只存在全局作用域和函数作用域,由于 JavaScript 存在变量提升这种特性,导致 |
|
在es6之前js只有全局作用域和函数作用域,会导致内层变量会不经意间覆盖全局变量,还有for循环中用来计数的循环变量泄露为全局变量,在之前解决这些问题只能创建单独的函数形成封闭的函数作用域,而块级作用域的出现很好的解决了这些问题 |
在Es5中,JavaScript只存在全局作用域和函数作用域,由于 JavaScript 存在变量提升这种特性,导致
for(let i = 0 ; i<5;i++){ //使用let变量 JS会在后台会为每个迭代循环声明一个新的迭代变量,每个settimeout引用的都是不同的变量 |
ES5 只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景。 第一种场景,内层变量可能会覆盖外层变量。 var tmp = new Date(); function f() { f(); // undefined 第二种场景,用来计数的循环变量泄露为全局变量。 var s = 'hello'; for (var i = 0; i < s.length; i++) { console.log(i); // 5 而块级作用域的出现就是为了解决上述问题,如果一种语言支持块级作用域,那么其代码块内部定义的变量在代码块外部是访问不到的,并且等该代码块中的代码执行完成之后,代码块中定义的变量会被销毁,可以帮助我们更好地控制作用域 |
没有块级作用域的影响
|
为了解决 var 变量提升带来的问题。var 声明的变量会被提升到整个作用域里面,于是就会导致块里面的变量不会被销毁,造成变量的污染。例如 for 循环,循环完之后,这个 i 依然存在。而且本来应该是每一次循环声明一个 i,但是提升导致了每次都只是对这个 i 进行赋值,使用同一个 i,这样在闭包的时候就会出现问题。另外就是变量覆盖的问题, 假设是 if 分支或者 for 循环里面声明了一个变量,那么在这个声明语句执行之前,所有跟这个变量同名的,都会被覆盖成 undefined,直到这个执行到了这个 if 分支里面的声明语句才会对这个变量进行赋值。 |
ES5 只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景。 function f() { f(); // undefined for (var i = 0; i < s.length; i++) { console.log(i); // 5 |
块级作用域由最近一对花括号来界定,let、const 声明的变量的作用域属于块级作用域。es6之前通过var声明的变量可能造成循环陷阱,因为var声明的变量提升到了函数级作用域,循环结束后使用var声明的变量的地方都变成了最后一次循环修改的值,可以通过 let 声明变量解决 。 另一方面,在比较长的函数中使用花括号来分隔代码,生成块级作用域,这样在多个块里使用let、const 声明相同变量名相互之间不受到影响。 |
|
|
在es5时代,没有块级作用域,经常会遇到内层变量覆盖外层变量的值,循环计数时变量被提升为全局变量等问题,步入es6时代后,引入let和const来定义变量,轻松解决了es5时期由于没有块级作用域而引起的一些列的问题 |
ES5只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景。 第一种场景,内层变量可能会覆盖外层变量。 第二种场景,用来计数的循环变量泄露为全局变量。 但是引入块级作用域以后就解决了上面的问题 |
var的缺陷,变量提升,变量提升所带来的问题
以上打印结果是 undefined; 2,循环陷阱
|
在没有块级作用域的以前,经常会出现以下问题
块级作用域不仅可以解决以上问题,还有写出更符合直觉的代码,与其他拥有块级作用域的语言一致 |
此处由于内部的 a 变量提升导致覆盖了外部的 a
|
es5的时候由于var变量存在变量提升导致一些列问题比如昨天说的循环陷阱,但这些可以通过自调函数来解决。es6的引入let能避免此类问题的发生 |
块级作用域可以让我们的变量只在当前代码块中可以访问,对保证变量不会被混乱的复用以及提升代码的可维护性有很大帮助。 例如下面的代码:
可以看到我们在
同时因为
上面的示例中可以看到,我们最开始声明的 |
为什么一定要有块级作用域呢?
|
其实真的不需要有块级作用域,至少JS语言的设计者一开始是这么想的,所以他们一开始就没有设计块级作用域。他们觉得只要有函数作用域,全局作用域就够了。 块级作用域的作用和函数级作用域的作用,其实是相同的。我们可以先思考一下,没有函数级作用域会怎么样,只有全局作用域,那简直就不可想象,所有变量都是存在于全局,那么变量的冲突的可能性会变高。 那么,用这个道理其实是套在块级作用域也是一样的。没有块级作用域的话,其实函数里变量名的冲突也会更高,但有了块级作用域之后呢,它能降低这种冲突的可能性,为编程带来方便。 |
The text was updated successfully, but these errors were encountered: