javaScript深度理解 — 变量、常量

在这里插入图片描述

变量

var hello = 'hello world';
console.log(hello);// => hello world

两年前刚刚接触JavaScript时候敲下了我的第一行代码(emm没错,我的第一段代码就是这么有简单,以至于特别容易回忆。)的时候就用到了变量,现在到了实习阶段,变量已经在程序中随处可见。那么到底什么是变量呢,我们先看看官方的定义:在程序中,当程序需要将值保存起来以便将来使用时,便将值赋值给一个变量。变量是一个值的符号名称,可以通过名称对值进行引用。
举个栗子,变量就像一张银行卡,你要有一个变量名银行卡号),你把钱存在卡里(把值赋值给变量),当你要取钱的时候我们提供银行卡号(变量名),卡里的钱(变量的值)自然就属于你了(忽略输入密码缓解)。ECMAScirpt的变量是松散类型的,意思是变量可以保存任何类型的数据。每个变量只不过是一个用以保存任意值的命名占位符。在js中,使用一个变量需要提前声明,有三种声明方式:var,const和let。const和let为ES6新增。

var关键字

要定义变量,可以使用var操作符,var是一个关键字,后面跟随变量名(即标识符),这段代码声明了一个名字为message的变量,可以用它保存任意类型的值。(不初始化的情况下变量会保存一个特殊值undefined):

var message;   	// => undefined
var a,b,c;//同时声明三个未初始化(未赋值)的变量 => undefined

ECMAScript实现变量初始化,因此可以同时定义多个变量并设置他的值,并且可以修改他的值和类型:

var message = 'hi';   	// => 'hi'
message = 1024			// => 1024
var d = 0,e = 1,f = [],//同时声明三个变量,并且初始化为数字0、1和空数组

如果用var重复声明了变量,是没有任何问题的,如果重复声明的变量同时初始化,就相当于为这个变量重新赋值(用let 和const重复声明会报错,后面会讲到)。

var i = 0;	//声明一个数字类型的变量
console.log(i);// => 0
var i; 		//再次声明
console.log(i);// => 0
var i = 5;  //再次声明并初始化
console.log(i);// => 5

1、var声明作用域

由于js中存在函数作用域,所以在函数内部声明的变量,只有在声明这个变量的函数内部能访问到。因为只在局部有定义,所以称作局部变量,在函数外部无法访问:

function demo() {
	var message = 'hi';
	console.log(message);
}
demo() // => 'hi'
console.log(message); // => 报错:message is not defined

由于message变量是在函数内部定义的,函数执行完之后没有任何引用指向这个变量,随即它被销毁,所以访问会出错。但是不定义在任何函数内部的变量,或者省略了var操作符,可以创建一个全局变量,全局变量在整个脚本中都可以访问:

var hello = 'hello Eric';	//声明一个全局变量
function demo() {
	message = 'hi';
}
console.log(message);//  => 'hi'
console.log(hello);  // => 'hello Eric'

注意:
虽然可以省略var操作符定义全局变量,但是不推荐这么做,在局部作用域定义的全局变量很难维护
并且容易造成困惑,因为不能一下断定是否就是有意为之,而且不用var语句定义变量容易修改全局
的重名变量,搞混全局环境。


下面的代码无意间修改了全局变量,全局命名空间乱了:

var scope= '全局';	//定义一个全局变量
function demo() {
	scope= '局部'; //无意间修改了全局变量
	return scope;
}
demo();  // => '局部'
console.log(scope);//  => '局部' 

在函数体内,局部变量优先级高于全局变量,如果函数中定义的局部变量或者是函数参数中带的变量和全局变量重名,(函数参数也是局部变量,只在函数体内有定义)全局变量就会被局部变量覆盖,但是两个变量同时存在:

var scope= '全局';	//定义一个全局变量
function demo() {
	var scope= '局部';
	console.log(scope);
}
demo();  // => '局部'
console.log(scope);//  => '全局'

关于函数作用域更多了解请点击详解JavaScript函数作用域、闭包

2、var声明提前

使用var时,下面的代码不会报错:

function demo() {
	console.log(a);//在变量声明之前访问
	var a = 1111;
	var c = 2222;
}
demo(); // => undefinde

访问一个没有定义的变量没有报错报错,是因为函数作用域的变量在整个函数体中始终是可见的,这就意味着变量在声明之前就可用了,通俗的说就是函数里的变量都被提前到函数体的最顶部(但不涉及赋值,初始化还在定义变量的位置)。也就是说上面的代码在运行时被解析为:

function demo() {
	var a;
	var c;
	console.log(a);//在变量声明之前访问
	a = 1111;
	c = 2222;
}
demo(); // => undefinde

所以上面的代码并不会报引用错误,而且输出了undefined,因为定义了但是未初始化的变量都被程序默认复制undefined。

let声明

在ES6以前并没有块级作用域,而是函数作用域,ES6新增了块级作用域。let跟var的作用最明显的区别就是let声明的是块作用域,var声明的是函数作用域。

