# var和let与const

# 相同点

  • 声明变量

# 区别

  1. var声明的变量存在变量提升(变量被声明前可用);let和const不存在变量提升(变量声明前使用会抛出错误)

    • 示例 1
    console.log(a) // 输出 undefined
    var a = 10;
    /***********以上代码在被执行时完全等于以下代码***********/
    var a;
    console.log(a);
    a = 10;
    
    1
    2
    3
    4
    5
    6
    • 示例二
    console.log(a); // 输出 undefined
    var b = 2;
    if (b === 2) {
      var a = 10;
    }
    /***********以上代码在被执行时完全等于以下代码***********/
    var a,b = 2;
    console.log(a);
    if (b === 2) {
      a = 10;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    • 示例 3
    console.log(a); // 报错 不再往下执行;注掉此行
    fn1();
    function fn1() {
      console.log(a); // 注掉第一行代码后,输入 undefined
      var a = 10;
    }
    /**********以上代码在被执行时完全等于以下代码(注释第一行console.log后)*********/
    fn1();
    function fn1() {
      var a;
      console.log(a); // 注掉第一行代码后,输入 undefined
      a = 10;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    • 示例 4
    <body>
      <script src="./index.js"></script>
      <script src="./index2.js"></script>
    </body>
    
    1
    2
    3
    4
    // index.js
    console.log(a); // 报错
    // index2.js
    var a = 10
    
    1
    2
    3
    4

    总结: 1. var声明的变量的变量提升不是直接提到最顶层,而是提升到当前变量执行作用域的顶层; 2. 除var存在变量提升外,function同样存在变量提升

    ​ 3. var的变量提升在独立的js文件内提升,不会跨文件提升;

  2. var的作用域是它当前执行的上下文;let和const存在块级作用域,即距离变量最近的花括号内

    • 示例 1
    <body>
      <script src="./index.js"></script>
      <script src="./index2.js"></script>
    </body>
    
    1
    2
    3
    4
    // index.js
    {
      var a = 10; // var 换成const/let后报错
    }
    // index2.js
    console.log(a); // 输出10
    
    1
    2
    3
    4
    5
    6
    • 示例 2 是关于函数/if判断/for循环内的作用域,实际效果和第一点示例3一致
  3. var在相同作用域内可以重复声明变量;let和const重复声明会抛出错误

    • 示例
    var a = 10;
    var a = 20;
    console.log(a); // 输出20
    
    /**************** or ******************/
    let a = 10;
    let a = 20;
    console.log(a); // 报错,不会继续执行
    
    1
    2
    3
    4
    5
    6
    7
    8
  4. let与var声明的变量所指向的内存地址可变(表现为定义的变量的可以被修改);const声明的变量所指向的内存地址不可改变(表现为不可以重新赋值)

    • 示例
    var a = 10; // 假设 10的内存地址 102091092091,a指向就是102091092091 下面同理
    let b = 15; // 假设内存地址为 102091092092
    const c = { a: a, b: b }; // 假设内存地址为 102091092093 内部是 拷贝a赋值a 拷贝 b 赋值给b
    const e = [10, b]; // 假设内存地址为 102091092094
    
    a = 20; // 假设 20的内存地址 102091092095,此时a的指向已经改变了,重新指向了102091092095,102091092091(也就是10)已经没有被引用,此时就被垃圾回收了;
    b = 25; // 同上
    // var/let声明的变量可以修改指向的内存地址
    console.log(a, b, c, e); // 输出 20 25 {a: 10, b: 15} [10, 15]
    c.d = 30; // 此时会在c所指向的内存地址中添加内容,而不会扩展
    e.push(c); // 同上
    console.log(c,e); // 输出 {a: 10, b: 15, d: 30} [10, 15, {a: 10, b: 15, d: 30}]
    c = { a: a, c: c, d: c.d }; // 此时相当于新建对象新建内存,但const声明的变量内存指向不允许更改,所以报错不在执行
    e = [10, 15, 20]; // 同上 但前一行报错之后的内容也不会执行
    console.log(c, e); // 同上
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

# 使用var可能导致的问题

<body>
  <script src="./index.js"></script>
  <script src="./index2.js"></script>
</body>
1
2
3
4
// index.js 假设此文件是一个第三方库
var a = 10;
function fn1() {
  return a + 20;
}
// index2.js 假设此文件是我们自己的业务js
var a = 60;
console.log(fn1()); // 输出 80
1
2
3
4
5
6
7
8

变量覆盖,变量被修改。假设我们在Vue项目没有使用第三方库,仅我们自己的业务js。如果在项目中全部使用var,虽然js写在不同的文件中,但webPack打包后会进行文件整合,假设对于状态的变量都使用了state/status,由于var的变量提升,变量被复写的可能性降大幅提高,对于let也会可能增加变量被修改的可能行,基于此如果引入第三方库对于常用的储存格式化等很基础的语义化单词,被复写修改可能性会更高

# 推荐

基于以上内容,推荐变量使用const > let > var

# 解构赋值

# 数组解构赋值

const list = [1, 2, 3, 4, 8, 9, 10];
const [first,second,,,fifth] = list; // 解构取数组前两个元素和第五个元素
console.log(first, second, fifth); // 1 2 8
1
2
3

# 对象的解构赋值

const obj = {name: '张三', sex: 1, sexDecs: '男', age: 12 };
const { name, sexDecs } = obj;
console.log(name, sexDecs); // 张三 男
1
2
3

# 函数的结构赋值

function fn1 ({userName, userId }) {
  console.log(userName, userId);
}
const user = {userName: '杜少陵', userId: 'dsl'};
fn1(user);
1
2
3
4
5

# 深层次混合嵌套的结构赋值

const user = {
  name: '杜甫',
  anotherName: '杜少陵',
  birthplace: '河南巩县',
  friends: [
    {
      name: '李白',
      dynasty: '唐',
      getOtherInfo: function() {
        return {
          father: '未知',
          mother: '不知',
        }
      },
    },
    {
      {
      name: '元稹',
      dynasty: '唐',
      getOtherInfo: function() {
        return {
          father: '元宽',
          mother: '郑氏',
        }
      },
    }
  ],
  getRelativesInfo: function() {
    return {
      grandfather: '杜审言',
      father: '杜闲',
      mother: '卢氏',
      wife: '司农少卿杨怡之女',
      child: ['宗文', '宗武', '凤儿'],
    }
  }
}
const { name, anotherName, birthplace, getRelativesInfo, friends: [, {dynasty, getOtherInfo}] } = user;
console.log(name, anotherName, birthplace, getRelativesInfo, dynasty, getOtherInfo); // 杜甫 杜少陵 河南巩县 输出函数 唐 输出函数
const {grandfather, father, mother, wife, child: [,,childrenName]} = getRelativesInfo();
console.log(grandfather, father, mother, wife, childrenName); // 杜审言 杜闲 卢氏 司农少卿杨怡之女 凤儿
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

# 解构赋值后重命名

const name = '李白';
const user = {name: '白居易', job: '诗人'};
// 对user进行解构赋值取name,会造成重复声明,此时可以解构后重新指定变量名
const {name: baiName} = user;
console.log(baiName); // 白居易
1
2
3
4
5

# 解构赋值的默认值

有时候进行解构赋值的时候我们并不知道我们所要解构的对象中是否有我们所要的对象,此时就可以指定默认值

  • 对象的默认值
const ChiZhiYang = { name: '柴止痒', age: 26 };
const {name = '未命名', gender = '未知', age = 0, bodyWeight = '0KG', other: {formerResidence} = {formerResidence: '中国'}} = ChiZhiYang;
console.log(name, gender, age, bodyWeight, formerResidence); // 柴止痒 未知 26 0KG 中国
1
2
3
  • 数组的默认值
const programmer = [];
const [firstPeople = '第一个程序猿', secondPeople = '第二个程序猿'] = programmer;
console.log(firstPeople, secondPeople); // 第一个程序猿 第二个程序猿
1
2
3
Last Updated: 4/27/2020, 10:56:04 PM