ECMAScript 的变量是松散类型的,所谓松散类型就是可以用来保存任何类型的数据。
声明变量
声明变量使用 var
操作符(var
是一个关键字),后跟变量名(标识符)。var
操作符定义的变量将成为定义该变量的作用域中的局部变量,关于作用域后续我们再介绍。
var message;
在ES6里,声明变量还可以用let和const。
let message;
const valuea = 10;
在同一作用域内使用var
声明的变量重复声明不会报错,视后面的声明无意义(声明后有赋值的话会赋值成功),当一个变量用到let
或const
声明的时候(不管let
或const
是第一次或是后面出现在声明),就不允许重复声明了。
var a = 1
var a = 'tomato'
// tomato
console.log(a);
var a = 1
let a = 'tomato'
//SyntaxError: Identifier 'a' has already been declared
console.log(a);
另外,如果一个变量省略了声明操作符var
(let
、const
),将会创建一个全局变量(应该是全局对象的属性)
var msg = 'hi'
function test(){
message = 'hello'
}
//{value: "hi", writable: true, enumerable: true, configurable: false}
Object.getOwnPropertyDescriptor(window,'msg')
//{value: "hello", writable: true, enumerable: true, configurable: true}
Object.getOwnPropertyDescriptor(window,'message')
//hello
console.log(message)
//false
delete window.msg
//true
delete window.message
没有省略var变成全局属性的变量configurable
特性为true
,表示此属性可被删除,特性可配置,所以能被delete删除。
给未经声明的变量赋值在严格模式下会导致抛出 ReferenceError
错误。
可以在一行定义多个变量,用逗号(,
)隔开
var a = 'hi',b = 'peter';
var声明的变量在当前执行环境中有交,ES6中使用let
、const
具有块级作用域,及在当前代码块中有效(使用{}
括起来的就是一个代码块)。
{
let a = 10;
}
//ReferenceError: a is not defined
console.log(a)
使用var声明变量不受条件语句限制,let、const则表现为块级特性:
if(true){
var a = 10;
}
//10
console.log(a)
if(true){
let a = 10;
}
//ReferenceError: a is not defined
console.log(a)
var
命令和function
命令声明的全局变量,依旧是顶层对象的属性;另一方面规定,let
命令、const
命令、class
命令声明的全局变量,不属于顶层对象的属性。也就是说,从 ES6 开始,全局变量将逐步与顶层对象的属性脱钩。
ES5 声明变量只有两种方法:
var
命令和function
命令。ES6 除了添加let
和const
命令,还有import
命令和class
命令。
变量命名
前面说到JavaScript 是一种区分大小写的语言。这意味着,变量名 myCounter 与变量名MYCounter 不同。变量名可以是任意长度。变量是一个标识符,所以必须满足标识的命名规则:
- 第一个字符必须是一个字母、下划线(_)或一个美元符号($);
- 其他字符可以是字母、下划线、美元符号或数字。
- 不能是关键字或保留字
变量赋值
前面说到 ECMAScript 的变量是松散类型的,一个变量可以赋值为任意类型的值,用=
为一个变量赋值。
var a;
var b = 1;
var b = [1,2,3];
上面代码中,变量a只声明,没有赋值,默认为undefined
,变量b赋值为数字1,随后又声明(没用到let、const的变量再次声明无效,如用到let、const声明的变量再次声明则会报错)a并赋值为数组类型值。
变量提升
一个变量的声明与赋值有时随写在一起,但解释器并不是完成声明后就为变量赋值的。
JavaScript 引擎的工作方式是,先解析代码,获取所有被声明的变量(获取的变量此时的值是undefined
),然后再一行一行地运行。这造成的结果,就是所有的变量的声明语句,都会被提升到代码的头部,这就叫做变量提升
(更准确的说是变量声明提升)。
//undefined
console.log(a);
var a = 10
//10
text(a);
//10
console.log(a)
function text(a){
console.log(a)
}
JavaScript 引擎在处理上面代码的时候,先整体解析一遍,把声明的变量(也包括函数声明)提升到代码的顶端。
实际运行的效果如下:
var a;
function text(a){
console.log(a)
}
//undefined
console.log(a);
a = 10
//10
text(a);
//10
console.log(a)
函数声明的提升是把整个函数提升,函数表达式只提升变量名:
//hi
b();
function b(){
console.log('hi')
}
//TypeError: a is not a function
a();
var a = function(){
console.log('hello world')
};
上面第一段代码,函数声明b整体提升到了顶部,然后再运行 b(),类似如下执行过程
function b(){
console.log('hi')
}
//hi
b();
第二段代码则是如下执行过程
var a;
a();
a = function(){
console.log('hello world')
}
ES6中,使用let
和const
声明的变量不存在变量提升,所以必须在声明后使用。