if(true) {
	var name = 'Eric';
	console.log(name);// => 'Eric'
}
console.log(name);// => 'Eric'
//下面用let声明变量
if(true) {
	let age= 24;
	console.log(age);// => 24
}
console.log(age);// => ReferenceError 引用错误

上文第二段的age变量之所以不能被引用是因为let定义的变量作用域是块作用域,也就是说age只有在定义它的块中有定义。块作用域是函数作用域的子集,因此适用于var的限制也限制了let。重复声明会报错:SyntaxError

if(true) {
	var name;
	var name;
}
//下面用let声明变量
if(true) {
	let age;
	let age; // => Uncaught SyntaxError: Identifier 'age' has already been declared,
	//报错的文本意思是:标识符 age 已经声明过了。
}

如果在不同的块作用域下使用相同的变量名则不会出错,因为在一个块中没有重复声明:

let name = 'Eric';
console.log(name); // => 'Eric'
if(true){
	let name = 'Lee';
	console.log(name); // => 'lee'
}

对于重复声明,即使混用let和var也会导致报错,因为他们声明的并不是两种类型的变量,只不过是指明了变量应该在相关作用域下如何存在:

let name;
var name; // => SyntaxError

var age;
let age; // => SyntaxError

区别1、

let 与var 另一个重要的区别就是let声明的变量不会被提升,下面的代码片用var声明的变量被提升,用let声明的变量并没有提升到代码块顶部:

console.log(name); // => undefined
var name = 'Eric'
console.log(age); // => ReferenceError 引用错误
let name = 24

区别2、

let 声明的变量不是全局对象的属性var声明的变量是全局对象的属性),但是let在全局定义的变量作用域依然在全局,重复定义还是会报SyntaxError:

var name = 'Eric';
console.log(window.name); // => 'Eric'
var name = 'Eric';		// => 'Eric'

let age= 24;
console.log(window.age); // => undefined
let age= 24;  // => SyntaxError 你已经定义过了age

区别3、

let 声明的变量不能条件式声明,因为在判断条件中声明的变量只作用域它的代码块。在赋值时相当于给没用var声明了一个新的全局变量:

区别4、

for循环中的let声明

for (var i=0;i<5;i++){};
console.log(i); // => 5   //可见var定义的变量已经渗透到了代码块外部
//而用let定义的变量则不会
for (let j=0;j<5;j++){};
console.log(j); // => ReferenceError   //结果为j未定义

如果在循环中设置异步调用(异步调用的时机是所有队列的同步内容执行完之后才执行异步),也就是说异步还没执行,for循环已经执行了。最终执行异步的时候这个变量是循环退出时的最终值,也就是5,此时执行异步队列,所以每次输出的都是5。而用let声明的变量在每次迭代时js后台都为它声明了一个新的迭代变量,所以setTimeout每次引用的都是不同的变量:

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

const 声明

const和let差不太多,const声明的是常量,就是说它指向的值不可以改变,如果const指向了一个对象,修改对象的属性那就没什么问题了,也就是说不修改它指向的对象自身它是不会管你的。唯一一个重要的区别是const声明时必须初始化,它的作用域也是块。

const a; //没有初始化 报错:Uncaught SyntaxError: Missing initializer in const declaration
const COUNT = 5;//正常的声明一个常量
COUNT = 6;		//修改它的值 报错:TypeError类型错误
const COUNT = 7;//重复声明也会 报错:SyntaxError已经定义过了
const PERSON = {};
PERSON.name = 'Eric'//这样修改啥事没有,const的限制只限于它指向的引用
for (const i=0;i<5;i++){};//这样肯定会不行,除非很自信的确定在循环里定义的常量不涉及自增以及再次赋值。

总结:

自从用了let声明的变量后,发现var居然用不到了,let声明的变量更加严谨,不担心会不会修改其他变量,因为let就不允许重名变量的出现。var的声明提前并不是好事,变量声明的位置最好要离引用的位置近一点。大部人认为应该优先使用const声明,其次是let,且禁用var。具体使用哪种声明,小白就不发言了!!!

var:

1、在脚本顶部或不在函数内部声明的变量为全局变量,全局变量是全局对象的属性,可以用window.变量名访问,用var定义的全局变量不可以用delete删除;
2、在函数内部省略关键字声明的也是全局变量,但它可以使用delete删除;
3、函数内部声明的变量是局部变量,在函数中都是有定义的,也就是说可以提前使用并不会报错(但是不涉及赋值);
4、函数内部声明的局部变量与全局变量重名,局部变量的优先级高于全局变量;
5、var重复声明变量并不会报错,如果声明的同时初始化了该变量,相当于为已有的同作用域且同名的变量重新赋值;

let:

1、具有块级作用域;
2、不能重复声明;
3、在循环中迭代变量时js后台会为每个迭代循环声明一个新的循环变量;
4、let声明的变量不会被提升;
5、全局声明的let变量不是全局对象window的属性,但是作用域还是在全局;

const:

1、具有let的相同点。
2、const声明的是常量,不允许修改它指向的引用;

坚持学习,每天进步,It’s going to be okey.

在这里插入图片描述
如果您觉得博文对您有帮助欢迎评论点赞收藏~
您的点赞将会成为我勇往直前的不竭动力~

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值