【笔记】再学JavaScript ES(6-10)全版本语法——作用域、let&const

文章目录

  • 一、作用域
    • 1.全局作用域
    • 2.函数作用域(局部作用域)
    • 3.块状作用域(ES6新增)
    • 4.动态作用域
  • 二、let&const
    • 1.let
    • 2.const
  • 三、经典面试题


一、作用域

新建static/lesson2-1.js

touch static/lesson2-1.js

1.全局作用域

定义在最外部,全局可用,可跨文件,跨函数使用
(经过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

2.函数作用域(局部作用域)

测试用例:

function test() {
  var a = 3
  return a + 4
}
console.log(test())
console.log(a)

测试结果:

7
a is not defined

具有函数作用域的变量若要在外部访问有两种方法:

  • return
  • 闭包

变量使用时沿着作用域链寻找,调用外部函数时,通过内部函数返回外部函数的变量,形成闭包

function test () {
  var a = 3

  function test2 () {
    var b = 4
    return a + b
  }

  return test2
}

test()

3.块状作用域(ES6新增)

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

4.动态作用域

window.a = 3

function test () {
  console.log(this.a)
}

test()
test.bind({ a: 100 })() // 动态绑定到匿名对象

二、let&const

1.let

  • let声明的变量是具有块状作用域的
  • let声明的变量不能用全局对象的属性来访问
  • let不能重复定义一个重名变量
  • let声明的变量不会进行变量提升(有暂时死区,不能在声明之前使用)

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

2.const

  • 用来声明常量
  • 值不可改动
  • 声明和初始化必须一句完成
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 中移出,然后方可访问。


拓展阅读

  1. JavaScript深入之词法作用域和动态作用域
  2. JavaScript深入之闭包
  3. 深入理解JS中声明提升、作用域(链)和 this 关键字
  4. JavaScript关于作用域、作用域链和闭包的理解
  5. ES6 系列之 let 和 const
  6. JavaScript深入之作用域链

三、经典面试题

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)
}

【笔记】再学JavaScript ES(6-10)全版本语法——作用域、let&const_第1张图片
【笔记】再学JavaScript ES(6-10)全版本语法——作用域、let&const_第2张图片

截图来自:scope-在JavaScript中使用“ let”和“ var”来声明变量有什么区别?-CSDN问答频道
————————
【笔记】再学JavaScript ES(6-10)全版本语法——作用域、let&const_第3张图片
截图来自: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))
}

相关题目日后补充。。。

拓展:

  • 关于JavaScript scope的一切

你可能感兴趣的:(ES(6-10)全版本语法)