Skip to content
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

Open
wzl624 opened this issue Jan 14, 2022 · 30 comments
Open

Day16 - 为什么一定要有块级作用域? #59

wzl624 opened this issue Jan 14, 2022 · 30 comments

Comments

@wzl624
Copy link

wzl624 commented Jan 14, 2022

@ruixue0702
Copy link

块级作用域:块语句由一对大括号界定,使用 let和const声明的变量是有块级作用域的,在块级作用域内能强制执行更新变量

作用域链:是由当前环境与上层环境的一系列变量对象组成,它保证了当前执行环境对符合访问权限的变量和函数的有序访问

静态作用域又叫做词法作用域,函数的作用域在函数定义的时候就决定了

动态作用域:是在运行时根据程序的流程信息来动态确定的

@oujinlong
Copy link

词法阶段时编译器的第一个工作阶段。词法作用域是定义在词法阶段的作用域,词法作用域是由你在写代码时将变量和快作用域写在哪里来决定的,也就是说函数的作用域在函数定义的时候就决定了

块级作用域是在ES6中新增的作用域概念,例如两个大括号,if,for语句内的作用域都是块级作用域

作用域链是变量在当前作用域中如果没有被找到,就会去他的外部作用域去寻找,直到找到全局作用域,这一个过程形成了一个链条,称为作用域链

静态作用域的意思是函数在定义的时候就已经决定了它的作用域,而动态走用语指的是函数在被调用的时候才会决定他的作用域

@su37josephxia su37josephxia changed the title ### 作用域 为什么一定要有块级作用域? Jan 15, 2022
@MMmaXingXing
Copy link

环境作用域(Environment Record)是ECMA的一种规范类型,用来定义特殊标识符所指代的变量或函数的空间,基于词法嵌套结构。

环境作用域中包含了声明性、对象、全局环境记录

而新增的模块环境作用域和原来的function环境作用域类型,同属于声明性环境作用域。

块级作用域指的就是模块环境作用域,

作用是:消除了变量提升等问题,完善了语言基础设计,规范了语法解析规则,使js日益变为一个规范化的语言。

@bianzheCN
Copy link

bianzheCN commented Jan 16, 2022

没有块级作用域,这带来很多不合理的场景

  • 变量提升导致变量的覆盖

  • 用来计数的循环变量泄露为全局变量

  • 让代码更模块化

参考

https://es6.ruanyifeng.com/#docs/let#%E5%9D%97%E7%BA%A7%E4%BD%9C%E7%94%A8%E5%9F%9F

@yanzefeng
Copy link

为什么需要块级作用域

  • ES5只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景
    • 第一种场景,内层变量可能会覆盖外层变量。
    • 第二种场景,用来计数的循环变量泄露为全局变量

@Limeijuan
Copy link

js 并不是必须有块作用域不可,在es6之前就是没有的,
但是没有块作用域的话,会带来一些不合理的场景:比如用来计数的循环变量泄露为全局变量, var的变量提升导致变量覆盖外部变量;
es6之前可以使用立即调用函数模拟块级作用域;es6新增了块作用域, 就更加方便了

@wzl624
Copy link
Author

wzl624 commented Jan 16, 2022

  • ES5只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景。
  • 第一种场景,内层变量可能会覆盖外层变量。
    var time=new Date(); //获取系统的当前时间
    function fun(){
    console.log(time);//undefined,
    if(true){
    var time="hello world!";//内部的time把外面全局的time的地址给覆盖了,但是只有false时才会运行time="hello world!",于是undefined
    };
    };
    fun();
  • 第二种场景,用来计数的循环变量泄露为全局变量。
    var string="hello world!" ;
    for(var i=0;i<string.length;i++){
    console.log(string[i]);
    };
    console.log("循环结束");
    console.log(i); //输出12

@QbjGKNick
Copy link

由于JS历史发展原因,ES5存在变量提升等相关问题,而且会有存在变量污染以及内存泄漏等问题,同时也不便于大家的理解,这时引入块级作用域就是为了解决这些问题而产生的。

@qytayh
Copy link

