let-const-块级作用域的学习

298 阅读3分钟

let-const-块级作用域的学习

let-const基本使用

const age = 18 // 不能修改
// 修改会报错: TypeError: Assignment to constant variable.

// 1.const保存的是一个引用类型(内存地址),不能修改引用类型.
// 可以通过引用找到对应的对象,去修改对象内部的属性值.
const info = {
  mes: 'hello world!'
}
// 这个是允许的
info.mes = 'hello Fhup!'
console.log(info)

// 2.let/const定义的变量名不可以重复定义
// var aaa = '123'
// var aaa = '456'
// console.log(aaa) // 456

let bbb = '123'
// SyntaxError: Identifier 'bbb' has already been declared
// let bbb = '456'
console.log(bbb);

let-const作用域提升

// var有作用域提升
// console.log(foo) // undefined
// var foo = '123' // 源代码解析时 GO { foo: undefined }

/**
 * ReferenceError: Cannot access 'bar' before initialization, 不能在初始化之前访问 bar
 * ECMA262新规范中解释,在执行上下文时bar已经被创建出来,但是不能提前被访问
 * 所以let/const 不能称之为作用域提升
 */
console.log(bar)
let bar = '123'

let-const执行过程

// VO/AO/GO -> 早期的标准
var age = 18
console.log(window.age);
window.toy = '枪'
console.log(toy)

// 最新的ECMA规范: VE代替VO, VE 指向 variables_:VariableMap(HashMap->HashTable)
// 自己理解:  VE(相当于VO) 指向 variables_(相当于GO), 新规范内部实现已经慢慢脱离window(已经不是同一个对象),但为了兼容而存在.最新规范已经将let/const的变量保存在variables_里面,在执行上下文时bar已经被创建出来,但是不能提前被访问 所以我认为: let/const没有进行作用域提升,但是会在解析阶段被创建出来.

ES5块级作用域

// 声明对象的字面量
var obj = {
  // 键值对
  name: 'Fhup'
}

// ES5中没有块级作用域, var声明的类型是无效的,可以访问
// 块代码(block code)
{
  // 里面写表达式, 可声明变量...
  var foo = '123'
}
console.log(foo) // 123

// ES5只有二个东西形成作用域: 1.全局作用域 2.函数作用域

ES6块级作用域

// ES6的代码有块级作用域
// let/const/function/class有作用域的,外部不能访问
{
  let foo = 'foo'
  function bar() {
    console.log('bar');
  }
  class Person {}
}
// console.log(foo) // ReferenceError: foo is not defined

// 函数可以访问,因为浏览器为了兼容以前的代码.让function失去块级作用域,(不同浏览器不同实现)
// 只支持ES6的浏览器,它就会报错
// bar()

let p = new Person()  // ReferenceError: Person is not defined

if-switch-for块级代码

{
  // 块级作用域中的var外部可访问,let外部不可访问
}

// let声明的变量只属于if语句内部使用
// if语句的代码就是块级作用域
// if(true) {
//   var bar = 'bar'
//   let foo = 'foo'
// }

// console.log(bar) // bar
// console.log(foo) // ReferenceError: foo is not defined


// switch语句的代码也是块级作用域
// var color = 'red'
// switch(color){
//   case 'red':
//     var xxx = 'xxx'
//     let yyy = 'yyy'
//     break;
//   default:
//     console.log('这是无色!');
//       break;
// }

// console.log(xxx) // xxx
// console.log(yyy) // ReferenceError: yyy is not defined

// for语句的代码也是有块级作用域
// for(var i = 0; i < 1; i++) {
//   var aaa = 'aaa'
//   let bbb = 'bbb'
// }
// console.log(i) // 1
// console.log(aaa) // aaa
// console.log(bbb) // ReferenceError: bbb is not defined

// let声明的变量只属于for循环的内部使用,外部不能访问
// for(let x = 0; x < 1; x++) {} 
// console.log(x) // ReferenceError: x is not defined 

块级作用域的应用场景

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <button>1</button>
    <button>2</button>
    <button>3</button>
    <button>4</button>
    <script>
      var btns = document.querySelectorAll("button");
      // for语句的代码有块级作用域
      // btns[i] 找 i 时,var没有作用域,找到全局, i = 4
      // 解决方案:
      // 使用let,形成块级作用域.所以i每次绑定为 0,1,2,3
      // 使用闭包()()
      for (var i = 0; i < btns.length; i++) {
        btns[i].addEventListener("click", () => {
          console.log("按钮" + i);
        });
      }
      console.log(i);
    </script>
  </body>
</html>

块级作用域的补充

const names = ['aaa', 'bbb', 'ccc']
// for(let i=0; i< names.length; i++) {
//   console.log(names[i]);
// }

// 过程:
// 有++的操作,不可以使用const
// {
//   let i = 0
//   console.log(names[i]);
// }
// {
//   // 拿到上次的i,进行++的结果赋值给i
//   let i = 1
//   console.log(names[i]);
// }
// {
//   let i = 2
//   console.log(names[i]);
// }

// for...of 遍历数组(对象),没有++的操作,可以使用const
for(const item of names) {
  console.log(item);
}
// console.log(item) // 使用var时,item的值为ccc

{
  const item = 'aaa'
  console.log(item);
}
{
  const item = 'bbb'
  console.log(item);
}
{
  const item = 'ccc'
  console.log(item);
}

暂时性死区

/**
 * 暂时性死区:
 * 在一个代码块中,使用let/const声明的变量,在声明之前,变量都是不可以访问的.
 */


var obj = 'obj'

// {}的内部区域称之为: 暂时性死区
if(true) {
  console.log(obj) // ReferenceError: Cannot access 'obj' before initialization
  let obj = 'xxx'
}


// 在开发中,多使用 const