【JS】2.语言基础、var、let、const声明的区别

本文详细介绍了JavaScript中的变量声明,包括var、let和const的关键字区别。var声明具有函数作用域,可能存在变量提升和重复声明,而let和const在ES6中引入,具有块级作用域。let允许重新声明但不可赋值,const一旦声明并初始化后不能修改。文中还讨论了暂时性死区、全局声明、条件声明以及在for循环中的使用差异。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

语言基础

语法

语法方面可以参考C语言。

区分大小写

ECMAScript中的一切都区分大小写。

标识符

即变量、函数、属性或者函数参数的名称。

标识符可以由一个或者多个下列字符组成:

  1. 第一个字符必须是一个字母、下划线_或者美元符号$;
  2. 剩下的字符可以是字母、下划线、美元符号或者数字。

驼峰命名法:是一种惯例,不做强制要求。即第一个单词的首字母小写,后面每个单词的首字母大写。例如:firstSecond myCar doSomething

注释

//这是单行注释

/*这是
多行
注释*/

严格模式

严格模式是一种不同的JS解析和执行模型。ES3中的一些不规范写法在严格模式下会被处理,对于不安全的活动将抛出错误。

要对整个脚本启用严格模式,在脚本开头加上这一行:"use strict";
这看起来是一个字符串,但是其实是一个预处理指令。

也可以单独指定一个函数在严格模式下执行,只要把这个预处理指令放到函数体开头即可:

function doSomething(){
	"use strict";
	//函数体
}

语句

ES中的语句以分号结尾。省略分号意味着由解析器来确定语句在哪里结尾。

let sum = a + b //没有分号能正确执行
let diff = a - b;//推荐加分号

多条相关语句可以合并到一个C语言风格的代码块中,代码块由一个左花括号{开始,一个右花括号}结束。

if(isTrue){
	isTrue = false;
	console.log(isTrue);
}

关键字和保留字

关键字有特殊的用途,比如表示语句的开始和结束,或者执行特定的操作。按照规定,保留的关键字不能用作标识符或者属性名。
常见的关键字有:
break do if else switch case while等等。

还有一些保留字,虽然在语言中没有特定的用途,但是是保留给将来做关键字用的嗯,所以保留字同样不能用作标识符或属性名。

变量

ES变量是松散类型的,即变量可以保存任何类型的数据。每个变量只不过是用于保存任意值的命名占位符。

声明变量的关键字(3个):

  1. var ES中的任何版本都有效;
  2. let const ES6及后面的版本有效;

Var 关键字

声明语法:var 标识符,例如:var message 即声明了一个名为message的变量。 可以用该变量保存任何类型的值,不初始化该变量的情况下,变量会保存一个特殊值 undefined

变量初始化:定义变量的同时设置它的值。
var message = "hi";
上面的操作只是将message定义为一个保存字符串"hi"的变量,像这样初始化变量不会将它标识成字符串类型,只是一个简单为赋值而已。随后,不仅可以改变变量的值,还可以改变值的类型。(一般不推荐改变变量值的类型)

var声明作用域

使用var定义的变量会成为包含它的函数的局部变量。 比如,在一个函数内部使用var定义一个变量,就意味着该变量在函数退出时被销毁

function test(){
	var msg = "hello";//局部变量
}
test();//执行函数
console.log(msg);//出错

但是在函数内部定义变量时省略var操作符,则可以创建一个全局变量。

function test(){
	msg = "hi";
}
test();
console.log(msg);//"hi"

不推荐在局部作用域中定义全局变量,因为这样会导致变量很难维护。

在严格模式下,给未声明的变量赋值,则会抛出ReferenceError

一次定义多个变量,可以在一条语句中用逗号隔开每个变量。

var msg = "hi",
	age = 19,
	isTrue = false;
var声明提升

提升(hoist):就是把所有变量的声明都拉到函数作用域的顶部。

function foo(){
	console.log(age);
	var age = 28;
}
foo();//"undefined"

之所以不报错,就是因为变量提升了,ES运行时将上面的代码等价成下面的代码:

function foo(){
	var age;//变量未初始化,自动赋值undefined
	console.log(age);//所以会输出undefined
	age = 28;
}

除此之外,var关键字还有一个特性就是,可以多次声明同一个变量。

var age = 18;
var age = 28;
var age = 38;
console.log(age);//38

let声明

let和var的作用差不多。但是却有很大的区别。

作用域

let声明的范围是 块级作用域
var声明的范围是 函数作用域
块级作用域,即一对花括号{}内部范围。

if(true){
	var name = "Maccx";//if不是函数,所以在里面定义的变量,在if外面也可以访问
	console.log(name);//"Maccx"
}
console.log(name);//"Maccx"

