新建static/lesson2-1.js
touch static/lesson2-1.js
定义在最外部,全局可用,可跨文件,跨函数使用
(经过eslint会受影响导致不能使用)
在最外部,通过var定义的全局变量具有全局作用域
不加任何关键字的变量也具有全局作用域
不同的一点是,不加任何关键字的变量默认会是全局对象windows的一个属性,而不是全局变量
测试用例:
var abc = 1234
abcd = 2345
function test() {
ab = 3456
}
test()
测试结果:
# 三者都全局可用
$ abc
1234
$ abcd
2345
$ ab
3456
$ window.abc
1234
$ window.abcd
2345
$ window.ab
3456
# 全局变量是不能被删除的,而全局对象的属性是可以被删除的
$ delete abc
false
$ delete abcd
true
$ delete ab
true
$ delete window.abc
false
$ delete window.abcd
true
$ delete window.ab
true
测试用例:
function test() {
var a = 3
return a + 4
}
console.log(test())
console.log(a)
测试结果:
7
a is not defined
具有函数作用域的变量若要在外部访问有两种方法:
变量使用时沿着作用域链寻找,调用外部函数时,通过内部函数返回外部函数的变量,形成闭包
function test () {
var a = 3
function test2 () {
var b = 4
return a + b
}
return test2
}
test()
function test () {
var a = 3
if (a === 3) {
var b = 4
let c = 5
console.log('if')
} else {
console.log('else')
}
console.log(b) // 4, 可以访问到,这里并没有形成块状作用域
console.log(c) // c is not defined, 无法访问到,c属于块状作用域里
return a + 4
}
console.log(test())
console.log(a) // a is not defined
window.a = 3
function test () {
console.log(this.a)
}
test()
test.bind({ a: 100 })() // 动态绑定到匿名对象
let深入理解—let存在变量提升吗?
{
let a = 1
console.log(a) // 1
}
console.log(a) // a is not defined, 通过let定义的变量只能在其块状作用域内访问
var b = 2
let c = 3
console.log(b, c) // 2 3
console.log(window.b, window.c) // 2 undefined, 通过let定义的变量不能用全局对象的属性来访问
var b = 4
console.log(b) // 4
let c = 5
console.log(c) // Identifier 'c' has already been declared, let不能重复定义一个重名变量
console.log(d) // Cannot access 'd' before initialization
let d = 6
const a = 1
a = 2
console.log(a) // Assignment to constant variable,不能重复赋值
const b
b = 3
console.log(b) // Missing initializer in const declaration,声明时缺少赋值
临时死区(Temporal Dead Zone),简写为 TDZ。
let 和 const 声明的变量不会被提升到作用域顶部,如果在声明之前访问这些变量,会导致报错。这是因为 JavaScript 引擎在扫描代码发现变量声明时,要么将它们提升到作用域顶部(遇到 var 声明),要么将声明放在 TDZ 中(遇到 let 和 const 声明)。访问 TDZ 中的变量会触发运行时错误。只有执行过变量声明语句后,变量才会从 TDZ 中移出,然后方可访问。
拓展阅读
for (var i = 0; i < 3; i++) {
setTimeout(function () {
console.log(i) // 3, 三次3
}, 0)
}
上述代码执行完后,打印3次3,这是setTimeout是异步执行的(主线上绑定,任务队列中触发)setTimeout里面的function是受setTimeout的定时参数控制的,也就是每次执行到setTimeout,setTimeout里面的function会排在任务队列中,直到主线全部执行完毕,0ms后执行任务队列里的任务,当主线执行完成后,i=3,所以打印3次3,详细可见下面图
那么怎么样打印0,1,2呢?两种方法:
1.使用let来定义i,i成为块级变量,绑定到循环体的每一次迭代中
for (let i = 0; i < 3; i++) {
setTimeout(function () {
console.log(i);
},0)
}
截图来自:scope-在JavaScript中使用“ let”和“ var”来声明变量有什么区别?-CSDN问答频道
————————
截图来自:javascript - ES6 module scope - Stack Overflow
——————————
根据断点执行过程,我的理解是这样:
var:循环执行完毕,三个setTimeout里面的function挂起,等到再执行时,读取的变量j=3还是仍然在内存中保留,因此输出333
let:循环执行完毕,三个setTimeout里面的function挂起,等到再执行时,读取的变量i需要重新声明赋值(前面执行离开let i的作用域后,i就被GC回收销毁),因此输出123
2.把setTimeout放到一个自执行函数中,将i作为参数
for (var i = 0; i < 3; i++) {
(function (a) {
setTimeout(function () {
console.log(a);
}, 0)
}(i))
}
相关题目日后补充。。。
拓展: