一、var变量
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>var 变量</title>
<script>
window.onload =function(){
var aLI = document.getElementsByTagName('li');
for(let i =0; i<aLI.length;i++){ // 当此处是var 是 alert全部为5 当为let是 依次 弹出0,1,2,3,4
aLI[i].onclick = function(){
alert(i)
}
}
}
</script>
</head>
<body>
<ul>
<li>0</li>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
</body>
</html>
二、let 变量
ES5中只有全局作用域和函数作用域,没有块级作用域,因此带来很多不合理的场景,es6之后,可以使用let来解决。如上图注释所示
let 关键字可以将变量绑定到所在的任意作用域中(通常是{...}内部。)换句话说,let为其声明的变量隐式了所在的块作用域
就是for循环还有一个特别之处,就是循环语句部分是一个父作用域,而循环内部是一个耽误的子作用域。
var 和let的区别
1、函数作用域 vs 块级作用域
var 和let第一点不同就是let说块级作用域,及其在整个大括号{}之内可见。
var:只有全局作用域和函数作用域概念,没有块级作用域的概念,但是会把{}内也假称为块作用域。
let:只有块级作用域的概念,由{}包起来,if语句和for语句里面的{}也是属于块级作用域。
function varTest(){
var x =10;
if(true){
var x = 20;
console.log(x); //20
}
console.log(x); //20
}
function letTest(){
let x =10;
if(true){
let x =20;
console.log(x); //20
}
console.log(x); // 10
}
2、变量提升 vs 暂时性死区
let和var 的第二点不同是,在变量声明之前就访问变量的话,会直接提示ReferenceError,而不是像var那样使用默认值 undefined。
var 存在变量提升,而let、const声明的变量不存在,所以使用let定义的变量一定要在声明之后再使用,否则会报错。
<script>
// 1.var变量
console.log(a); // undefined
var a = 1;
b =10;
console.log(b); //10
var b;
// 2. let变量
console.log(c); //报错: Uncaught ReferenceError: c is defind
let c = 2;
console.log(d); //报错: Uncaught ReferenceError: d is defind
let d;
</script>
<script>
var x = 5;
elem = document.getElementById('demo'); // 查找元素
elem.innerHTML = "x 为:"+ x + ",y 为:" + y;
var y = 7; // 初始化Y
//结果输出: x 为:5,y 为:undefined
// y输出为undefined,这是因为变量声明 (var y)提升了,但是初始化为 (y = 7) 不会提升,所以 y变量是一个未定义的变量
</script>
<script>
a = 5;
show();
var a;
function show(){};
// 预解析
var a;
a = 5;
show(); // 需要注意都是函数声明提升直接把整个函数提升到执行环境的最顶端。
</script>
ES6明确规定,如果区块中存在let命令,这个号区块对这些命令声明的变量,从一开始就形成了封闭的作用域,凡是在声明之前就只用这些变量,就会报错。所以在代码块内,使用let命令声明变量之前,该变量都是不可用的,这在语法上,称为:“暂时性死区(temporal dead zone) TDZ”
let a ='outside';
if(true){
console.log(a); // a is not defined
let a = 'inside'
}
当前作用域顶部到该变量声明位置中间的部分 ,都是该let变量的死区,禁止访问该变量。由此,我们给出结论,
let 声明的变量存在变量提升,但是由于死区我们无法在声明之前访问这回变量
“暂时性死区”也意味着typeof不再是一个百分之百安全的操作,因为会使typeof报错
{
typeof name; //ReferenceError
let name;
}
只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部影响。在代码块中,使用let命令声明变量之前,该变量都是不可用的,这在语法上称为“暂时性死亡”
3、let不允许重复声明变量
var:变量可以多次声明,而let不允许在相同作用域内,重复声明一个变量。
<script>
if(true){
let a;
let a; // 报错:'a' has already been declared
}
if(true){
var d;
var d; // 不会报错
}
if(true){
var c;
let c; // ‘c’ has already been declared
}
if(true){
let d;
var d; //'d' has already been declared
}
</script>
4、全局变量 vs 全局对象的属性
es5 中全局对象的属性与全局变量基本是等价的,但是也有区别。比如通过var声明的全局变量不能使用delete从window/global(global是针对node环境)上删除,不过在变量的访问上基本等价。
es6中做了严格的区分,使用var 和function 声明的全局变量依旧作为全局对象的属性,使用let
,const
声明的全局变量不属于全局对象的属性。
<script>
var a = 10;
console.log(window.a); // 10
console.log(this.a); //10
let b =20;
console.log(window.b); //undefined
console.log(this.b); // undefined
</script>
三、const 声明的常量
除了let以外,ES6还引入了const,const和let的作用域是一致的,但不同的是const变量一旦别赋值,就不能再改变了。但这并不意味着使用const声明的变量本身不可变,只是说它本身不可被再赋值了,而且const声明的变量必须经过初始化。
const a = 1;
a = 2;
cosnt b;
注
:复合类型const变量保存的引用,因为符合类型(如数组和对象)的常量不指向数据,而是指向数据(heap)所在的地址(stack),所以通过const 声明的复合类型只能保证其地址不变,但不能保证其数据不变,所以将一个对象声明为常量必须要谨慎。
简单数据类型
(数值、字符串、布尔值):值保存在变量指定的那个内存地址,因此等同于常量
复合类型的数据
(对象和数组):变量指向的是内存地址:保存的是一个指针,const只能保存这个指针地址是固定的,至于他指向的数据结构是不是可变的,就完全不能控制了。
综上所述,大多数情况下都使用 const,除非你知道你的变量的值还会被改变。
网友评论