1. let / var


  • 事实上var的设计可以看成JavaScript语言设计上的错误. 但是这种错误多半不能修复和移除, 以为需要向后兼容.
    • 大概十年前, Brendan Eich就决定修复这个问题, 于是他添加了一个新的关键字: let.
    • 我们可以将let看成更完美的var
  • 块级作用域
    • JS中使用var来声明一个变量时, 变量的作用域主要是和函数的定义有关.
    • 针对于其他块定义来说是没有作用域的,比如if/for等,这在我们开发中往往会引起一些问题。


1.1 ES6之前 没有块级作用域的缺陷

变量作用域:变量在上面范围内是可用的
没有块级作用域引起的问题:

  1. {
  2. var name = 'JLE'
  3. console.log(name)
  4. }
  5. console.log(name)
  6. //输出
  7. //JLE
  8. //JLE
  9. var func;
  10. if (true) {
  11. var name = 'WHO'
  12. func = function () {
  13. console.log(name)
  14. }
  15. func()
  16. }
  17. func()
  18. //输出
  19. //WHO
  20. //WHO

上面就是变量 和 if 没有块级作用域的问题,可能会觉得问题不是很大。
下面再来看一个 for 没有块级作用域的问题:

  1. <button type="button">按钮1</button>
  2. <button type="button">按钮2</button>
  3. <button type="button">按钮3</button>
  4. <button type="button">按钮4</button>
  5. <button type="button">按钮5</button>
  6. <script type="text/javascript">
  7. var btns = document.getElementsByTagName('button');
  8. for (var i = 0; i < btns.length; i ++ ) {
  9. btns[i].addEventListener('click', function () {
  10. console.log('第' + i + '个按钮被点击')
  11. })
  12. }
  13. </script>

这段代码本来是点击按钮,控制台输出对应编号的按钮被点击,结果不论点击哪个按钮都是显示 “第5个按钮被点击”

通过for 循环给5个按钮绑定事件监听,由于 i 没有作用域的概念,相当于 i 是全局变量,Java中的 类的static 变量,大家共享一个 i。当 for循环结束到时候, i = 5,所以不论点击哪个按钮都是显示 “第5个按钮被点击”。

for 代码演示,当 i 没有块级作用域时,也就是 变量 i 在全局都是可用的。当 i 改变时,5个事件监听里面的 i 都会改变

  1. var i = 5
  2. {
  3. //0
  4. btns[i].addEventListener('click', function () {
  5. console.log('第' + i + '个按钮被点击')
  6. })
  7. }
  8. {
  9. //1
  10. btns[i].addEventListener('click', function () {
  11. console.log('第' + i + '个按钮被点击')
  12. })
  13. }
  14. {
  15. //2
  16. btns[i].addEventListener('click', function () {
  17. console.log('第' + i + '个按钮被点击')
  18. })
  19. }
  20. {
  21. //3
  22. btns[i].addEventListener('click', function () {
  23. console.log('第' + i + '个按钮被点击')
  24. })
  25. }
  26. {
  27. //4
  28. btns[i].addEventListener('click', function () {
  29. console.log('第' + i + '个按钮被点击')
  30. })
  31. }

在ES5的时候,想要完成上面的操作,由于 变量 是没有块级作用域,但是 函数有块级作用域,所以使用 闭包来完成上述的操作

为什么闭包可以解决问题:函数是一个作用域
通过自增量 i 作为函数的实参 传入到函数中,并绑定函数,由于 函数中有块级作用域,当 i 再发生改变时,函数内部的形参不会跟着改变。

  1. for (var i = 0; i < btns.length; i ++ ) {
  2. (function (num) {
  3. btns[i].addEventListener('click', function () {
  4. console.log('第' + num + '个按钮被点击')
  5. })
  6. })(i)
  7. }

再看一个简单的小栗子:这里给函数传入 参数 name 的是 James,即使外部 name 改为 Kobe,也不会影响函数内部,

  1. var name = 'WHO'
  2. function abc(name) {
  3. console.log(name)
  4. }
  5. name = 'Kobe'
  6. abc('James')

1.2 ES6:let 解决块级作用域问题

在 ES6 新增的 let 为了解决块级作用域的问题: 不需要使用闭包的繁琐写法

  1. const btns = document.getElementsByTagName('button');
  2. for (let i = 0; i < btns.length; i ++ ) {
  3. btns[i].addEventListener('click', function () {
  4. console.log('第' + i + '个按钮被点击')
  5. })
  6. }

for 代码演示如下:每个监听函数都有自己一个独立的 i,当外部的 i 发生改变时,不影响函数内部的 i

  1. let i = 5
  2. {
  3. i = 0
  4. btns[i].addEventListener('click', function () {
  5. console.log('第' + i + '个按钮被点击')
  6. })
  7. }
  8. {
  9. i = 1
  10. btns[i].addEventListener('click', function () {
  11. console.log('第' + i + '个按钮被点击')
  12. })
  13. }
  14. {
  15. i = 2
  16. btns[i].addEventListener('click', function () {
  17. console.log('第' + i + '个按钮被点击')
  18. })
  19. }
  20. {
  21. i = 3
  22. btns[i].addEventListener('click', function () {
  23. console.log('第' + i + '个按钮被点击')
  24. })
  25. }
  26. {
  27. i = 4
  28. btns[i].addEventListener('click', function () {
  29. console.log('第' + i + '个按钮被点击')
  30. })
  31. }

2. const

  • const 关键字

    • 在很多语言中已经存在,比如 C/C++中,主要的作用是将某个变量修饰为常量。
    • 在 JavaScript中也是如此,使用 const修饰的标识符为常量,不可以再次赋值
  • 什么时候使用 const呢?

    • 当我们修饰的标识符不会被再次赋值时,就可以使用 const 来保证数据的安全性
  • 在 ES6开发中,优先使用 const,只有需要改变一个标识符的时候才使用 let

  • const 的注意:
    1. const a = 20;
    2. a = 30; //错误:不可以修改
    1. const name; //错误:const修饰的标识符必须赋值
    常量的含义是指向对象不能修改,但是可以修改对象内部的属性
    **

    3. 对象字面量的增强写法

在 ES6 中,对对象字面量进行了很多增强
属性初始化简写和方法简写:

  1. //1.属性的增强写法
  2. let name = 'JLE';
  3. let age = 18;
  4. let height = 1.8;
  5. //ES6之前写法
  6. const obj1 = {
  7. name: name,
  8. age: age,
  9. height: height
  10. }
  11. console.log(obj1);
  12. //ES6之后
  13. const obj2 = {
  14. name, age, height
  15. }
  16. console.log(obj2)
  1. //2.方法的简写
  2. //ES6之前写法
  3. const obj1 = {
  4. test: function () {
  5. console.log('obj1的test函数')
  6. }
  7. }
  8. obj1.test()
  9. //ES6之后
  10. let obj2 = {
  11. test () {
  12. console.log('obj2的test函数')
  13. }
  14. }
  15. obj2.test()