1. var
1.1 描述
var
用于声明一个变量,在全局范围内声明,它的作用域是全局;在函数中声明,它的作用域是它所在函数的执行上下文及其闭包(嵌套函数);
var
声明的变量存在变量提升(hoisting),即把变量提升为go/ao的不可配置属性,值为undefined
;函数提升,即把函数提升为go/ao的不可配置属性,值为函数对象的地址;不可配置属性代表不能使用delete
关键字删除;此外,需要注意是提升到当前作用域的顶部;
var
允许重复声明变量,先声明的被覆盖;
1.2 示例
示例代码
var username = '小野友树';
var n = 100;
foo();
function foo() {
var num = 10;
console.log(num, n, 'top',username); // 10,undefined,'top','小野友树'
var n = 20;
console.log(num, n, 'bottum',username); // 10,20,'bottom','小野友树'
console.log(temp); // Uncaught ReferenceError: temp is not defined 引用错误
}
伪代码
// 全局对象预解析
globalObject = {
String:'',
Number:'',
setTimeout:, // ...
window:globalObject, // go身上又有一个window属性,指向自己
username:undefined, // 变量提升
n:undefined, // 变量提升
foo:0x11a // 函数提升, ==> foo函数对象
}
// 全局执行上下文入栈,执行代码
username = '小野友树';
n = 100;
foo()
// 遇到函数调用语句,函数的ao预解析
ao = {
num:undefined,
n:undefined
}
// 函数执行上下文入栈,执行代码
num = 10;
console.log(num, n, 'top',username); // 10,undefined,'top','小野友树'
n = 20;
console.log(num, n, 'bottum',username); // 10,20,'bottom','小野友树'
console.log(temp); // Uncaught ReferenceError: temp is not defined
输出结果
此例中:
var
存在变量和函数声明提升,因此第一句打印,n输出undefined而不是100- username和temp在函数作用域内找不到,就会沿着作用域链去查找
1.3 var声明多个变量
1. 等号
function show() {
var a = (b = c = d = 5);
}
show();
console.log(b, c, d); // 5 5 5
console.log(a); // Uncaught ReferenceError: a is not defined
因为赋值是从右向左结合 :
var a = b = c = d = 5;
等价于
var a = ( b = ( c = ( d = 5 ) ) );
其中只有 a 被 var 声明了,b , c 和 d 没有声明而是在函数作用域中没有寻找到,沿着作用域链也没找到,最终都自动解析为全局变量了,因此可以在全局内打印。
2. 逗号
function show(){
var a = 5, b = 6, c = 7;
}
show();
console.log(a,b,c); // 全都报错 var.html:34 Uncaught ReferenceError: a(b,c) is not defined
注意: var 声明了3个局部的变量
2. let
-
let
不存在变量、函数声明提升,即不会在全局对象/ao上创建属性; -
let
不允许重复声明变量,否则会抛出SyntaxError
语法错误; -
let
声明的变量作用域只在其声明的块或子块内部,这一点,与var相似。二者之间最主要的区别在于var 声明的变量的作用域是整个封闭函数。
function varTest() {
var x = 1;
{
var x = 2; // same variable!
console.log(x); // 2
}
console.log(x); // 2
}
function letTest() {
let x = 1;
{
let x = 2; // different variable
console.log(x); // 2
}
console.log(x); // 1
}
- 与
var
不同的是,let
只是开始声明,而非一个完整的表达式。这意味着你不能将单独的let
声明作为一个代码块的主体(这是有道理的,因为声明的变量无法被访问)。
if (true) let a = 1; // SyntaxError: Lexical declaration cannot appear in a single-statement context
- 暂时性死区问题
var foo = 123
{ // TDZ starts at beginning of scope
console.log(bar); // undefined
console.log(foo); // ReferenceError
var bar = 1;
let foo = 2;
// End of TDZ (for foo)
}
没有变量提升,且块级作用域内被let声明的变量也不会沿着作用域去找!
let foo = (foo + 55); // ReferenceError
因为表达式是先执行右边,再赋值
- 暂时性死区与
typeof
{
console.log(typeof b); // undefined
console.log(typeof a); // Uncaught ReferenceError: Cannot access 'a' before initialization
let a = 1;
var b = 2;
}
var
与let
合并的声明方式会抛出SyntaxError
语法错误,因为var
会将变量提升至块的顶部,这会导致隐式地重复声明变量。
let x = 1;
{
var x = 2; // SyntaxError for re-declaration
}
3. const
与 let
相同的部分不再赘述。
const
声明创建一个常量,常量要求一个初始值,因此const声明必须初始化;
const
声明创建一个值的只读引用,但这并不意味着它所持有的值是不可变的,只是变量标识符不能重新分配;const
声明的对象和数组,对象的属性和数组的元素是可以改变的;