声明变量
var a=100
let b=’hello’
let的三大特性:
1、不存在变量提升,必须要先声明,再使用。否则报错。
console.log(a); //报错ReferenceError,因为顺序错了,应该是let a=2在上面
let a=2;
那要是改为var a=2呢?
console.log(a); //结果:undefined。因为a未定义
var a=2;
var存在变量提升,所以实际上程序是这样运行的:
var a;
console.log(a); //var存在变量提升。let不存在变量提升,所以会直接报错。
a=2;
2、不能重复声明
更准确理解:不能在同一块级区域下,重复声明let。
let a='hello';
let a='world';
console.log(a);//报错:SyntaxError: Identifier 'a' has already been declared
而var是可以的
var a='hello';
var a='world';
console.log(a);//运行结果:world。与python一样。
3、块级作用域,变量只在代码块内有效
块级作用域指的是{},那么不单止函数有块级作用域,就连for循环也有块级作用域了
(1)let适合设置在for循环中的循环变量
目标:实现循环log9次,但是此处才log3次,这是怎么回事?
for(var i=0 ; i<3 ; i++){
for(var i=0; i<3;i++){
console.log(i);// 0 1 2
}
}
解释:外部for循环i=0时,进去内部for循环,log3次后,i被重置为3→此时再到达外部for循环已经不满足i<3了,由此退出循环。
那么,如何解决这个问题呢?引入let方法,它只在块级作用域生效。
for(var i=0 ; i<3 ; i++){
for(let i=0; i<3;i++){
console.log(i);//012 012 012
}
}
如果先声明let,再声明var会怎么样呢?会因为重复声明报错。
for(let i=0 ; i<3 ; i++){
for(var i=0; i<3;i++){
console.log(i);
}
}
解释:var声明的变量会“下克上”,从而导致在同一个区域内使得let声明的变量被重复声明:
var i=1;
(let i=0;var i=1){
var i=1;
}
var i=1,影响了三个作用域。(ps:函数作用域 ≠ 普通作用域,函数作用域=闭包,函数作用域下的var无法影响全局作用域的。一般称作用域都是普通作用域,函数作用域称为特殊作用域,也就是闭包)
(2)通过循环批量添加事件
首先,这样思路咋一看是没有问题的:
var list = [];
for(var i=0;i<3;i++){
list.push(i);
}
console.log(list);// [ 0, 1, 2 ]
但是,在循环console.log(i)时,则是这样:
var eles = [{},{},{}];
for(var i=0; i<eles.length;i++){
eles[i].onclick = function(){
console.log(i);
}
}
eles[0].onclick();// 3
eles[1].onclick(); //还是3
因为console.log的是i,而这个i在全局作用域经过一遍循环后最终为3。相当于这样:
i=3
{
eles[i].onclick = function(){
console.log(i);
}
{
eles[i].onclick = function(){
console.log(i);
}
{
eles[i].onclick = function(){
console.log(i);
}
解决:改为块级作用域,for循环经过3次后相当于这样
(let i=0;){
eles[i].onclick = function(){
console.log(i);
}
(let i=1;){
eles[i].onclick = function(){
console.log(i);
}
(let i=2;){
eles[i].onclick = function(){
console.log(i);
}
那么,为什么上面push(i)方法不会受i是全局作用域影响,而console.log(i)就会呢? console.log就是python里的print(i)呀,而print(i),这不就相当于把3个print(i)放进列表里嘛。
(3)for循环实际上有两层作用域
第一层作用域是小括号里的,第二层作用域是在大括号里的
for(let i=0;i<3;i++){
let i = 'foo';
console.log(i);// foo foo foo
}
实际上是这样的(形象理解):
(let i=0){
let i = 'foo';
console.log(i);//foo
}
(let i=1){
let i = 'foo';
console.log(i);// foo
}
(let i=2){
let i = 'foo';
console.log(i);// foo
}
那么问题来了,为什么(let i=0){console.log(i)}时,上面的i会影响下面的i呢?不是说好两个作用域吗?实际上,开发者通过对for循环某种设置下,使得for循环拥有这样的处理机制。
(4)常见:var在外,let在内;let在外,var在外时情况
function func(){
let n=5;
if(true){
let n=10;
}
console.log(n);
}
func(); //调用函数,运行结果是5,let是块级作用域,只在代码块内有效
而var可以跨代码运行
function func(){
var n=5;
if(true){
var n=10;
}
console.log(n);
}
func(); //调用函数,运行结果是10,let是块级作用域,只在代码块内有效
要是先var再let呢?不会报错,因为var是可以跨级使用的,而let只可以在if内使用。因此最终结果是5,这叫“上克不了下”。
function func(){
var n=5;
if(true){
let n=10;
}
console.log(n);
}
func(); //调用函数,运行结果是5,let是块级作用域,只在代码块内有效
那要是先let再var呢?报错。实际上因为let不可跨块级使用,但是var可以跨块级使用。因此两个同时作用于n,那n到底是5还是10呢。因此就会报错了。
function func(){
let n=5;
if(true){
var n=10;
}
console.log(n);
}
func(); //SyntaxError: Identifier 'n' has already been declared
解释:var作用域会“下克上”,导致重复函数重复声明报错
function func(){
var n=10;
let n=5;
if(true){
var n=10;
}
console.log(n);
}
素材来源:拉勾大前端训练营。