# js变量详解

JavaScript 变量是松散类型的,而且变量不过就是特定时间点一个特定值的名称而已。由于没有规则定义变量必须包含什么数据类型,变量的值和数据类型在脚本生命期内可以改变。

有 3 个关键字可以声明变量:var、const 和 let。其中,var 在 ES 的所有版本中都可以使用,而 const 和 let 只能在 ES6 及更晚的版本中使用。

# 1、var 关键字

要定义变量,可以使用 var 操作符(注意 var 是一个关键字),后跟变量名(即标识符,如前所述):

var message;

不初始化的情况下,变量会保存一个特殊值 undefined。

  1. var 声明作用域

使用 var 操作符定义的变量会成为包含它的函数的局部变量。省略 var 操作符,可以创建一个全局变量。

function test() { 
 var message = "hi"; // 局部变量
 name = "xiaoming"; // 全局变量
} 
test(); 
console.log(message); // 出错!
console.log(name); // "xiaoming"
1
2
3
4
5
6
7

使用关键字声明的称为显式声明,未使用任何关键字声明的称为隐式声明

  1. var 声明提升

var 声明的变量会自动提升到函数作用域顶部:

function foo() {
  console.log(age);
  var age = 26;
}
foo(); // undefined
1
2
3
4
5

# 2、let 声明

let 跟 var 的作用差不多,但有着非常重要的区别。最明显的区别是,let 声明的范围是块作用域,而 var 声明的范围是函数作用域。

if (true) { 
 var name = 'Matt'; 
 console.log(name); // Matt 
} 
console.log(name); // Matt

if (true) { 
 let age = 26; 
 console.log(age); // 26 
} 
console.log(age); // ReferenceError: age 没有定义
1
2
3
4
5
6
7
8
9
10
11

let 也不允许同一个块作用域中出现冗余声明。

  1. 暂时性死区

let 与 var 的另一个重要的区别,就是 let 声明的变量不会在作用域中被提升。

// name 会被提升
console.log(name); // undefined 
var name = 'Matt'; 
// age 不会被提升
console.log(age); // ReferenceError:age 没有定义
let age = 26; 
1
2
3
4
5
6

在解析代码时,JavaScript 引擎也会注意出现在块后面的 let 声明,只不过在此之前不能以任何方式来引用未声明的变量。在 let 声明之前的执行瞬间被称为“暂时性死区”(temporal dead zone),在此阶段引用任何后面才声明的变量都会抛出 ReferenceError

  1. 全局声明

与 var 关键字不同,使用 let 在全局作用域中声明的变量不会成为 window 对象的属性(var 声明的变量则会)

var name = 'Matt'; 
console.log(window.name); // 'Matt' 
let age = 26; 
console.log(window.age); // undefined
1
2
3
4
  1. 条件声明
<script> 
 var name = 'Nicholas'; 
 let age = 26; 
</script> 
<script> 
 // 假设脚本不确定页面中是否已经声明了同名变量
 // 那它可以假设还没有声明过
 var name = 'Matt'; 
 // 这里没问题,因为可以被作为一个提升声明来处理
 // 不需要检查之前是否声明过同名变量
 let age = 36; 
 // 如果 age 之前声明过,这里会报错
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
  1. for 循环中的 let 声明

在 let 出现之前,for 循环定义的迭代变量会渗透到循环体外部:

for (var i = 0; i < 5; ++i) { 
 // 循环逻辑 
} 
console.log(i); // 5
1
2
3
4

改成使用 let 之后,这个问题就消失了,因为迭代变量的作用域仅限于 for 循环块内部:

for (let i = 0; i < 5; ++i) { 
 // 循环逻辑
} 
console.log(i); // ReferenceError: i 没有定义
1
2
3
4

# 3、const 声明

const 的行为与 let 基本相同,唯一一个重要的区别是用它声明变量时必须同时初始化变量,且尝试修改 const 声明的变量会导致运行时错误。

const age = 26; 
age = 36; // TypeError: 给常量赋值

// const 也不允许重复声明
const name = 'Matt'; 
const name = 'Nicholas'; // SyntaxError 

// const 声明的作用域也是块
const name = 'Matt'; 
if (true) { 
 const name = 'Nicholas'; 
} 
console.log(name); // Matt

1
2
3
4
5
6
7
8
9
10
11
12
13
14

const 声明的限制只适用于它指向的变量的引用。换句话说,如果 const 变量引用的是一个对象,那么修改这个对象内部的属性并不违反 const 的限制。

const person = {}; 
person.name = 'Matt'; // ok
1
2

const 在 for 循环中的示例:

for (const i = 0; i < 10; ++i) {} // TypeError:给常量赋值

// 如果你只想用 const 声明一个不会被修改的 for 循环变量,那也是可以的。
let i = 0; 
for (const j = 7; i < 5; ++i) { 
 console.log(j); 
} 
// 7, 7, 7, 7, 7

// 这对 for-of 和 for-in 循环特别有意义:
for (const key in {a: 1, b: 2}) { 
 console.log(key); 
} 
// a, b 
for (const value of [1,2,3,4,5]) { 
 console.log(value); 
} 
// 1, 2, 3, 4, 5 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 4、声明风格及最佳实践

ES6 增加 let 和 const 从客观上为这门语言更精确地声明作用域和语义提供了更好的支持。

  1. 不使用 var 有了 let 和 const,大多数开发者会发现自己不再需要 var 了。限制自己只使用 let 和 const 有助于提升代码质量,因为变量有了明确的作用域、声明位置,以及不变的值。

  2. const 优先,let 次之使用 const 声明可以让浏览器运行时强制保持变量不变,也可以让静态代码分析工具提前发现不合 法的赋值操作。因此,很多开发者认为应该优先使用 const 来声明变量,只在提前知道未来会有修改时,再使用 let。这样可以让开发者更有信心地推断某些变量的值永远不会变,同时也能迅速发现因意外赋值导致的非预期行为

# 5、总结

  1. 3 个关键字可以声明变量:varconstlet

  2. 定义变量:操作符 + 变量名(标识符)

  3. 使用关键字声明的称为显式声明,未使用任何关键字声明的称为隐式声明

  4. var 声明的特点:

    • 可以省略关键字自动变为全局变量
    • 可以重复声明
    • 可以先访问后声明(声明提升)
    • 声明范围是函数作用域
    • 只声明不初始化则值为 undefined
  5. let 声明的特点:

    • 未声明不可使用,会报错
    • 不可以重复声明
    • 声明范围是块作用域
  6. const 声明的特点:

    • 包含 let 所以特点
    • 声明变量时必须同时初始化变量
    • 值类型不可修改,引用类型不可修改引用地址
  7. 共同点:都可以声明为全局作用域

上次更新: 4/10/2022, 10:26:50 AM