变量

119 阅读4分钟

在js中有三种声明变量的方式,分别为var,let,const。那么它们三个有什么区别呢?

var关键字

要定义变量,可以使用var操作符(注意,var是一个关键字)
例如:

var message
console.log(message)

就是定义了一个变量message,因为没有赋值,所以它的值默认为undefined。
我们来运行一下,因为输出为undefined,所以message确实是undefined。

image.png

var声明作用域

在函数内部也是可以创建全局变量的 正常情况下

function msg() {
    var message = '123'
}
msg()
console.log(message)

输出,因为在全局找不到message

image.png
如果在函数内部不写var声明变量

function msg() {
     message = '123'
}
msg()
console.log(message)

因为去掉var关键字之后,message就变成了全局变量,所以只要调用一次函数就能访问到

image.png

虽然可以省略var操作符定义全局变量,但是不推荐这么做。

如果想同时定义多个变量,,可以用逗号隔开

var message = 'hi',
    found = false,
    age = 29

变量提升

如果我们写下这段代码

function foo() {
     console.log(message)
}
foo()

会报这样的错误

image.png
可是,当我们输入这段代码时

function foo() {
    console.log(message)
    var message = 'hi'
}
foo()

输出

image.png
它等同于这段代码

function foo() {
    var message
    console.log(message)
    message = 'hi'
}
foo()

这就是所谓的变量提升,也是把所有的变量声明都拉到顶部

let声明

let和var差不多,但还是有一些非常重要的区别。最明显的区别就是let声明块作用域,而var声明函数作用域

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

输出为

image.png
而用let声明

if(true){
    let age = 'name';
    console.log(age);
}
console.log(age);

输出

image.png
因为let声明的是块级作用域,所以只在if里面有效,所以外面找不到age。

let还有一个特点,就是不能重复声明一个变量

var age = 1;
var age = 2;
console.log(age);

输出为

image.png

let age = 1;
let age = 2;
console.log(age);

输出为,标识符age已经被声明过了

image.png
但如果两个let在不同的块作用域就不一样了。

let age = 1;
if(true){
    let age = 2;
    console.log('内',age);
}
console.log('外',age);

输出为

image.png
用let还可以在实际开发中防止声明冗余

let age = 1;
var age = 2;

报错

image.png

var age = 1;
let age = 2;

也同样报错

image.png

1.暂时性死区

这个和变量提升有关,比如用var

console.log(age);
var age = 1;

相当于

var age
console.log(age);
age = 1;

输出undefined

image.png
可如果是let

console.log(age);
let age = 1;

直接报错

image.png

2.全局声明

var声明变量会成为window的属性,比如

var age = 1;
console.log(window.age);

输出为1

image.png
如果是let声明

let age = 1;
console.log(window.age);

就是undefined

image.png

3.条件声明

var声明变量会提升变量,在作用域顶部合并为一个声明,但是let作用域是块,所以不可能检查前面let是否声明过同一个变量。比如下面的代码就不会报错

<script>
    let age = 1;
    var name = 'name'
</script>
<script>
    var name = 2
</script>

而一旦用let在下面新声明一个age

<script>
    let age = 1;
    var name = 'name'
</script>
<script>
    var name = 2
    let age = 2
</script>

就会报错

image.png

4.for循环中的let声明

在let出现之前,var会渗透到外面

for(var i = 0; i < 5; i++){}
console.log(i);

输出为

image.png
这样很容易造成变量污染,而用let则不会出现这种情况

for(let i = 0; i < 5; i++){}
console.log(i);

image.png
还有一个区别,就是如果for循环内部有异步的时候

for(var i = 0; i < 5; i++){
    setTimeout(() => {
        console.log(i);
    }, 0);
}

你以为会输出0、1、2、3、4
实际上会输出5、5、5、5、5

image.png
而用let

for(let i = 0; i < 5; i++){
    setTimeout(() => {
        console.log(i);
    }, 0);
}

输出为

image.png
这种适合所有的for循环,包括for-in和for-of
for-in和for-of我后面会具体介绍

3.const声明

const和let差不多,只不过const声明的是常量,就是声明后不可改变(除了对象和数组,这个后面说)

const age = 35;
age = 26

这样会报错,因为给常量age赋值

image.png
也不允许重复声明

const age = 35;
const age = 26

image.png
声明作用域也是块

const age = 35;
if(true){
    const age = 26
    console.log('内',age);
}
console.log('外',age);

image.png
但如果声明的是对象

const age = {};
age.name = '你好'

这样就不会报错,因为const声明的限制只适用于它指向的变量的引用。换句话说,就是const指向的是一个对象,那么修改对象内部不违反const限制;数组同理

关于for-in和for-of

对于数组来说,for-in获取的是下标,for-of获取的是内容

const age = ['你','我','他','它'];
for(var i in age){
    console.log('for-in',i);
}

for(var i of name){
    console.log('for-of',i);
}

输出为

image.png
如果变量是对象,for-in

const age = {
    name: '123',
    age: 46,
    title: '789'
};
for(var i in age){
    console.log('for-in',i);
}

输出为

image.png
for-of

const age = {
    name: '123',
    age: 46,
    title: '789'
};
for(var i of age){
    console.log('for-of',i);
}

报错

image.png