var
声明方式
声明一个变量,并赋值。
'var age = 23;
声明多个变量并赋值,逗号分隔。
var age = 23, name = 'admin';
弱语言类型,变量类型随时可以改变
var a = 22; // Number类型
a = 'name''; // String类型
作用域
var
为函数作用域
function fun(){
var msg = 'hi'; //msg 的作用域只在fun内,无法在函数外访问
}
fun();
console.log(msg); // Uncaught ReferenceError: msg is not defined
如果不写var
,直接赋值,则msg
会变成全局对象window
的属性。
function fun(){
msg = 'hi';
}
fun();
console.log(msg); // hi
console.log(window.msg); // hi
声明提升
先看下面例子
console.log(msg); //输出 undefined
var msg = 'hi';
为什么msg
可以在声明之前输出?
因为var声明提升,实际解析过程如下:
var msg; //msg的声明会提升到作用域顶部,此时声明未赋值,所以值为:undefined
console.log(msg);
msg = 'hi';
所以多个同变量var
会合并为一个声明
console.log(msg);
var msg = 'hi';
var msg = '你好';
实际执行为
var msg;
console.log(msg);
msg = 'hi';
msg = '你好';
let
其余方面和var
一样,记一下不一样的点。
let
为块作用域
{
let msg = 'hi'; // msg只在{}内有效
}
console.log(msg); // msg is not defined
let
绕不开的经典问题 for循环 setTimeout
for(var i=0;i<5;i++){
setTimeout(()=>console.log(i),1000);
}
//最后输出 : 5 5 5 5 5
因为5次log
的i
是同一个变量,循环结束后,i = 5
,所以输出了5个5。
用 let
可以完美解决这个问题。
for(let i=0;i<5;i++){
setTimeout(()=>console.log(i),1000);
}
// 输出:0 1 2 3 4
实际上执行内容可以理解为下面的代码
for(let i=0;i<5;i++){
let j = i; //每次循环的 j 并不是同一个变量,而是每次都创建了一个块级作用域的新局部变量。
setTimeout(()=>console.log(j),1000);
}
暂时性死区
let
声明不会提升,如下写法是错误的,必须先声明在再使用。
console.log(msg);
let msg = 'hi'; // msg is not defined
此外,let
声明的变量作用域内,变量声明之前的的这段被称为暂时性死区
,不可以使用变量。
看下面代码
let age = 2;
{
age = 5;
}
代码可以正常运行,{}
内使用的age
变量是外层的age
变量。
但是如果我这么写
let age = 2;
{
age = 5;
let age = 5; //ReferenceError: Cannot access 'age' before initializationidentifier 'age'
}
说明其实括号内的let
声明也是”提升“
了,JavaScript
引擎解析到age = 5
之前已经知道了let age
,但是没有被初始化
,所以不能使用。
全局声明
let
声明的全局变量不会成为window
的属性。
const
和let
其余特征都一样,除了一点:
const
声明的变量必须在声明的时候赋值,并且不可以再次改变值。
const age = 3;
age = 55; //Identifier 'age' has already been declared
注意,不能改变的是const
变量的自身值,如果const
变量的值是一个对象的引用,那么只要对象的引用不变,对象的属性可以改变,因为属性改变,不影响对象的引用,也就是对象的指针,或者说内存地址。
const obj = {};
obj.age = 33;
obj.name = '海森堡';
obj保存的是对象指针。
总结
建议:优先使用const
,剩余使用let
,不使用var
。