qytayh commented Jan 16, 2022

块级作用域是一个用来对之前的最小授权原则进行扩展的工具,将代码从在函数中隐藏信息扩展为在块中隐藏信息,从而使得变量只能在块作用域的内部使用,对保证变量不会被混乱得复用以及提升代码的可维护性有很大帮助。

@zzzz-bang
Copy link

由于JavaScript 存在变量提升这种特性,从而导致了很多与直觉不符的代码,这也是 JavaScript 的一个重要设计缺陷,而ES6只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景。
第一种场景,内层变量可能会覆盖外层变量。
第二种场景,用来计数的循环变量泄露为全局变量。
而块级作用域的出现就是为了解决上述问题,如果一种语言支持块级作用域,那么其代码块内部定义的变量在代码块外部是访问不到的,并且等该代码块中的代码执行完成之后,代码块中定义的变量会被销毁,可以帮助我们更好地控制作用域

@alienRidingCat
Copy link

在Es5中,JavaScript只存在全局作用域和函数作用域,由于 JavaScript 存在变量提升这种特性,导致
● 变量容易在不被察觉的情况下被覆盖掉,比如在函数内访问全局变量a之后重新定义变量a,由于变量提升导致全局变量在被访问时会被重新覆盖为undefined
● 本应销毁的变量没有被销毁,比如常见的循环陷阱问题,循环中的作用域实际上是共用的,在 for 循环结束之后,本应被销毁的变量并不会被销毁,使用累加结束后的变量去绑定事件会获得与预期不符的情况出现
而块级作用域的出现就解决了这些不合理,使得代码结果更加符合预期

@chunhuigao
Copy link

chunhuigao commented Jan 16, 2022

  • 不是说一定要有块级作用域?在ES3、5时代没有块级作用域,JavaScript依然活跃;但是在使用过程中会出现一下问题;比如for循环中var变量会跑到全局;比如外部变量可能会被函数中的变量覆盖。
  • ES6中块级作用域,能有效的解决这些问题.

@yaoqq632319345
Copy link

在es6之前js只有全局作用域和函数作用域,会导致内层变量会不经意间覆盖全局变量,还有for循环中用来计数的循环变量泄露为全局变量,在之前解决这些问题只能创建单独的函数形成封闭的函数作用域,而块级作用域的出现很好的解决了这些问题

@674252256
Copy link

在Es5中,JavaScript只存在全局作用域和函数作用域,由于 JavaScript 存在变量提升这种特性,导致

  • 变量容易在不被察觉的情况下被覆盖掉
  • 比如在for循环中的let声明,for循环定义的迭代变量会渗透到循环体外部;
  • 改成let之后,这个问题就消失了,因为迭代变量的作用域仅限于for循环内部;
    `
    for(var i = 0 ; i<5;i++){ //使用var变量是因为在退出循环时迭代变量保存的是导致循环退出的变量
    setTimeout (()=>{
    console.log(i); //55555
    },0)
    }

for(let i = 0 ; i<5;i++){ //使用let变量 JS会在后台会为每个迭代循环声明一个新的迭代变量,每个settimeout引用的都是不同的变量
setTimeout (()=>{
console.log(i); // 01234
},0)
}
`

@partiallove
Copy link

ES5 只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景。

第一种场景,内层变量可能会覆盖外层变量。

var tmp = new Date();

function f() {
console.log(tmp);
if (false) {
var tmp = 'hello world';
}
}

f(); // undefined
上面代码的原意是,if代码块的外部使用外层的tmp变量,内部使用内层的tmp变量。但是,函数f执行后,输出结果为undefined,原因在于变量提升,导致内层的tmp变量覆盖了外层的tmp变量。

第二种场景,用来计数的循环变量泄露为全局变量。

var s = 'hello';

for (var i = 0; i < s.length; i++) {
console.log(s[i]);
}

console.log(i); // 5
上面代码中,变量i只用来控制循环,但是循环结束后,它并没有消失,泄露成了全局变量。

