一、let和const简单用法介绍:
1、let和const是什么
let和const是用来声明变量和常量的,之前用的var声明变量,现在let也是用来声明变量的,而const是用来声明常量的,const就是constant的缩写
2、let 和 const 的用法:
let就跟var差不多,例如:
var username = 'Alex';
let age = 18;
const sex = 'male';
console.log(username, age, sex); // Alex 18 male
什么是变量,什么是常量:
username = 'ZS';
age = 28;
console.log(username, age); // ZS 28
// 同上,这里的age和username就是变量
sex = 'female'; // 在这里会报错,因为sex是常量,一旦被赋值就不能再被改变了
总结:
var、let声明的就是变量,变量一旦初始化之后,还可以重新赋值
const 声明的就是常量,常量一旦初始化,就不能重新赋值了,否则就会报错
二、let和const为何出现:
// 1.为什么需要 const
// let
let sex = 'male';
// ...
sex = 'female';
console.log(sex); // 不会报错
// const
const sex = 'male';
// ...
sex = 'female';
console.log(sex); // 这里会报错,因为const定义常量
// const 就是为了那些一旦初始化就不希望重新赋值的情况设计的
// 2.const 的注意事项
// 2.1.使用 const 声明常量,一旦声明,就必须立即初始化,不能留到以后赋值
const sex;
sex='male'; // 这样会报错 Missing initializer in const declaration
const sex = 'male'; // 这样写才为正确写法!
// 2.2.const 声明的常量,允许在不重新赋值的情况下修改它的值
// 基本数据类型
const sex = 'male';
sex = 'female'; // 报错!
// 引用数据类型
const person = { username: 'Alex' };
// person = {}; // 一旦再重新赋值即会报错!
person.username = 'ZhangSan';
console.log(person); // 不会报错 输出 {username: "ZhangSan"} // 可以修改值但不能重新赋值
person = {}; // 报错 原因是进行了重新赋值
// 3.什么时候用 const,什么时候用 let
// 推荐:
// 在不知道用let还是const时,推荐先用const
总结:
1、const可以在引用类型的情况下修改里面的值,但一旦const声明的是基本类型则无法修改值。2、在不知道到底用let和const的情况下尽量用const,一是实际开发中基本这样来,二是用const一旦改变值时会报错(这是对人的一个提醒),这样可以根据需求去将const改为let,但如果用的let则不会报错,可能后面对值的改变会影响前面(最重要的是程序没有提醒,人可能不知道)。
三、let和const的区别:
1、重复声明:
// 已经存在的变量或常量,又声明了一遍
// var 允许重复声明,let、const 不允许
let a = 1;
// ...
let a = 2;
console.log(a); // 会报错
function func(a) {
let a = 1;
}
func();
// 会报错,a在函数里面有,故不能再在函数内声明!
2、变量提升:
// var 会提升变量的声明到当前作用域的顶部
console.log(a);
var a = 1; // 不会报错,可以打印出a
// 相当于
// var a;
// console.log(a);
// a = 1;
// console.log(a);
// let、const 不存在变量提升
console.log(a); // 会报错,找不到a
let a = 1;
这里的没有变量提升,实际上是为了开发者养成良好的编程习惯,对于所有的变量或常量,做到先声明,后使用!
3、暂时性死区:
// 只要作用域内存在 let、const,它们所声明的变量或常量就自动“绑定”这个区域,不再受到外部作用域的影响
// let、const 存在暂时性死区
let a = 2;
let b = 1;
function func() {
console.log(b); // 1
}
func();
// 在这个例子中不会报错,因为函数中没有b,所以会向上找,找到b = 1
// let、const 存在暂时性死区
let a = 2;
let b = 1;
function func() {
console.log(a);
let a = 1;
}
func();
// 在这里会报错,原因是a用的let声明的,因此就将a绑定到了函数的作用域内,在该作用域中是先打印a后声明a的,所以会报错!
4、window 对象的属性和方法:
// var/function
var age = 18;
function add() {}
console.log(window.age); // 18
console.log(window.add === add); // true
// let/const
let age = 18;
const add = function () {};
console.log(window.age); // undefined
console.log(window.add === add); // false
5、块级作用域:
(1)什么是块级作用域:
for (var i = 0; i < 3; i++) {
}
console.log(i); // 3
// let/const 有块级作用域
for (let i = 0; i < 3; i++) {
}
console.log(i); // 报错 --- i不能被找到 , 这是因外i在{}之外,故不能找到
说到作用域时就不得不提一下作用域链这个事,这个在JS基本方法中有,记住:只能由内往外找不能由外往内找就对了!
(3).有哪些块级作用域:
一般有{}的都有块级作用域:
// 块级作用域:
{}
for(){}
while(){}
do{}while()
if(){}
switch(){}
// 但函数和对象可不是块级作用域
// function(){}
// const person = {
// getAge: function () {}
// };
注意:块级作用域是相对let和const而言的,var可没有块级作用域!
简单举例循环中块级作用域中的区别:
目标:当点击相应按钮时会在控制台打印出相应按钮的下标!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div{
margin: 0 auto;
width: 300px;
display: flex;
justify-content: space-between;
}
.btn{
width: 50px;
height: 50px;
font-size: 16px;
text-align: center;
}
</style>
</head>
<body>
<div>
<button class="btn">0</button>
<button class="btn">1</button>
<button class="btn">2</button>
</div>
<script>
var btns = document.querySelectorAll('.btn');
for(var i=0; i < btns.length; i++){
btns[i].onclick = function() {
console.log(i); // 由于不在块级作用域导致每次打印的均为3,而不是相应的下标 0 1 2
// 不打印相应的下标原因很简单,当打印i时,会往外找i,i在全局作用域中且循环完为3,所以打印出来全是3,与作用域有关!
}
}
</script>
</body>
</html>
在上面的案例中,因为函数里面没有i,所以程序就会往外找,因为出了点击事件绑定的函数就是for循环,因为for循环没有作用域,所以相当于在全局里面找i,在全局中i是3,因此打印出来是3
ES6之前的解决方法:
采用闭包的思想,具体代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div{
margin: 0 auto;
width: 300px;
display: flex;
justify-content: space-between;
}
.btn{
width: 50px;
height: 50px;
font-size: 16px;
text-align: center;
}
</style>
</head>
<body>
<div>
<button class="btn">0</button>
<button class="btn">1</button>
<button class="btn">2</button>
</div>
<script>
var btns = document.querySelectorAll('.btn');
for(var i=0; i < btns.length; i++){
(function(index) {
btns[index].onclick = function() {
console.log(index);
}
})(i);
// 将其放到一个立即执行的匿名函数里面,这种是采用了闭包的思想!
}
</script>
</body>
</html>
在这里可以这样理解:打印index就要找到index,在点击事件绑定的函数中没有index,所以就会往外找,于是就到了立即执行函数的作用域,在立即执行函数的作用域里面可以找到index,index就是立即执行函数传进去的参数嘛,因此能找到,所以可以打印出来。
ES6之后的解决方案:
直接将var 改为 let 即可实现:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div{
margin: 0 auto;
width: 300px;
display: flex;
justify-content: space-between;
}
.btn{
width: 50px;
height: 50px;
font-size: 16px;
text-align: center;
}
</style>
</head>
<body>
<div>
<button class="btn">0</button>
<button class="btn">1</button>
<button class="btn">2</button>
</div>
<script>
let btns = document.querySelectorAll('.btn');
for(let i=0; i < btns.length; i++){
btns[i].onclick = function() {
console.log(i); // let有块级作用域,相当于创建三个空间,分别是 0 1 2 这三个事件
}
}
</script>
</body>
</html>