Skip to content

let 和 const

JS 作用域:全局作用域(var 声明,变量提升)、函数作用域、块级作用域(es6 新增)

let

let 关键字:声明的变量只在当前代码块中生效(块级作用域)

  • 可以重新赋值
  • 不能在同一作用域重复声明
  • 无变量提升 - 防止变量声明前使用变量

解决问题:

  • 解决 i 丢失的问题 - 不同于 var全局有效,每次循环let都是重新声明的变量
  • 内层变量可能会覆盖外层变量
  • 替代匿名函数

const

const 声明为只读常量,声明后不可改

  • 必须给初始值
  • 只在声明所在的块级作用域内有效
  • 存在暂时性死区

只读常量:变量指向的内存地址不能改动,只影响一层。

js
const foo = {}

// 为 foo 添加一个属性,可以成功
foo.prop = 123
foo.prop // 123

// 将 foo 指向另一个对象,就会报错
foo = {} // TypeError: "foo" is read-only

对象冻结,应该使用Object.freeze方法,冻结后添加新属性不起作用

js
const foo = Object.freeze({})

// 常规模式时,下面一行不起作用;
// 严格模式时,该行会报错
foo.prop = 123

不同类型值存放情况

普通类型:值保存在变量指向内存地址 引用类型:变量指向存放实际数据的指针

六种声明变量方法 :var、function、let、const、import、class

暂时性死区

ES6 规定,区块中存在letconst命令,区块对let const声明的变量,形成了封闭作用域。声明之前就使用这些变量,就会报错,称为暂时性死区(temporal dead zone)

本质:进入作用域,变量就已经存在了,但是不可获取,只有声明变量的那一行代码之后(tdz 结束),才可以获取和使用该变量。

typeof 不再 百分百安全

js
typeof x // ReferenceError

隐蔽的死区

js
function bar(x = y, y = 2) {
  return [x, y]
}

bar() // 报错
js
var x = x // 不报错
let x = x // 报错

块级作用域

为什么需要块级作用域

在块级作用域出现之前,会出现不合理场景。

第一种场景,内层变量可能会覆盖外层变量。

js
var tmp = new Date()

function f() {
  console.log(tmp)
  if (false) {
    var tmp = 'hello world'
  }
}

f() // undefined

第二种场景,循环变量泄露为全局变量

js
var s = 'hello'

for (var i = 0; i < s.length; i++) {
  console.log(s[i])
}

console.log(i) // 5

块级作用域

ES6 允许块级作用域的任意嵌套,并且每层都有单独作用域

js
{
  {
    {
      {
        let insane = 'Hello World'
        {
          let insane = 'Hello World'
        }
      }
    }
  }
}

块级作用域与函数声明

示例

js
function f() {
  console.log('I am outside!')
}

;(function () {
  if (false) {
    function f() {
      console.log('I am inside!')
    }
  }
  f()
})()

ES5:输出 I am inside!,它将 f 函数 提升到函数顶部

js
;(function () {
  function f() {
    console.log('I am inside!')
  }
  if (false) {
  }
  f()
})()

ES6:报错 Uncaught TypeError: f is not a function。遵循以下三条规则

  • 允许在块级作用域内声明函数。
  • 函数声明类似于var,即会提升到全局作用域或函数作用域的头部。
  • 同时,函数声明还会提升到所在的块级作用域的头部。
js
;(function () {
  var f = undefined
  if (false) {
    function f() {
      console.log('I am inside!')
    }
  }
  f()
})()

ES6 的块级作用域必须有大括号,如果没有大括号,JavaScript 引擎就认为不存在块级作用域。

顶层对象

顶层属性和全局变量挂钩的问题

  1. 无法在编译阶段报出变量未声明错误
  2. 误创全局变量
  3. 顶层对象可读写,不利于模块化
  4. window 有实体意义

从 ES6 开始

varfunction声明的全局变量,依旧是顶层对象的属性

letconstclass声明的全局变量,不属于顶层对象的属性

globalThis 对象

浏览器:顶层对象 window,node 和 Web Worker 没有

浏览器、Web Worker :顶层对象self,node 没有

Node :顶层对象global,其他环境没有

js
// 方法一
typeof window !== 'undefined'
  ? window
  : typeof process === 'object' && typeof require === 'function' && typeof global === 'object'
  ? global
  : this

// 方法二
var getGlobal = function () {
  if (typeof self !== 'undefined') {
    return self
  }
  if (typeof window !== 'undefined') {
    return window
  }
  if (typeof global !== 'undefined') {
    return global
  }
  throw new Error('unable to locate global object')
}

ES2020 引入globalThis作为顶层对象,指向全局环境下的this

相关链接

[-] let 和 const