一、js中变量的定义
变量的定义过程可以分为两个部分声明
和赋值
。
在es6前,我们用var
可以声明一个变量,也可以直接使用
一个变量,es6新加了一种变量的声明方式let
;就是下面三种变量的使用方法了。
var a;//用var声明一个变量a
let b;//用let声明一个变量b
c=10;//直接使用变量c;这里c是全局作用域
console.log(a);//undefined
console.log(b);//undefined
console.log(c);//10
1、那么问题来了,这三种使用变量的方式有什么不同?这个在第五小节给予回答。
2、现在看一下变量c的应用方式,如果我们直接下面的写法:
c;
console.log(c);
会报错如下:
VM85:3 Uncaught ReferenceError: c is not defined
对比一下:
c=10;
console.log(c); // 10
这里说明了一个问题,c变量其实已经声明过了,只不过是浏览器帮我们声明了。
3、还可以得出一个结论,仅仅声明,没有赋值的变量,值是undefined
二、js中变量的命名规则
js中变量的命名规则和其它语言类似,总结起来如下:
- 变量以数字,字母,下划线或$组成
- 变量以字母,下划线或$开头
- 变量大小写敏感
- 变量不能为js中的保留字
三、js变量的提升
js变量的提升是指:无论var变量声明在哪里,js都会把变量声明放在最前面
1、注意,是用var变量声明的变量,也就是说let是没有变量提升的。
2、如何理解js都会把变量声明放在最前面?看下面的例子:
console.log(a);//undefined
var a=10;
上面这个例子为什么会是undefined?就是因为js会把变量的声明放在前面,也就是说,js会加一个变量的声明,而且,将这个变量的声明放在代码的开头,上面的代码和下面的这个是等价的。
var a;
console.log(a);
var a=10;
有了这个理解后,下面的这个实例也就不难解释了
function fun(){
console.log(a);//undefined
var a=10;
}
fun();
因为在函数内部,变量的声明也会被放在函数内部的前面
,上面的例子和下面的实际上是等价的。
function fun(){
var a;
console.log(a);//undefined
var a=10;
}
fun();
四、js变量的作用域
讲js变量的作用域我们必须分开来说,因为var和let的作用域是不同的
。
1、var的的作用域
按作用域划分,var声明的变量可以化为为全局变量
和局部变量
- 全局变量:声明在函数之外的变量
- 局部变量:声明在函数里的变量
重点来了,怎么理解声明在函数里的变量?看下面这个例子:
function fun(){
for(var i = 0; i < 10; i++){
}
console.log(i);
}
fun();
猜一下,它会打印什么?10,为什么?
还记得var变量的提升不?在函数内部,变量的声明也会被放在函数内部的前面
,其实上面的例子和下面是等价的。
function fun(){
var i;
for(var i = 0; i < 10; i++){
}
console.log(i);
}
fun();
也就是说,局部变量是以函数块为标准划定的,无论它是在if,for,switch哪个地方定义的,看以下例子:
function fun(){
if(1 == "1"){
var a =10;
}
switch(1){
case 1:
var b = 10;
}
for(var c = 0; c < 10; c++){
}
d=10;
console.log(a);//10
console.log(b);//10
console.log(c);//10
}
fun();
console.log(d); // 10
console.log(a); // VM197:21 Uncaught ReferenceError: a is not defined
下一个问题:在html中<script>
标签引入的作用域是怎样的:
//c.js
var a = 10;
//a.html
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
</body>
<script type="text/javascript" src="./c.js"></script>
<script type="text/javascript">
console.log("this is: " + a);//this is: 10
</script>
</html>
看起来答案显而易见,
//c.js
var a = 10;
//a.html
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
</body>
<script type="text/javascript">
console.log("this is: " + a);//Uncaught ReferenceError: a is not defined
</script>
<script type="text/javascript" src="./c.js"></script>
</html>
它报错说a没有定义,啥情况?
对比一下:
//c.js
var a = 10;
//a.html
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
</body>
<script type="text/javascript">
setTimeout(function(){
console.log("this is: " + a);// this is 10
}, 1000)
</script>
<script type="text/javascript" src="./c.js"></script>
</html>
这里和<script>
标签的加载顺序
有关,<script>
标签加载的js文件中定义的全局变量只有在它加载完成之后我们才可以使用。
2、let的作用域
2.1块级作用域
let是块级作用域,就是说let是在{}
中生效的,包括函数,if,for,switch,看下面的例子:
let a = 10;
{
let b =10;
console.log(b);
}
//console.log(b);
function fun (){
let c = 10;
for(let d = 0; d < 10; d++){
}
//console.log(d);
if(1 == "1"){
let e = 10;
}
//console.log(e);
switch(1){
case 1:
let f = 10;
}
//console.log(f);
}
fun();
//console.log(c);
发现被注释的地方都在报错,这很好的解释了什么是块级作用域,就是{}
里面的区域就是块级作用域
。
2.2暂时性死区
了解完块级作用域我们再来了解一下let的暂时性死区
:在代码块中,使用let命令所声明的变量在声明之前都是不可用的。在语法上,称为“暂时性死区”(temporal dead zone)TDZ。
下面这个例子帮助理解一下什么叫暂时性死区。
console.log(a);
let a = 10;//Uncaught ReferenceError: a is not defined
因为let没有变量的提升(不太正确)
,所以这个地方会报错,暂时性死区就是说let定义的变量要先声明,后使用
。
看下面的例子:
var test = 1;
function func(){
console.log(test);
let test = 2;
};
func(); // Uncaught ReferenceError: Cannot access 'test' before initialization
在let声明变量前,使用该变量,它是会报错的,而不是像var那样会‘变量提升’。
其实说let没有‘变量提升’的特性,不太对
。或者说它提升了
,但是ES6规定了在let声明变量前不能使用该变量
。
看下面:
var test = 1;
function func(){
console.log(test);
let test = 2;
};
func(); // VM289:3 Uncaught ReferenceError: Cannot access 'test' before initialization
如果let声明的变量没有变量提升,应该打印’1’(func函数外的test);而它却报错,说明它是提升了的,只是规定了不能在其声明之前使用而已。我们称这特性叫“暂时性死区(temporal dead zone)
”。且这一特性,仅对遵循‘块级作用域
’的命令有效(let、const)
2.3不允许重复声明
let声明的变量不可重复声明,看下面例子:
function fun(){
let a = 10;
let a = 1;
}
fun();//Uncaught SyntaxError: Identifier 'a' has already been declared
即使和var重复声明也不可以(块级作用域内)
function fun(){
let a = 10;
var a = 1;
}
fun();//Uncaught SyntaxError: Identifier 'a' has already been declared
看一下下面的情况:
for(let i = 0; i < 3; i++){
let i = "test";
console.log(i);
}
// test test test
for设置循环变量的是父作用域
和一个子作用域
五、let,var,直接定义变量的区别
- let是块级做用域,var是局部做用域和全局做用域
- let没有变量提升,var有变量提升
- let要先声明后使用,var因为有变量提升,可以先使用后定义
- 直接定义变量是全局变量
- let不允许重复声明,var可以重复声明