ES6 之前的变量声明方式:
在 ES5 中写 var 和 不写 var 区别是什么?
- 使用 var 是在当前的作用域内声明一个变量
- 在函数中声明就是函数中的作用域变量
- 在全局声明就是全局作用域变量
- 不写 var 就是在 window 这个这个全局对象上定义一个属性
如何证明 不写 var 就是在全局对象 window 上增加了一个属性呢??
通过 函数 delete 方法
delete 的作用就是用于删除对象上的属性 => 注意也只能用于删除对象上的属性 并不能够删除变量
var a = 5
console.log(a) => 5
delete a
console.log(a) => 5
console.log(window.a) => 5
b = 6
console.log(b) => 6
delete b
console.log(b) // 当前的b已经被删除掉了 报错
console.log(window.b) // undefined
总结 写 【var 就是一个变量 不写 var 就是浏览器顶层对象 window 上的一个属性】
方法 delete 可以删除对象上的属性
window.a 和 window.b 都能够输出内容是因为 js 早期的作者将 Window 顶层对象和 全局变量做了挂钩 (现在多数人都以为这是js var 变量设计的败笔)=> 所以我们使用 let 声明变量就可以很好的解决这个问题
【不推荐在开发项目的时候将 变量挂载在全局对象 window 上 这样会污染全局对象 window】
let aa = 5
console.log(aa) // 5
console.log(window.aa) // undefined
let 声明变量的优点:
- 不属于顶层对象 window
- 不允许重复声明 重复声明会报错
- 不存在变量提升
- 暂时性死区
- 块级作用域
优点解释:
· 暂时性死区
【暂时性死区 => 防止变量在声明之前使用变量】
var aaa = 5
if (true) {
aaa = 6
let aaa // Cannot access 'aaa' before initialization
}
【这种就会报错 因为在使用的时候还没有声明 这个也是暂时性死区】
function foo(a = b, b = 2) {
console.log(a, b)
}
foo()
【这里也会报错 因为在调用函数的时候 b 未初始化 暂时性死区】
function foo(a = b, b = 2) {
console.log(a, b)
}
foo()
【这个先声明在使用的就没有问题】
function foo(a = 2, b = a) {
console.log(a, b)
}
· 块级作用域
块级作用域: 【在 es5 变量的声明中只有全局作用域和函数作用域 并没有全局作用域的概念 {} 内就是块级作用域】
{} 中定义的变量只能在 {} 中使用出了这个范围就不能使用了
看看 ES5 中没有块级作用域引发的后果
for (var i = 0, i < 3; i++) {
console.log('循环内:' + i)
}
console.log('循环外' + i) // 最终的结果 i = 3
if (false) {
var a = 10
}
console.log(a) // undefined
ES6 中存在块级作用域
for (let i = 0, i < 3; i++) {
console.log('循环内:' + i)
}
console.log('循环外' + i) // 最终的结果 undefined
if (false) {
var a = 10
}
console.log(a) // 报错
【 ES6 中 块级作用域必须存在 {} 否则 js 引擎就会认为不存在块级作用域 => 报错】
细微差别:
if (true) var a = 10 // => var 声明变量是可以的 => var 会有变量的提升
if (true) let a = 10 // => let 声明变量就会报错 => js 引擎认为不存在块级作用域 => 无法声明变量 => 报错
牛刀小试:
// 考验题
for(var i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i) // 3 3 3
}, 1000)
}
这里输出的结果是 3个3
解释:
在 js 中 setTimeout 定时器是一个异步函数 异步函数的执行会等待同步函数的执行完成 当 for 循环执行完成的时候 才开始异步的 函数执行 所以 会输出 3 个 3
提出挑战 如何才能按照 0 1 2 的方式输出 结果值呢??
方式1: 通过 闭包的方式
for(var i = 0; i < 3; i++) {
(function(j) {
setTImeout(function() {
console.log(j)
})
})(i)
}
方式2: 在原来代码的基础上直接使用 let 声明 变量 i 即可
在 编译代码的时候自动将代码编译成 闭包的方式 锁住 变量 i