都9102年了,是时候成为这条gai里最靓的仔了,ES6 之 let AND const(一)

483 阅读5分钟

你是否还在为代码不优雅而烦恼?
你是否还在为长长的代码而半夜惊醒?
你是否还在为怎么成为最靓的仔而烦恼?

没关系

从今天起,让我们拥抱ES6,成为最靓的仔吧~

第一次发文,可能写的不是很 NICE !!
欢迎大家来一起讨论~文中如有错误欢迎指出~~


好了,闲话不多说,进入正题吧!!
今天要讲的是我们的新朋友 letconst 但是在介绍新朋友之前,先来讲讲我们的老朋友 var

关于 var 的特点相信大家都已经非常的熟悉了,那我就将他们都罗力出来吧

  1. 变量提升
  2. 可以重复声明
  3. 可以重复赋值
  4. 没有块级作用域
  5. 在全局作用域下声明的变量,会成为顶层对象(window)的属性

那么我们也将围绕着这几点来讲解 letconst

1、变量提升

letconst 都没有变量提升,也就是常说的暂时性死区,必须先声明变量,然后才能访问,否则会报错,例如:

    // 以 let 为例
    // BAD
    console.log(a);  // Uncaught ReferenceError: Cannot access 'a' before initialization
    let a = 1;
    
    // GOOD
    let a = 1;
    console.log(a);  // 1

2、重复声明

letconst 都不支持同一个变量重复声明,否则会报错,例如:

    // 以 let 为例
    var a = 1let a = 1;  // Uncaught SyntaxError: Identifier 'a' has already been declared

3、重复赋值

letvar 都支持给一个变量重复赋值,而 const 顾名思义,声明的是一个常量,一旦声明就不可修改了,否则会报错,例如:

    const a = 1;
    a = 2;  // Uncaught TypeError: Assignment to constant variable

这里还需要注意两点:

  1. const 在声明常量的时候,需要赋值,否则也会报错,例如:
    // BAD
    const a;  // Uncaught SyntaxError: Missing initializer in const declaration
    
    // GOOD
    const a = 1;

  1. const 实际上并不是保证变量的值不可修改,而是变量所指向的那个内存地址所保存的数据不可修改。

如果是基本数据额类型的话,是指向栈内存里的数据
如果是引用数据类型的话,指向的是保存在栈内存里指向堆内存的一个指针
所以指针指向的内存地址是不可修改的,否则会报错,例如:

    const obj = {};
    obj = {a: 1};   // index.html:86 Uncaught TypeError: Assignment to constant variable

但是对象内部的属性还是可以进行修改的,例如:

    const obj = {
        a:1
    }
    
    obj.a = 2;
    console.log(obj.a);  // 2

如果想让对象内部的属性也不能进行修改的话,可以使使用Object.freeze()方法,将对象冻结,例如:

    const obj = Object.freeze({});
    obj.a = 123;
    console.log(obj); // {}

上面的代码中,对象 obj 指向的是一个冻结的对象,所以添加属性是不起作用的,如果要将对象彻底冻结,还是不够的,需要用到以下方法:

    var constantize = (obj) => {
      Object.freeze(obj);
      Object.keys(obj).forEach( (key) => {
        if ( typeof obj[key] === 'object' ) {
          constantize( obj[key] );
        }
      });
    };

4、块级作用域

letconstvar 不同的一点就是,它们不仅有全局作用域和函数作用域,还有块级作用域,例如:

    (() => {
        let n = 1;
        if (true) {
            let n = 2;
        }
        console.log(n);  // 1
    })();

以上代码中,我们在代码中用 let 命令对变量 n 进行了重复声明,但是输出的结果却是1,这表明外层代码块不受内层代码块的影响,所处的作用域不同,如果两次改用 var 声明变量 n 的话,输出的肯定是 2


5、在全局作用域下声明变量

我们的老朋友 var 命令在全局作用域下声明变量时,声明的变量会成为到顶层对象(window)的属性,而 letconst命令在全局作用域下声明的变量,不属于顶层对象(window)的属性,例如:

    var a = 1;
    let b = 1;
    const c = 1;
    
    console.log(window.a);  // 1
    console.log(window.b);  // undefined
    console.log(window.c);  // undefined

到这里,那么关于 letconst的基本特性就已经讲的差不多了,如果遗漏或者有错误,欢迎指出,向各位大佬看齐~


6、应用场景

1、 let 简单的一些应用场景
最常见的就是for循环,例如:

    for (let i = 0, i < 5, i++) {
        console.log(i);  // 0 1 2 3 4
    }
    console.log(i); // Uncaught ReferenceError: i is not defined

以上代码在全局打印变量 i 的时候,提示没有定义,说明在for 循环里面定义的变量 i 不会泄露到全局变量中


还有一种应用场景,在 for 循环里进行异步操作,例如:

    for (let i = 0; i < 5; i++) {
        setTimeout(() => console.log(i), 5000); // 0 1 2 3 4
    }

以上代码中,变量 ilet 声明的,而且 i 只在本轮循环中有效, 也就是说每次循环都会产生一个新的代码块(块级作用域)和新的变量 i ,并且 javascript 内部引擎会记住上一轮循环的值,初始化本轮变量 i 时,就会在上一轮循环的基础上进行计算


如果想用 var 实现上面相同的结果的话,可以使用我们的老朋友闭包,例如:

    for (var i = 0; i < 5; i++) {
        ((i) => setTimeout(() => console.log(i), 5000))(i); // 0 1 2 3 4
    }

顺便补充一点,for 循环,循环变量那部分是一个父作用域,而循环体部分是一个子作用域,例如:

    for (let i = 0; i < 2; i++) {
        let i = 'a';
        console.log(i);  // a a
    }

2、const 简单的一些应用场景
const 的应用场景也非常的广,一般定义一些不会更改数据,如常量、引入的数据和模块,或者请求的返回的数据等等,只要数据是不变更的,我们默认就用 const,如下:

    const fs = require("fs");
    const ERR_CODE = 1;

简单梳理一下大概的内容

命令 let const var
变量提升
重复声明
重复赋值
块级作用域
全局声明变量会成为顶层对象的属性

好了,恭喜你,在成为最靓的仔的路上又前进了一步 ヾ(◍°∇°◍)ノ゙
欢迎补充和指出错误,也欢迎小手手~