if(true){
	let age = 28;
	console.log(age);//28
}
console.log(age);//ReferenceError:age undefined

上面的代码中,age变量之所以不能在if外部引用,就是因为它的作用域仅限于该块内部。

块作用域是函数作用域的子集,所以适用于var的作用域限制同样适用于let。

重复声明

let不允许在同一个块作用域中重复声明同一变量。

var name;
var name;

let age;
let age;//SyntaxError;

JS引擎会记录用于变量声明的标识符及其所在的块作用域,因此嵌套使用相同的标识符不会报错。

if(true){
	let age = 28;
	console.log(age);//28
	if(true){
		let age = 38;
		console.log(age);//38
	}
}

对声明冗余报错不受混用let和var影响。这两个关键字声明的并不是不同类型的变量,它们只是指出变量在相关的作用域中如何存在。

暂时性死区

let与var的另一个区别就是,let声明的变量不会在作用域中被提升。

//name 被提升
console.log(name);//"undefined"
var name = "Maccx";

//age 不会被提升
console.log(age);//ReferenceError
let age = 28;

在解析代码的时候,JS引擎也会注意到出现在块后面的let声明,只不过在此之前不能以任何方式来引用声明的变量。在let声明之前的执行瞬间被称为“暂时性死区”。

全局声明

使用let在全局作用域中声明的变量不会成为window对象的属性(也就是全局变量,var声明的变量则会。)

var name = 'Maccx';
console.log(window.name);//"Maccx"

let age = 28;
console.log(window.age);//undefined

不过,let声明仍然是在全局作用域中发生的,相应的变量会在页面的声明周期内存续。因此,必须确保页面不会重复声明同一个变量。

条件声明

使用var声明变量时,由于变量提升,JS引擎会自动将多余的声明在作用域的顶部合并为一个声明。但是let的作用域是块,所以不可能检查前面是否已经使用let声明过同名变量,同时也就不可能在没有声明的情况下声明它。

啥意思呢,我理解的就是,当页面不确定页面中的某个变量是否已经声明同名变量的时候,就假设还没有声明过。然后此时如果是使用var进行变量声明则没问题,会被合并掉,但是如果是使用let则会报错。

<script>
var name = "Maccx";
let age = 28;
</script>
<script>
var name = "Mac";//这里没有问题
let age = 18;//这里则会报错
</script>

即使使用try/catch语句或者typeof操作符也不能解决问题,因为条件块中的let声明的作用域仅限于该块。

意思就是,假如你不确定某个变量是否已经声明,然后你想通过try/catch语句或者typeof操作符来实现:如果这个变量未声明,则声明它,使用let关键字是实现不了的。

for循环中的let声明

在let出现前,for循环定义的迭代变量会渗透到循环体外部。
改成let之后,这个问题就消失了,因为迭代变量的作用域仅限于for循环内部。

for(var i = 0; i < 10; i++){
	//循环体
}
console.log(i);//10

for(let i = 0; i < 10; i++){
	//循环体
}
console.log(i);//ReferenceError: i undefined

这里就常用的实例就是 setTimeout()

for(var i = 0; i < 5; i++){
	setTimeout(() => console.log(i),0);
}
//设想中输出:0,1,2,3,4
//实际输出:5,5,5,5,5

原因就是,迭代变量渗透到循环体外面,在之后执行的超时逻辑时,所有的i都是同一个变量,因而输出的都是同一个值。

然而如果改成使用let声明迭代变量,JS引擎在后台为每个迭代循环声明一个新的迭代变量,每个setTimeout()引用的都是不同的变量实例,所以console出了我们期望的值。

for(let i = 0; i < 5; i++){
	setTimeout(() => console.log(i),0);
}
//输出:0,1,2,3,4

这一点特性适用于所有风格的for循环,包括for-in、for-of

const声明

const的行为基本上与let相同,唯一重要的区别就是使用const声明变量时必须同时初始化变量,尝试修改const声明的变量会导致运行抛错。

const age = 18;
age = 19;//TypeError

//不允许重复声明
const name = "Maccx";
const name = "Mac";//SyntacError

//作用域也是块级作用域
if(true){
	const name = "maccx";
}
console.log(name);//ReferenceError

还有一点很重要,const声明的限制只适用于它指向的变量的引用。 即如果const变量引用的是一个对象,那么修改这个对象内部的属性并不违反const的限制。

const person = {};
person.age = 18;//这个是可以的

声明风格和最佳实践

  1. 不使用var,限制自己只使用let和const有助于提升代码质量,因为变量有了明确的作用域,声明位置以及不变的值。
  2. const优先,let次之。能迅速发现因为意外赋值导致的非预期行为。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值