而块级作用域的出现就是为了解决上述问题,如果一种语言支持块级作用域,那么其代码块内部定义的变量在代码块外部是访问不到的,并且等该代码块中的代码执行完成之后,代码块中定义的变量会被销毁,可以帮助我们更好地控制作用域

@aiuluna
Copy link

aiuluna commented Jan 16, 2022

没有块级作用域的影响

  • 内层变量覆盖外部变量,代码量过长情况下很容易出现
  • 用来计数的循环变量泄露为全局变量,导致计数错误

@zcma11
Copy link

zcma11 commented Jan 16, 2022

为了解决 var 变量提升带来的问题。var 声明的变量会被提升到整个作用域里面,于是就会导致块里面的变量不会被销毁,造成变量的污染。例如 for 循环,循环完之后,这个 i 依然存在。而且本来应该是每一次循环声明一个 i,但是提升导致了每次都只是对这个 i 进行赋值,使用同一个 i,这样在闭包的时候就会出现问题。另外就是变量覆盖的问题, 假设是 if 分支或者 for 循环里面声明了一个变量,那么在这个声明语句执行之前,所有跟这个变量同名的,都会被覆盖成 undefined,直到这个执行到了这个 if 分支里面的声明语句才会对这个变量进行赋值。

@BambooSword
Copy link

ES5 只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景。
第一种场景,内层变量可能会覆盖外层变量。
var tmp = new Date();

function f() {
console.log(tmp);
if (false) {
var tmp = 'hello world';
}
}

f(); // undefined
第二种场景,用来计数的循环变量泄露为全局变量。
var s = 'hello';

for (var i = 0; i < s.length; i++) {
console.log(s[i]);
}

console.log(i); // 5

@liangle
Copy link

liangle commented Jan 16, 2022

块级作用域由最近一对花括号来界定,let、const 声明的变量的作用域属于块级作用域。es6之前通过var声明的变量可能造成循环陷阱,因为var声明的变量提升到了函数级作用域,循环结束后使用var声明的变量的地方都变成了最后一次循环修改的值,可以通过 let 声明变量解决 。

另一方面,在比较长的函数中使用花括号来分隔代码,生成块级作用域,这样在多个块里使用let、const 声明相同变量名相互之间不受到影响。

@guoshukun1994
Copy link

  1. 在Es6之前是没有块级作用域的,都是用var来声明变量,变量会被提升至全局环境,这样就会造成各层函数之间的变量变得混淆,容易互相污染,难以使用,而用块级作用的出现正是解决了这种问题,让我们很容易的拥有独立的块级作用域;

  2. 例如Es6中的let、const声明变量的作用域就是块级作用域,只有在该级作用域以及内层嵌套函数作用域才能使用这些变量。

@superjunjin
Copy link

  • 内层块级作用域变量不影响外层块级作用域变量,

    保证更小的块级作用域是一个独立的互不干扰的作用域空间

    function f1() {
     let n = 5;
     if (true) {
       let n = 10;
     }
     console.log(n); // 5
    }
    function f1() {
     var n = 5;
     if (true) {
       var n = 10;
     }
     console.log(n); // 10
    }
  • 外层块级作用域无法读取内层块级作用域的变量

@ruixue0702
Copy link

ruixue0702 commented Jan 16, 2022

在es5时代,没有块级作用域,经常会遇到内层变量覆盖外层变量的值,循环计数时变量被提升为全局变量等问题,步入es6时代后,引入let和const来定义变量,轻松解决了es5时期由于没有块级作用域而引起的一些列的问题

@792472461
Copy link

ES5只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景。

第一种场景,内层变量可能会覆盖外层变量。

第二种场景,用来计数的循环变量泄露为全局变量。

但是引入块级作用域以后就解决了上面的问题

@JanusJiang1
Copy link

var的缺陷,变量提升,变量提升所带来的问题
1,变量容易在不被察觉的情况下被覆盖掉

var myname = "极客时间"
function showName(){
  console.log(myname);
  if(0){
   var myname = "极客邦"
  }
  console.log(myname);
}
showName()

以上打印结果是 undefined;
虽然if(0)=false, var myname = "极客邦"并未执行,但是因为变量提升,会先在showName声明var myname =undefined;

