JS 中声明变量的几种方式
- ES5: var & function
- ES6: let & const & class & import
let vs const
ES6 中,class 和 import 都是有具体含义的,而 let 和 const 有什么区别呢?
有些人可能会认为, let 是声明变量的,const是声明常量的,这句话就大错特错了。
let 和 const 声明的都是变量,都是要存储到当前上下文的 VO / AO 变量对象中的。
二者的区别在于:
- let声明的变量,后续根据需求,可以改变"变量"和"值"之间的指针指向;而 const 不允许改变"变量"的指针指向;
- const 声明的变量必须要有初始值。
let 和 var
都可以声明一个值可变的变量,它们的区别如下:
- var 存在变量提升,而 let 或者 const 都不会
console.log(x); // undefined
console.log(y); // cannot assess 'y' before initialization
var x = 12;
let y = 13;
// 好处 更严谨 参见以下的函数表达式声明函数
// 「不存在变量提升,只能在创建函数以下使用函数」
// 「用 const 声明,后期不允许重构 fn」
const fn = function() {}
- 全局上下文基于 var 声明的变量是直接存储到 GO 中的(window), 而 let 是存储到 VO(G) 中的,和 GO 没关系。
var x = 12;
let y = 13;
console.log(x, y); // 12 13
console.log(window.x, window.y); // 12 undefined
- var 允许重复声明「只不过浏览器只声明一次而已」,但是词法上是允许的,let/const 在词法上都不允许重复声明「错误发生在词法解析阶段」, 并且不论我们基于何种方式声明过这个变量(例如: var/function 再或者函数形参),let/const 声明都会报错!
// 不输出 ok 直接报错
console.log('ok'); // Uncaught SyntaxError: Identifier 'x' has already been declared
var x = 12;
let x = 13;
浏览器拿到从服务端获取的 JS 代码, 首先进行词法分析 (按照 ECMA262),分析完成的结果是一个 AST 语法树,然后浏览器底层 (比如 C++)程序按照生产的语法树一步步的去执行!这个过程比较复杂,后面单独写一篇文章来总结它。
- 在 JS 代码执行的过程中,如果花括号(排除对象和函数的)中出现 let/const/function/class 等关键词(切记:没有 var),则当大括号所在的代码块,会产生一个"私有块级上下文"。 var 不会产生块级上下文,而且块级上下文对他也没有任何的作用。
块级上下文 EC(BLOCK) 中有自己的变量对象,代码执行前会做以下几个事情。
- 初始化作用域链
初始化this初始化 arguments- 变量提升
- 代码执行
// console.log(a); // undefined
// console.log(b); // uncaught ReferenceError: bb is not defined
var a = 12;
let b = 13;
if (1 == 1) {
// EC(BLOCK) 块级私有上下文
// VO(BLOCK) // 私有变量对象
// let b = 200 执行后,在 VO 中 b ---> 200
// 作用域链(有): <EC(BLICK), EC(G)> 上级上下文是看在哪指向代码产生的块级上下文
// 初始化 THIS: 块级上下文没有自己的 this
// 初始化 Arguments: -
// 变量提升(有):...
// 代码执行(有):
console.log(a); // 12
// console.log(b); // ReferenceError: b is not defined
var a = 100;
let b = 200;
console.log(a, b); // 100 200
}
console.log(a, b); // 100 13
- 暂时性死区
console.log(x); // VM3107:1 Uncaught ReferenceError: x is not defined
console.log(typeof x); // undefined 基于 typeof 检测一个未被声明的变量,结果是undefined,而不会报错。
再看下面这一段代码:
console.log(typeof x); // cannot assess 'y' before initialization
let x = 12;
这就是暂时性死区,在变量声明前不允许使用,也不允许在上面声明。