学习笔记 JavaScript ES6 声明方式let

一、let定义的变量不属于顶层对象window

先复习一下var => variable 变量

var a = 5;
console.log(a);


b = 7;
console.log(b);


-------
5
7

写var和不写var都可以正常输出5,7,但是写var和不写var的意义是不同的

写var代表在当前作用域内声明了变量a,不写var相当于对window这个全局对象中的属性b进行赋值。这一点可以通过使用delete函数来测试,这个函数只能删除对象的属性,不能删除变量。

var a = 5;
delete a;
console.log(window.a);

b = 7;
delete b;
console.log(window.b);

--------------
5
undefined // b已经被删除了,无法找到

既然说a是变量不是属性,b是属性,但是下面代码还是可以正常执行

var a = 5;
console.log(window.a);

b = 7;
console.log(window.b);

-----------
5
7

为什么a不是属性,但是window.a不报错还能正常输出呢?这是因为最初在设计JS的时候,将顶层对象的属性和全局变量进行了挂钩,正常的逻辑是不应该这样的,所以这也被多人认为是JS设计败笔之一。

试想,如果是复杂的项目,全局变量都被挂在了window对象下面,那么window对象会越来越大,这就是污染全局变量,而let就很好的解决了污染全局变量这个问题。这也就是ES不断升级的目的。

let a = 5;
console.log(a);
console.log(window.a);

---------------
5
undefined // let定义的a就不能输出了

上面那里有一个需要注意的地方,就是写在src里面的代码(环境构建那节说过src与static目录的区别)是会被webpack打包编译的,打包编译时webpack会屏蔽掉一些问题,比如var a定义的变量,cosole.log(window.a)时就会变成undefined。

二、let不允许重复声明

let a = 1;
let a = 2;
console.log(a);

----------------------------------------------------------------
Uncaught SyntaxError: Identifier 'a' has already been declared

三、不存在变更提升

先来看var定义变量

console.log(a);
var a = 7;

// 上面代码相当于下面的代码

var a;
console.log(a);
a = 7;

---------------------------------------------------------------------------
undefined // undefined不是错误,只有红叉才是错误,此时变量a被提升到console上面了
7

再来看let

console.log(a);
let a = 7;
----------------------------------------------------------------
Uncaught ReferenceError: Cannot access 'a' before initialization

所以,let不存在变更提升~

关于“;”,使用webpack构建时,每行代码后面会被加上“\n”,所以不写";"也是可以的。

四、暂时性死区

防止变量在声明前被使用,使代码更加安全。

// 第一个例子
var a = 5
if(1 == 1) {
    a = 6
    let a
}

-----------------------------------------------------------------
Uncaught ReferenceError: Cannot access 'a' before initialization

// 第二个例子

function test(a = b, b = 1) {
    console.log(a,b)
}
test();

-----------------------------------------------------------------
Uncaught ReferenceError: Cannot access 'b' before initialization

五、块级作用域({~}内就是块)

var定义的变量没有块级作用域,ES5的世界里只有全局作用域和函数作用域,这样会带来很多不合理的场景,比如

// var例子,结果不合理
for (var i = 0; i < 3; i++) {
    console.log(i);
}

console.log("for循环外" + i) // i在上面的花括号外面也能取得到值
----------------------------------------------------------
0
1
2
for循环外3

// let例子,结果合理
for (let i = 0; i < 3; i++) {
    console.log(i);
}

console.log("for循环外" + i)

------------------------------------------
0
1
2
Uncaught ReferenceError: i is not defined

注意:ES6的块级作用域,是必须有花括号的

if(1 == 1) let a = 7;

--------------------------------------------------------
Uncaught SyntaxError: Lexical declaration cannot appear 
in a single-statement context

看一个小面试题,求输出结果

for(var a = 0; a < 3; a++) {
    setTimeout(function () {
        console.log(a)
    })
}

-------------------
(3) 3 // 输出了三次3

因为setTimeout函数是异步操作,当不会马上执行,而且上面的例子中没写时间,就是等到它该执行的时候才会被执行。for循环是同步操作,for执行完之后,setTimeout才会执行。

在JS中有一个叫作事件循环机制eventloop,setTimeout会等主线程空闲,且时间到了的时候才会执行。

闭包

如果需要上面的程序输出0,1,2应该如何改程序呢?

先写一个函数的立即执行就是() (),对就是两个括号,看程序吧还是

// 闭包是以前的知识,它有几个特点,有一个外部函数,还有一个内部函数,内部函数调用外部函数变量
// 这个时候能保证外部函数的变量状态不被释放,也就是每次循环时i的状态,0,1,2不被释放
for(var i = 0; i < 3; i++) {
    (function(j) { // j作为形参接收i传过来的值
        setTimeout(function () {
            console.log(j)
        })
    })(i) // 通过方法+括号可以直接调用这个方法,因为方法没有名字,所以只能被调用一次
}
-------
0
1
2

上面的代码是一种实现输出0,1,2的方式,通过let也可以实现,见代码

for (let i = 0; i < 3; i++) {
  setTimeout(function () {
    console.log(j);
  });
}
-----------
0
1
2
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值