var关键字
1.var声明作用域
使用var关键字声明的变量会成为包含它的函数的局部变量。这意味着当在函数中声明一个var变量时,当函数执行完便会被销毁。
function test(){
var messsage = "hi";
}
test();
console.log(message); //出错!
特别的时当在函数内部声明变量时省略了var关键字,则可以创建一个全局变量。
function test(){
messsage = "hi";
}
test();
console.log(message); //hi
2.声明提升
先看例子:
function foo(){
console.log(age);
var age = 16;
}
foo(); // undefined
这里之所以不会报错,==是因为var关键字声明的变量会自动提升到函数作用域的顶部==,同时给该变量赋一个初始值为undefined
的值,这便是声明提升。
上面的例子等同于:
function foo(){
var age; //提前声明
console.log(age); //undefined
age = 16;
}
foo(); // undefined
var关键字的声明提升,也就是把所有变量声明都拉到函数作用域的顶部。同时var关键字也可以反复声明同一个变量。
function foo(){
var age = 16;
var age = 26;
var age = 36;
console.log(age);
}
foo(); // 36
let关键字
let关键字与var关键字作用差不多,但是有着非常重要的区别。 最明显的区别就是var声明的变量是函数作用域,而let声明的变量是块级作用域。
if(true){
var age = 16;
}
console.log(age); //16
if(true){
let name = "Tom";
}
console.log(name); //ReferenceError name没定义
let不允许在同一块级作用域中重复声明:
if(true){
let age = 16;
let age = 26; //SyntaxError: Identifier 'age' has already been declared
}
1.暂时性死区
let与var的另一个重要的区别,就是let声明变量不会在作用域中被提升,也就是没得变量声明提升。
console.log(name); //undefined
var name = "Tom";
console.log(age); //SyntaxError
let age = 16;
在解析代码时,javascript引擎也会注意到出现在块后面的let声明,只不过在此之前不能以任何方式来引用let未声明的变量。 ==在let声明之前的执行瞬间被称为“暂时性死区”,在此阶段引用任何后面才声明的变量都会抛出ReferenceError。==
2.全局声明
与var关键字不同,使用let在全局作用域中声明的变量不会成为window对象的属性,var声明则会。
var name = "Tom";
console.log(window.name); // Tom
let age = 16;
console.log(window.age); //undefined
不过,let声明仍然会在全局作用域中发生,相应的变量也会在页面的生命周期内存续。
let age = 16;
console.log(age); //16
console.log(window.age); //undefined
3.条件声明
在使用var声明变量时,由于声明会被提升,所以javascript引擎会被冗余的声明在作用域的顶部合并成为一个声明,因为let时块级作用域的,所以不可能检查出前面是否已经使用let声明过同名变量,同时也不可以在没有声明的情况下声明它。
if (typeof name === "undefined") {
let name = "Tom";
}
console.log(name); // <empty string>
为此,对于let这个新的ES6声明关键字,不能依赖条件声明模式。
4.for循环中的let声明
在let出现之前,for循环定义的迭代变量会渗透到循环体外部:
for (var i = 0; i < 5; ++i){ //循环逻辑}console.log(i); // 5
在let出现之后,let解决了这个问题,因为迭代变量的作用域仅局限于for循环内部:
for (let i = 0; i < 5; ++i){ //循环逻辑}console.log(i); // ReferenceError 未定义
在使用var最常见的问题就是迭代变量的奇特声明与修改:
for (var i = 0; i < 5; ++i) { setTimeout(() => { console.log(i); }, 0)}//你可能会以为输出 0 、1 、2 、3 、4 、5//实际上会输出 5 、5、5、5 、5、5
之所以会这样,是因为退出循环时,迭代变量保存的是导致循环退出的值:5,在执行超时逻辑时,所以i都是同一个值,因而输出的是同一个值。
==而let声明迭代变量时,javascript引擎会在后台为每一个迭代循环声明一个新的迭代变量。== 每一个setTimeout引用的都不是同一个变量实例。
for (let i = 0; i < 5; ++i) { setTimeout(() => { console.log(i); }, 0)}//输出 0 、1 、2 、3 、4 、5
这种每次迭代声明一个独立变量实例的行为适用于所有风格的for循环,包括for-in , for-of循环。
const声明
const的行为与let基本相同,唯一一个重要的区别就是用它声明变量时必须同时初始化变量,且初始化之后值不能被修改。
const age = 16;age = 26; //TypeError 给常量赋值
跟let一样,const也不允许重复声明:
const name = "Tom";const name = "Jack"; // SyntaxError
同时,==const的作用域也是块级的:==
const name = "Tom";if(true){ const name = "Jack";}console.log(name); //Tom
要注意的是,const声明的限制只适用于它指向的变量的引用。换句话说,如果const变量引用的是一个对象,那么修改这个对象内部的属性并不违反const的限制:
const person = {};person.name = "Tom"; // ok
1 条评论
牛