• let和const的一些知识点


    let和const

    1. 不可以重复声明
    2. 不会发生变量提升,因此必须在声明之后使用,否则报错!
    3. 只在声明所在的块级作用域内有效

    let

    同一个作用域内不能重复声明同一个变量:

    function func() {
      let a = 10;
      var a = 1;
    }
    func()    // 报错
    
    function func() {
      let a = 10;
      let a = 1;
    }
    func()    // 报错
    function func(arg) {
      let arg;
    }
    func()    // 不能在函数内部重新声明参数!!!!!因为参数等同于在函数内部var声明的一个局部变量
    
    function func(arg) {
      {
        let arg;
      }
    }
    func()    // 该例子不报错,是因为在func里面增加{}等同于又新建了一个作用域,因此{}里的arg不是参数arg

    let可以只声明不赋值,没有值的时候会输出undefined

    console.log(let1)  // let1 is not defined
    let let1;
    console.log(let1)  //  undefined
    let1 = 1;
    console.log(let1)  // 1

    let在for循环中的应用:【下面将给出我对这两个例子的理解】

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

    该demo的执行过程如下:

    var a = [];   // 全局变量

    var i = 0;    // 全局变量,第1次循环

    a[0] = function(){ console.log(i) }   // 此时的i不是0,是因为这里只是声明了这个函数,并没有执行它,即没有创建这个函数的上下文,因此i不会沿着作用域链向外去找i的值

    var i = 1;  // 第2次循环,因为是全局变量,因此i = 1会替换上一个i = 0

    a[1] = function(){ console.log(i) }  // 同a[0],所有该函数的执行的作用域是全局作用域

    var i = 2  // 第3次循环,同理,i = 2会替换上一个i = 1

    ...

    var i = 9;  // 第10次循环,i = 9会替换上一个i = 8

    a[9] = function(){ console.log(i) }

    直到var i = 10,10<10不满足循环条件,则跳出循环,继续向下执行全局作用域下面的语句:a[6](); 

    调用a[6]函数,并创建a[6]函数的函数上下文,执行该函数内部的console.log(i),这个函数中没有i,因此顺着作用域链向外去找i,而此时全局变量var i = 10,因此输出10

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

    第二个demo的执行过程如下:

    let a = [];   // 全局变量

    {  // 第1次循环

      let i = 0;  // let使得for循环变成一个块级作用域,则let i = 0是这个块级作用域下的局部变量

      a[0] = function(){ console.log(i) }   // 注意!!!由于let的声明创建了一个块级作用域,此时的a[0]这个函数就是一个闭包

    }

    {  // 第2次循环 

      let i = 1;   // 注意!!!此时的let i = 1和let i = 0是属于两个不同的块级作用域,因此两者的值并不会相互影响

      a[1] = function(){ console.log(i) }   // 同a[0]

    }

    ...

    {  // 第10次循环

      let i = 9;

      a[9] = function(){ console.log(i) }  // 同样该函数也是一个闭包!

    }

    {

      直到let i = 10,不满足循环条件,跳出循环,注意!!该代码块中不存在闭包因此,let i = 10在这次循环之后代码块随即被销毁

    }

    继续向下执行全局作用域下面的语句:a[6](); 

    调用a[6]函数,进入该块级作用域的代码环境,在该闭包内部找i值,如果没有则顺着作用域链向外去找i,而此时块级作用域内有let i = 6,因此输出6

    此时闭包被调用,所以整个代码块中的变量i和a[6]函数被销毁。

     

    暂时性死区

    在代码块内,凡是用let声明变量之前,该变量都是不可使用的,这在语法上叫做“暂时性死区”(Temporal Dead Zone,简称TDZ)。所以,凡是在声明之前使用该变量,就会报错!

    var tmp = 'ning';
    
    if (true) {
      // TDZ开始
      tmp = 'abc'; // ReferenceError  
      console.log(tmp); // ReferenceError  去掉这两句才能继续向下执行代码并输出响应的结果
    
      let tmp; // TDZ结束
      console.log(tmp); // undefined
    
      tmp = 123;
      console.log(tmp); // 123
    }

    从上面的代码可以看出虽然全局有一个tmp变量,但是在if这个块级作用域下let声明了一个局部变量tmp,导致该局部变量tmp绑定(binding)了这个区域,所以在let声明之前使用它,都属于该tmp的死区,会报错!

    在没有let之前,使用typeof是百分之百不会报错的,因为如果typeof一个未声明的变量,会输出'undefined'

    但是,暂时性死区的出现使得typeof的使用需要小心!因为如果在let声明前typeof该变量则会报错!

    typeof x; // ReferenceError
    let x;

    const

    const声明一个只读的常量。一旦声明其值就不能改变,并且一旦声明变量就必须立即初始化,只声明不赋值就会报错

    console.log(const1)  // Missing initializer in const declaration
    const const1;
    console.log(const1)  // Missing initializer in const declaration

    cosnt也是在块级作用域内有效,例子如下:

            if (true) {
                const MAX = 5;
            }
            console.log(MAX);   // Uncaught ReferenceError: MAX is not defined

    const命令声明的常量不能提升,只能在声明的位置之后使用const,例子如下:

            if (true) {
                console.log(MAX); // Uncaught ReferenceError: MAX is not defined
                const MAX = 5;
            }

    const同样不能重复声明!例子如下:

    function func() {
      const a = 10;
      var a = 1;
    }
    func()    // 报错
    
    function func() {
      const a = 10;
      let a = 1;
    }
    func()    // 报错

    const的本质:

    实际上是保证的是变量指向的内存地址保存的数据不能改动!

    对于基本数据类型而言,内存地址里保存的就是值,所以等同于常量

    而对于引用类型而言(主要是对象和数组),变量指向的内存地址中保存的是 指向实际数据的指针,因此只要该指针是固定不变的,该指针指向的堆内存的数据是否变化const是不在意的!因此,将一个对象声明为常量必须非常小心!!!!例子如下:

            const obj = {};
    
            obj.attr = 'ning';
            console.log(obj.attr)  // 'ning'
    
            obj = { attr: 'li' }
            console.log(obj.attr)  // Uncaught TypeError: Assignment to constant variable.

    常量obj存的是一个地址,指向一个对象,不可变的是这个地址,而对象本身里的内容是可变的!

  • 相关阅读:
    C语言字符串读入函数笔记
    济大路痴
    Super Jumping! Jumping! Jumping!
    SpringMVC中静态资源的处理
    SpringMVC的拦截器讲解
    九、Spring中使用@Value和@PropertySource为属性赋值
    spring中最重要的一些Aware接口
    八、spring生命周期之BeanPostProcessor
    七、spring生命周期之初始化和销毁方法
    六、spring之通过FactoryBean为ioc容器中添加组件
  • 原文地址:https://www.cnblogs.com/ningyn0712/p/11660226.html
Copyright © 2020-2023  润新知