前言:js基础是我们入门前端的重中之重,良好的js基础在我们深入探究这门学科的过程中会起到关键作用。今天该篇文章主要分享var、let、const三者的区别。手打1600+文字希望能够让你读有所获,如果文章某些知识点有误,欢迎在评论区指正。后续还会继续分享更多知识😑。
1.标识符
所谓标识符,就是变量、函数、属性或函数参数的名称。标识符可以由一个或者多个字符组成:
- 第一个字符必须是一个字母、下划线( _ )或者美元符号( $ )
- 剩下的字符可以是字母、下划线( _ )、美元符号( $ )或者数字
2.注释
- //单行注释
- /* 多行注释 */
3.变量
ECMAScript变量是松散类型的,意思是可以用于保存任何类型的数据。有3个关键字可以声明变量:var、let和const。其中var可以在ECMAScript的所有版本中使用,let和const只能在es6及其之后的版本使用,后面我会详细讲一下这三者的区别。
3.1 var声明作用域
function test(){
var message = "学会去爱";
}
test()
console.log(message) //报错
上述代码看出,使用var操作符定义的变量会成为包含它的函数的局部变量,意味着test函数调用之后变量随即销毁。
如果需要定义多个变量,可以在一条语句中用都用分隔每个变量:
var message = "hi",
found = false,
age = 25;
3.2 var声明提升
function foo(){
console.log(age)//undefined
var age = 25;
}
之所以上述age为undefined,是因为ECMAScript运行中把它看成等价于如下代码
function foo(){
var age ;
console.log(age)
age = 25;
}
此外,反复多次使用var声明变量也是没有问题的
function foo (){
var age = 12;
var age = 13;
var age = 14;
console.log(age) // 14
}
foo()
3.3 let 声明
let和var最大的区别就是,let声明是块级作用域,而var声明的范围是函数作用域。
注:SyntaxError => 语法错误, ReferenceError => 引用错误
if(true){
var age = 25;
console.log(age) //25
}
console.log(age) //25
------------------var和let区别-----------------------
if(true){
let age = 25;
console.log(age) //25
}
console.log(age) //ReferenceError:age没有定义
在这里,用let声明的age变量之所以不能在if块外部被引用,是因为它的作用域仅限于该块内部吧。同时let不允许同一个块级作用域中出现冗余声明。这样会导致报错:
if(true){
var age = 12;
var age =12;
---------分割线---------
let name = "tom";
let name = "tony"; //SyntaxError;标识符age已经声明过了
---------分割线---------
var age = 12;
let age = 12;//SyntaxError;标识符age已经声明过了
---------分割线---------
let age = 12;
var age = 12;//SyntaxError;标识符age已经声明过了
}
暂时性死区
let和var另一个最重要的区别,就是let声明的变量不会在块级作用域中被提升。
function foo(){
console.log(age)//undefined
var age = 25;
}
function foo(){
console.log(age)//ReferenceError:age没有定义
let age = 25;
}
在解析代码时,js引擎也会注意到出现在块后面的let声明,只不过在此之前不能以任何方式引用未声明的变量,在let声明之前的执行瞬间被称为暂时性死区,在此阶段引用任何后面才声明的变量都会抛出ReferenceError(引用错误)。
全局声明
与var关键字不同,let在全局作用域中声明的变量不会成为window对象的属性,var声明的变量则会。
var name = "tony";
console.log(window.name) // tony
let name = "tony";
console.log(window.name) // undefined
不过,let声明仍然是在全局作用域中发生的,相应变量会在页面的声明周期内存续卡因此为了避免SyntaxError,必须保证页面不会重复声明同一个变量。
for循环中的let声明
//在let出现之前,for循环定义的迭代变量会渗透到循环体外部:
for(var i = 0;i<5;i++){
console.log(i)//0 1 2 3 4
}
conosle.log(i)//5
---------分割线----------------
for(let i = 0;i<5;i++){
console.log(i)//0 1 2 3 4
}
conosle.log(i)//ReferenceError
---------分割线----------------
for(var i = 0;i<5;i++){
setTimeout(()=>console.log(i),0) //55555
}
//之所以是55555,是因为在退出循环时,迭代变量保存的是导致循环退出的值:5,在之后执行超时逻辑时,所有的i都是同一个变量,因而输出都是同一个最终值。
---------分割线----------------
for(let i = 0;i<5;i++){
setTimeout(()=>console.log(i),0) //0 1 2 3 4
}
//而在使用let声明迭代变量时,js引擎在后台会为每个迭代循环声明一个新的迭代变量,每个setTimeout引用的都是不同的变量实例,所以console.log输出的是我们的期望值,也就是循环执行过程中的每个迭代变量的值。这种迭代声明一个独立的变量实例适用于所有风格的for循环,包括for in,for of循环。
3.4 const声明
cosnt的行为与let基本相同,唯一一个重要区别是用它声明变量时必须同时初始化变量,且尝试修改const声明的变量会导致运行时错误。
const age = 26;
age = 13; //TypeError:给常量赋值
-------分割线-----------
//const也不允许重复声明
const name = "tony";
const name = "tom"; //SyntaxError
-------分割线-----------
//const 声明的作用域也是块级作用域
const name = "tony";
if(true){
const name = "tom";
}
console.log(name) // tony
const声明的限制只适用于它指向的变量的引用,换句话说,如果const变量引用的是一个对象,那么修改该对象内部的属性并不违反const的限制。
const obj = {};
obj.a = 1;//这样没有问题
-------------分割线----------------
赋值为对象的const变量不能再被重新赋值为其他引用值,单对对象的键不受限制。
const objObj = {}
objObj = {} //TypeError:给常量赋值。
js引擎会为for循环中的let声明分别创建独立的迭代变量实例,虽然const和let声明的变量很相似,但是不能用const来声明迭代变量,因为迭代变量会自增,换句话说,在循环过程中会循环赋新值。
for(const i = 0;i<5;i++){
console.log(i) //TypeError:给常量赋值
}
不过,如果你只想用const声明一个不会被修改的for循环变量,那也是可以的。也就是说,每次迭代知识创建一个变量。这对for-of,for-in循环特别有意义:
let i = 0;
for(const j = 7 ; i < 5 ; i++){
console.log(j) // 77777
}
for(const key in {a:1,b:2}){
console.log(key) // a,b
}
for(const value of [1,2,3]){
console.log(value) //1,2,3
}
3.5 声明风格和最佳实践
- 减少使用甚至不使用var
- const优先,let次之