概述

JS 中作用域有全局作用域、函数作用域,没有块作用域的概念。ES6 中新增了块级作用域,块作用域由 {} 包括,if 语句和 for 语句里面的 {} 也属于块作用域

区别:

  • var 有变量声明提前,声名的是函数作用域,可以重复声明
  • let 没有变量声明提前,声名的是块作用域,不能重复声明
  • const 没有变量声明提前,声名的是块作用域。声名时必须初始化,并且不能修改,如果声名的是引用类型,可以修改引用类型的属性。不能重复声明

var

使用 var 关键字声明的变量会在所有代码执行之前被声明,但是未赋值(声明提前)

  1. console.log(a); // undefined
  2. var a = 1;
  3. console.log(a); // 1
  4. // 相当于
  5. var a;
  6. console.log(a);
  7. a = 1;

如果不初始化会输出 undefined,不会报错

定义变量后的值是可以修改的,也可以重复声明

  1. var a = 1;
  2. a = 2;
  3. console.log(a); // 2
  4. var a = 3;
  5. console.log(a); // 3

var 定义的变量是函数作用域,可以跨块访问,不能跨函数访问

  1. for (var i = 0; i <= 10; i++) {
  2. var sum = 0;
  3. sum += i;
  4. }
  5. console.log(sum); // 10
  6. function test () {
  7. var a = 1;
  8. console.log(a);
  9. }
  10. test() // 1
  11. console.log(a); // err: a is not defined

let

let 关键字定义的变量没有声名提前,会报错

console.log(a); // err: Cannot access 'a' before initialization
let a = 1;

let 不能重复声明,会报错

let a = 1;
let a = 2;

err: Identifier 'a' has already been declared

let 定义的变量,只能在块作用域中访问,不能跨块访问,也不能跨函数访问

{ var a = 1; }

console.log(a); // err: a is not defined

function test () {
  var b = 2;
  console.log(b);
}

test() // 2
console.log(b); // err: b is not defined

不过使用 let 关键字并不影响作用域链

{
     let name = '张三'
  function fn () {
       console.log(name) 
  }

  fn() // 张三
}

const

const 和 let 一样也没有声明提前

console.log(a);
const a = 1;

err: Cannot access 'a' before initialization

const 用来定义常量,使用时必须初始化(即必须赋值)

const a;
// err: Missing initializer in const declaration

const 一旦定义无法修改

const b = 3;
b = 4;

// err: Assignment to constant variable.

const 和 let 一样也不能重新声明

const a = 1;
const a = 2;

// err: Identifier 'a' has already been declared

const 定义的是块级作用域

{ const b = 1 }

console.log(b); // err: b is not defined

function test () {
  const c = 2;
  console.log(c);
}

test() // 2
console.log(c); // err: c is not defined

但是当 const 定义的是一个引用对象时,这个引用对象的属性是可以修改的

const person = {
  name : 'zs'
}

person.name = 'ls';
console.log(person.name); // ls

因为引用对象是存放在堆内存中的,person 保存的只是这个对象的引用(指针),当修改对象的属性时,并没有改变引用

注意,当不使用关键词定义的是全局变量

{ a = 1; }
console.log(a); // 1

function test() {
  b = 2;
  console.log(b);
}

test(); // 2
console.log(b); // 2