JS 中的变量

276 阅读4分钟

到目前我们声明变量有3种方式varletconst,下面我列了一张他们之间的区别表。

关键字变量提升变量作用域重复声明全局声明
vartrue函数作用域truetrue
letfalse块级作用域falsefalse
constfalse块级作用域falsefalse

变量提升

JS 引擎在解析函数的时候会先将函数内部的所有 var关键字声明的变量提升到函数作用域的顶部,letconst关键字声明的变量则不会。

function person() {
    console.log(age); // 可以访问到age,因为它被提升到作用域的顶部了,只不过是 undefined。
    console.log(height); // ReferenceError:初始化前无法访问 "height"
    console.log(sex) // ReferenceError:初始化前无法访问 "sex"
    var age = 10;
    let height = 155;
    const sex = 1;
}

变量作用域

在 JS 中 作用域有 3 种,全局作用域、函数作用域、块级作用域

其实还有一个特殊的原型链作用域

全局作用域表示声明在全局下的,即在任何地方都能访问到这个变量。

函数作用域表示声明在函数内部的变量,只能在函数内部访问得到,当函数退出时变量会随之被销毁。

块级作用域表示在一段代码块内声明的变量,只能在当前代码块内访问,如 iffortry catch 块。

块级作用域

使用 ES6 关键字,如 letconst 声明的变量是具有块级作用域的,而 var 关键字声明的变量会被提升到父级作用域。

if(true) {
    // 这里块级是作用域
    var age = 10;
    let height = 155;
    const sex = 1;
}
console.log(age); // 10 
console.log(height); // 引用错误 height 未声明
console.log(sex); // 引用错误 sex 未声明

之所以 在 if 外部还能访问到 age 是因为使用了 var 关键字来声明这个变量,他的作用域被提升到父级作用域了,这里的父级作用域是全局。

重复声明

var 关键字可以重复声明同一个变量名,但letconst 不行。

var age = 10;
var age = 20; // 最后等于 20

let height = 155;
let height = 160; // 引擎在解析到这里就会报一个 height 已经被声明的语法错误。

// 其实下面的代码已经不会被解析了,因为引擎在解析出错是就会停止解析。
const weight = 90;
const weight = 100; // 引擎在解析到这里就会报一个已声明的错误。

全局声明

在全局作用域中用不同的关键字声明变量会有一些区别,在全局作用域中用varletconst 声明的变量后续可以在任何地方访问,不同的是只有var关键字声明的变量会成为window对象的属性,letconst 则不会。

const 和 let

constlet的特性几乎相同,不同的地方在于const有强制初始化和不可修改的特性。

强制初始化

当我们要声明一个变量但还不确定它的值的时候,varlet 允许我们先将变量声明,而无需初始化它的值,而const则是声明时必须初初始化它的值。

var age;
let height;
const weight; // 语法错误:const 声明缺少初始化值。

// 所以在用 const 声明变量时必须同时初始化它的值
const weight = 100;

不可修改

const声明的变量在后续不可被修改。

const sex = 1;
sex = 2; // 语法错误 给常量赋值

const obj = {
    height: 155
}

obj.height = 160; // 但可以修改引用数据类型的属性值

总结就是,用const声明的基本数据类型不可被修改,但可以修改引用数据类型的属性值;这和数据在内存中存储的方式有关,基本数据类型是存储在栈中,引用数据类型存储在堆中,但引用数据类型的引用地址值是存储在栈中。

var 的提升

  • var关键字声明的变量会被提升到函数的顶部。
  • 在块级作用域中用var关键字声明的变量会被提升到父作用域。

只有var关键字存在提升的问题,所以建议在声明变量的时候使用let或者const关键字,只有他俩声明的变量才真正符合我们的预期,除非你想利用var的特性耍什么花招。

扩展

全局作用域不一定等于window。因为用var关键字在全局作用域下声明一个变量,该变量确实会被挂载到window对象中,但用letconst关键字声明的变量不会被挂载到window对象中。

var a = 1;
let b = 2;
const c = 3;

console.log(window.a == a); // true
console.log(window.b == b); // false
console.log(window.c == c); // false