# 声明关键字
# 前言
- es6 开始之后,又出现了
let
,const
两个关键字用于声明变量,三个关键字存在一些细微的区别,这里从区别性方面出发,总结一下。注:某些示例代码因为
let
和const
的表现一致,所以统一用let
作为示例,有不同的时候会特意说一下const
。 - 变量提升 and 函数提升与普通变量提升的区别
# let var const 区别
# 作用域区别
- var 声明变量会挂载在全局对象(浏览器环境就是
window
对象),函数作用域。 - let,const 不会,块级作用域,就是一个大括号包裹的代码块。
- 注意如果变量不加声明的关键字,默认就是挂载在全局对象上。
// 代码块1 声明挂载全局变量上
var bar = 'bar';
let foo = 'foo';
console.log(window.bar); // bar
console.log(window.foo); // undefined
// 代码块2 作用域
{
let b = 2;
var a = 1;
}
function fn() {
var c = 3;
d = 10;
}
fn();
console.log(a); // 1
console.log(d); // 10
console.log(window.d); // 10
console.log(c); // Uncaught ReferenceError: c is not defined // 函数作用域
console.log(b); // Uncaught ReferenceError: b is not defined // 块级作用域
# 重复声明
- var 可,会进行值的覆盖。
- let,const 不可。
var a = 1;
var a = 2;
console.log(a); // 2
let b = 1;
let b = 2; // Uncaught SyntaxError: Identifier 'b' has already been declared
# 声明前可否使用(变量提升,暂时性死区)
- var 可,存在变量提升。
- let,const 不可,存在暂时性死区,声明前的区域不能使用该变量大概就是暂时性死区的意思。
console.log(a); // undefined
var a = 1;
// 等价于
// var a;
// console.log(a);
// a = 1;
console.log(b); // Uncaught ReferenceError: b is not defined
let b = 1;
# 声明前是否必须赋值
- var,let 不需要。
- const 在声明时就需要显性指定赋值,否则报错
Uncaught SyntaxError: Missing initializer in const declaration
。
var a;
let b;
const c; // Uncaught SyntaxError: Missing initializer in const declaration
# 常量
const 声明的变量为常量,变量为基础类型则值不可变,引用类型时则引用的指针值不可变。
const a = 1;
const obj = { a: 1 };
obj.a = 2; // 不报错,因为引用不变
a = 2; // Uncaught TypeError: Assignment to constant variable
# 变量提升
其实应该叫变量声明提升。
- 只有声明被提升,初始化不会被提升。
- 声明会被提升到当前作用域的顶端。
- 普通变量和函数声明都存在提升,另外函数
function
关键字声明的是整个声明和赋值都会提升。 - 注意:变量的函数赋值与普通的变量赋值表现一致,即函数的赋值不会提升。
说了这么多,看几个经常会遇到的题目对比一下就清楚了:
console.log(a); // undefined
var a = 1;
console.log(a); // f a() {}
console.log(a()); // 2
function a() {
return 2;
}
console.log(a); // undefined
console.log(a()); // Uncaught TypeError: a is not a function
var a = function() {
// 这种方式声明的函数赋值操作不会提升
return 2;
};
# 函数提升与变量提升优先级
如果一个同名变量,又被声明为函数,又用 var 声明,两者间都存在变量提升,优先级怎么样呢?
函数提升
在变量提升
之前。变量的问题,莫过于
声明
和赋值
两个步骤,而这两个步骤是分开
的。函数声明被提升时,声明和赋值两个步骤都会被提升,而
普通变量却只能提升声明步骤
,而不能提升赋值步骤
。变量被提升过后,先对提升上来的所有对象统一执行一遍声明步骤,然后再对变量执行一次赋值步骤。而执行赋值步骤时,会
优先执行函数变量的赋值步骤
,再执行普通变量的赋值步骤
。先解析,再
按顺序
执行。
来看两个例子:
# 例子1:
console.log(a); // f a() {} 不管var a = 1;function a() {};哪行代码在前此处都是函数
var a = 1;
function a() {}
console.log(a); // 1
// 等价于
预编译后等价于:
var a;
function a
a = () {} // 此处都是优先函数的赋值
console.log(a);
a = 1;
console.log(a);
# 例子2:
var foo = 'hello';
(function(foo) {
console.log(foo);
var foo = foo || 'world';
console.log(foo);
})(foo);
console.log(foo);
// 依次输出 hello hello hello
预编译后等价于:
var foo = 'hello';
(function(foo) {
var foo; // undefined;
foo = 'hello'; //传入的foo的值
console.log(foo); // hello
foo = foo || 'world'; // 因为foo有值所以没有赋值world
console.log(foo); //hello
})(foo);
console.log(foo); // hello,打印的是var foo = 'hello' 的值(变量作用域)