如果使用var在函数内部定义了一个变量,就意味着该变量将在函数退出时被销毁
具体代码如下:
function test(){ var message='hi'; //局部变量 } test(); console.log(message) //出错!
不过如果在函数内部声明变量的时候省略var操作符,则可以创建一个全局变量:
function test(){ message='hi'; //局部变量 } test(); console.log(message) // hi
function foo(){ console.log(age); var age=26; } foo(); //undefine
同时反复声明同一个变量也没问题
function foo(){ var age=26; var age=36; var age=46; var age=56; console.log(age); } foo(); //56
let跟var的作用差不多,但是有着重要的区别,最明显的是,let的范围时块作用域,而var声明的范围是函数作用域。
if(true){ var name='Matt'; console.log(name); //Matt } console.log(name); //Matt if(true){ let age=26; console.log(age); //26 } console.log(age); //ReferenceError; age 没有定义
注意重复定义的问题
var name; var name; let age; let age; //SyntaxError 标识符age已经被声明过
JavaScript引擎会记录用于变量声明的标识符及其所在的块作用域,因此嵌套使用相同的标识符不会报错,而这是因为同一个块中没有重复声明:
var name ='Nicholas'; console.log(name); // Nicholas if(true) { var name='Matt'; console.log(name); } let age=30; console.log(age); //30 if(true){ let age =26; console.log(age); //26 }
对于声明冗余报错不会因混用let和var而受影响。这两个关键字声明的并不是不同类型的变量,它们只是指出变量在相关作用域如何存在。
var name; var name; let age; let age; //SyntaxError 标识符age已经被声明过
let与var另外一个重要的区别,就是let声明的变量不会在作用域中被提升
// name会被提升 console.log(name); var name= name='Matt'; //age不会被提升 vonsole.log(age); //ReferenceError; age没有被定义 let age=26;
在解析代码时候,JavaScript引擎会注意到出现在块后面的let声明,只不过在此之前不能以任何方式来引用未声明的变量。在let声明之前的执行顺序被称为‘暂时性锁区’,在此阶段引用任何后面才声明的变量都会抛出ReferenceError。
与var关键字不同,使用let在全局作用域中声明的变量不会成为window对象的属性(var声明的会)
var name='Matt'; console.log(window.name); //'Matt' let age=26; console.log(window.age); //undefined
不过。let声明仍然是在全局作用域中发生的,相应变量会在页面的声明周期内存续,因此,为了避免SyntaxError,必须确保页面不会重复声明同一个变量。
在使用var声明变量时候,由于声明会被提升,Javascript引擎自动将多余的声明在作用域顶部合并为一个声明,因为let的作用域是块,所以不可能检查前面是否已经使用过let声明过同名变量,同时也就不可能在没有声明的情况下声明它。
<script> var name="aa" let age=26; </script>
<script> var name="bb" let age=213; </script> <script> if(typeof name==='undefined'){ let name; } //name被限制在if{}块的作用域内 //因此这个赋值形同于全局赋值 name='Matt'; try{ console.log(age); } catch{ let age; } age=26; //age被限制在catch{}块的作用域内 //因此这个赋值形同于全局赋值 </script>
使用try/catch操作符也不能解决,因为条件块中let声明的作用域仅限该块
在let出现之前。for循环定义的迭代变量会渗透到循环体外部
for (var i=0;i<5;i++) { //循环体 } console.log(i) //5
改成let之后
for (let i=0;i<5;i++) { //循环体 } console.log(i) //ReferenceError:i没有定义
在使用var的时候,最常见的问题是对迭代变量的奇特声明和修改:
for (var i=0;i<5;++i) { setTimeout(()=>console.log(i),0) } //你可能以为的输出0,1,2,3,4 //实际上5,5,5,5,5
1.之所以会这样看,是因为在退出循环时,迭代变量保存的是导致循环退出的值:5.在之后执行超时逻辑时候,所有的i'都是同一个变量,因而输出的都是同一个最终值。
2.而在使用let声明迭代变量时候,Javascript引擎在后台会为每一个迭代循环声明有ige新的迭代变量。每个setTimeout引用的都是不同的变量实例,所以输出的是我们所想要的,也就是循环执行过程中每个迭代变量的值。
3.这种每次迭代声明一个独立变量实例的行为适用于所有风格的for循环,包括for-in和for-of循环。
4.这种每次迭代声明一个独立变量实例的行为适用于所有风格的for循环,包括for-in和for-of循环。
行为与let基本相同,唯一一个重要的区别在于它声明的变量必须同时初始化变量,且尝试修改const声明的变量会导致运行时错误
const age=26; age=36; //TypeError:给常量赋值 //const也不允许重复声明 const name='Matt'; const name='Nicholas'; //SyntaxError //const声明的作用域也是块 const name='Matt'; if(true){ const name='Nicholas'; } console.log(name); //Matt
const声明的限制只适用于它指向的变量的引用,换句话说,如果const变量引用的是一个对象,那么修改这个对象内部的属性并不违反const的限制(数组也可以)
const person={}; person.name='Matt'; //os
即使Javascript引擎会为for循环的let声明并创建独立的变量实例,而且const变量跟let变量很相似,也不能用const声明去替代迭代变量(因为迭代变量会自增)
for (const i=0;i<10;i++){} //TypeError;给变量赋值
不过,如果你只想用const声明一个不会被修改的for循环变量,那也是可以的。也就是说,每次迭代只是创建一个新的变量。这对for-of和for-in循环特别有意义;
let i=0; for (const j=7;i<5;==i) { console.log(j); } //7,7,7,7,7 for (const key in {a:1,b:2}) { console.log(key) } //a,b for (const value of[1,2,3,4,5]){ console.log(value); } //1,2,3,4,5