2,循环陷阱

function foo(){
  for (var i = 0; i < 7; i++) {
  }
  console.log(i); //7
}
foo()

@jiafei-cat
Copy link

在没有块级作用域的以前,经常会出现以下问题

  • 想访问函数外的全局作用域的变量,结果访问到函数内变量提升的undefined
  • 循环使用的变量提升到上层作用域且未销毁

块级作用域不仅可以解决以上问题,还有写出更符合直觉的代码,与其他拥有块级作用域的语言一致

@rachern
Copy link

rachern commented Jan 16, 2022

  1. 防止内层变量覆盖外层变量
    var 声明的变量会出现变量提升的现象,变量声明会提升到函数作用域或者全局作用域顶部
var a = 123
function test() {
    console.log(a)
    if(false) {
        var a = 'xx'
    }
}
test()   // undefined

此处由于内部的 a 变量提升导致覆盖了外部的 a

  1. 防止循环陷阱
    在 es5 中为了解决循环陷阱需要使用立即执行函数,而 es6 引入了 块级作用域之后,不需要使用立即执行函数去解决循环陷阱问题,只需要将 for 循环的变量使用 let 声明就可以了

@alec1815
Copy link

es5的时候由于var变量存在变量提升导致一些列问题比如昨天说的循环陷阱,但这些可以通过自调函数来解决。es6的引入let能避免此类问题的发生

@zhenyuWang
Copy link

块级作用域可以让我们的变量只在当前代码块中可以访问,对保证变量不会被混乱的复用以及提升代码的可维护性有很大帮助。

例如下面的代码:

if(true){
  var name = 'snail'
}
console.log(name) // snail

可以看到我们在 if 语句中定义了变量 name,如果存在块级作用域的话,变量 name 会在 if 语句执行完成之后被销毁,但是在 JavaScript 中,if 语句的变量声明会将变量添加到当前的执行环境中。这也是循环陷阱的原因所在。

var result = [];
for(var i = 0;i<10;i++){
result[i] = function(){
  console.log(i)
}
}
result[0](); // 10
result[1](); // 10

同时因为 var 声明变量的这种变量提升问题,可能会引起变量的覆盖。

var name = 'snail'
if(true){
  var name = 'running snail'
}
console.log(name) // running snail

上面的示例中可以看到,我们最开始声明的 nameif 语句中的 name 覆盖掉了,这就引起变量的覆盖,数据的丢失等一些列我们不希望的结果。

@crazyyoung1020
Copy link

crazyyoung1020 commented Jan 16, 2022

为什么一定要有块级作用域呢?

  1. 解决变量提升的问题,变量提升会导致一些变量在不被察觉的情况下被覆盖。es6之后推出let和const,限制用他们申明的变量不可变量提升,一定需要先定义后使用,否则会报错。全局作用域下会报 xx is not defined;块级作用域下会报can't acess 'xx' before initialization。
  2. 解决变量污染的问题,如for循环内用var申明的循环下标,会污染循环外部变量,这就是典型的块级内污染了块级外的变量环境。
  3. 大多数主流的语言都支持块级作用域,如java、c、c++等。js作为一门成熟的语言,想要更广泛的应用,那么也需要更加的规范化,所以需要支持块级作用域。

@rhythm022
Copy link

其实真的不需要有块级作用域,至少JS语言的设计者一开始是这么想的,所以他们一开始就没有设计块级作用域。他们觉得只要有函数作用域,全局作用域就够了。

块级作用域的作用和函数级作用域的作用,其实是相同的。我们可以先思考一下,没有函数级作用域会怎么样,只有全局作用域,那简直就不可想象,所有变量都是存在于全局,那么变量的冲突的可能性会变高。

那么,用这个道理其实是套在块级作用域也是一样的。没有块级作用域的话,其实函数里变量名的冲突也会更高,但有了块级作用域之后呢,它能降低这种冲突的可能性,为编程带来方便。

@su37josephxia su37josephxia changed the title 为什么一定要有块级作用域? Day16 - 为什么一定要有块级作用域? Jan 20, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests