• 浏览器: Google Chrome 124.0.6367.119 (arm64)
  • Node.js: v20.12.2

混乱的 varfunction

造成混乱原因有:

  • 语言初期设计的缺陷,如自动提升、自动挂载到 globalThis、允许重复声明等
  • 新版本特性和向下兼容的取舍,如 ES6 块级作用域对 function 的影响
  • 兼容模式的自动修正

var 声明的变量

  • 无块级作用域(无视)
  • var 变量提升:
    • 声明:将变量声明提升到全局作用域或函数作用域的头部
    • 赋值:仅提升声明,赋值仍然在原位置,未赋值前值为 undefined
  • 挂载到 globalThis:到了 var 语句才会挂载,相当于 globalThis.a = a
    • 全局作用域:
      • 浏览器:挂载
      • Node.js:不挂载
    • 函数作用域:都不挂载
  • 可重复声明
// 'use strict'
// 严格模式无影响
 
console.log(globalThis.a)
console.log(a)
{
  console.log(globalThis.a)
  console.log(a)
 
  var a = 1
 
  console.log(globalThis.a)
  console.log(a)
}
console.log(globalThis.a)
console.log(a)
 
// ------------- 相当于 -------------
 
var a // !!! var 变量声明提升
 
console.log(globalThis.a) // undefined
console.log(a) // undefined
{ // !!! 无视块级作用域
  console.log(globalThis.a) // undefined
  console.log(a) // undefined
 
  a = 1 // !!! 赋值不提升
  globalThis.a = a // !!! 浏览器在全局作用域下会挂载到 globalThis;Node.js 不会
 
  console.log(globalThis.a) // 浏览器: 1; Node.js: undefined
  console.log(a) // 1
}
console.log(globalThis.a) // 浏览器: 1; Node.js: undefined
console.log(a) // 1
// 'use strict'
// 严格模式无影响
 
;(function () {
  console.log(globalThis.a)
  console.log(a)
  {
    console.log(globalThis.a)
    console.log(a)
 
    var a = 1
 
    console.log(globalThis.a)
    console.log(a)
  }
  console.log(globalThis.a)
  console.log(a)
})()
 
console.log(globalThis.a)
console.log(a)
 
// ------------- 相当于 -------------
 
;(function () {
  var a // !!! var 变量声明提升
 
  console.log(globalThis.a) // undefined
  console.log(a) // undefined
 
  { // !!! 无视块级作用域
    console.log(globalThis.a) // undefined
    console.log(a) // undefined
 
    a = 1 // !!! 赋值不提升
    // !!! 浏览器和 Node.js 在函数作用域下都不会挂载到 globalThis
 
    console.log(globalThis.a) // undefined
    console.log(a) // 1
  }
  console.log(globalThis.a) // undefined
  console.log(a) // 1
})()
 
console.log(globalThis.a) // undefined
console.log(a) // ReferenceError: a is not defined

函数声明

  • 考虑块级作用域:
    • 允许在块级作用域内声明函数
    • 非严格模式:影响函数赋值提升
    • 严格模式:有块级作用域
  • 函数提升:
    • 非严格模式:
      • 声明:提升到全局作用域或函数作用域的头部,值为 undefined
      • 赋值:提升到最近的块级作用域的头部(无块级作用域则提升到全局作用域或函数作用域的头部),值为相应函数
    • 严格模式:声明和赋值都提升到最近的块级作用域的头部
  • 挂载到 globalThis:到了 function 语句才会挂载,相当于 globalThis.a = a
    • 全局作用域:
      • 浏览器:
        • 非严格模式:挂载
        • 严格模式:不挂载
      • Node.js:不挂载
    • 函数作用域:都不挂载
  • 可重复声明
// 'use strict'
 
console.log(globalThis.a)
console.log(a)
{
  console.log(globalThis.a)
  console.log(a)
 
  function a() {}
 
  // do something
 
  function a() { 1 }
 
  console.log(globalThis.a)
  console.log(a)
}
console.log(globalThis.a)
console.log(a)
 
// ------------- 相当于 -------------
 
var a // !!! 非严格模式:函数声明提升到全局作用域或函数作用域
 
console.log(globalThis.a) // undefined
console.log(a) // undefined
 
{
  块内a = function a() {} // !!! 非严格模式:函数赋值提升到最近的块级作用域
  块内a = function a() { 1 } // !!! 重复声明
 
  console.log(globalThis.a) // undefined
  console.log(a) // [Function: a{1}]
 
  全局a = 块内a
  globalThis.a = a // !!! 非严格模式:浏览器在全局作用域下挂载到 globalThis; Node.js 不会
 
  // do something
 
  全局a = 块内a
  globalThis.a = a // !!! 遇到 function 或 var 就会判断是否需要挂载:是否全局作用域、是否严格模式、浏览器环境
 
  console.log(globalThis.a) // 浏览器: [Function: a{1}], Node.js: undefined
  console.log(a) // [Function: a{1}]
}
console.log(globalThis.a) // 浏览器: [Function: a{1}], Node.js: undefined
console.log(a) // [Function: a{1}]
'use strict'
 
console.log(globalThis.a)
console.log(a)
{
  console.log(globalThis.a)
  console.log(a)
 
  function a() {}
 
  console.log(globalThis.a)
  console.log(a)
}
console.log(globalThis.a)
console.log(a)
 
// ------------- 相当于 -------------
 
'use strict'
 
console.log(globalThis.a) // undefined
console.log(a) // ReferenceError: a is not defined
{
  function a() {} // !!! 严格模式:函数声明和赋值都提升到最近的块级作用域
 
  console.log(globalThis.a) // undefined
  console.log(a) // [Function: a]
 
  // !!! 严格模式:浏览器和 Node.js 在全局作用域下都不挂载到 globalThis
 
  console.log(globalThis.a) // undefined
  console.log(a) // [Function: a]
}
console.log(globalThis.a) // undefined
// !!! 严格模式:函数有块级作用域
console.log(a) // ReferenceError: a is not defined

未声明而直接赋值的变量

  • 修正
    • 非严格模式:修正为 globalThis.a = 1,即:在函数作用域下也会挂载到 globalThis
    • 严格模式:不修正,访问未声明变量直接报错
  • 无声明提升
    • 毕竟不是修正为 var a = 1,而是 globalThis.a = 1
'use strict'
 
// !!! 严格模式不修正,访问未声明变量直接报错
a = 1 // ReferenceError: a is not defined
// 'use strict'
 
console.log(globalThis.a) // undefined
console.log(a) // ReferenceError: a is not defined
{
  console.log(globalThis.a) // undefined
  console.log(a) // ReferenceError: a is not defined
 
  a = 1 // !!! 修正为 globalThis.a = 1
 
  console.log(globalThis.a) // 1
  // !!! 相当于省略了 globalThis.
  console.log(a) // 1
}
console.log(globalThis.a) // 1
// !!! 相当于省略了 globalThis.
console.log(a) // 1

题目

// 'use strict'
 
console.log(globalThis.a)
console.log(a)
{
  console.log(globalThis.a)
  console.log(a)
 
  a = 1
 
  console.log(globalThis.a)
  console.log(a)
 
  function a() {}
 
  console.log(globalThis.a)
  console.log(a)
}
console.log(globalThis.a)
console.log(a)
 
// ------------- 相当于 -------------
 
var a // !!! 函数声明提升
 
console.log(globalThis.a) // undefined
console.log(a) // undefined
{
  块内a = function a() {} // !!! 函数赋值提升
 
  console.log(globalThis.a) // undefined
  console.log(a) // [Function: a]
 
  块内a = 1 // !!! 这里的 a 已经声明过了,而不是未声明而直接赋值
 
  console.log(globalThis.a) // undefined
  console.log(a) // 1
 
  全局a = 块内a
  globalThis.a = a // !!! 判断是否需要挂载
 
  console.log(globalThis.a) // 浏览器: 1, Node.js: undefined
  console.log(a) // 1
}
console.log(globalThis.a) // 浏览器: 1, Node.js: undefined
console.log(a) // 1
// 'use strict'
 
console.log(globalThis.a)
console.log(a)
{
  console.log(globalThis.a)
  console.log(a)
 
  function a() {}
 
  console.log(globalThis.a)
  console.log(a)
 
  a = 1
 
  console.log(globalThis.a)
  console.log(a)
}
console.log(globalThis.a)
console.log(a)
 
// ------------- 相当于 -------------
 
var a // !!! 函数声明提升
 
console.log(globalThis.a) // undefined
console.log(a) // undefined
{
  块内a = function a() {} // !!! 函数赋值提升
 
  console.log(globalThis.a) // undefined
  console.log(a) // [Function: a]
 
  全局a = 块内a
  globalThis.a = a // !!! 判断是否需要挂载
 
  console.log(globalThis.a) // 浏览器: [Function: a], Node.js: undefined
  console.log(a) // [Function: a]
 
  块内a = 1 // !!! 这里的 a 已经声明过了,而不是未声明而直接赋值
 
  console.log(globalThis.a) // 浏览器: [Function: a], Node.js: undefined
  console.log(a) // 1
}
console.log(globalThis.a) // 浏览器: [Function: a], Node.js: undefined
console.log(a) // [Function: a]
// 'use strict'
 
console.log(globalThis.a)
console.log(a)
{
  console.log(globalThis.a)
  console.log(a)
 
  function a() {}
 
  console.log(globalThis.a)
  console.log(a)
 
  a = 1
 
  function a() { 1 }
 
  console.log(globalThis.a)
  console.log(a)
 
  console.log(globalThis.a)
  console.log(a)
}
console.log(globalThis.a)
console.log(a)
 
// ------------- 相当于 -------------
 
var a // !!! 函数声明提升
 
console.log(globalThis.a) // undefined
console.log(a) // undefined
{
  块内a = function a() {}
  块内a = function a() { 1 } // !!! 函数赋值提升
 
  console.log(globalThis.a) // undefined
  console.log(a) // [Function: a{1}]
 
  全局a = 块内a
  globalThis.a = a // !!! 判断是否需要挂载
 
  console.log(globalThis.a) // 浏览器: [Function: a{1}], Node.js: undefined
  console.log(a) // [Function: a{1}]
 
  块内a = 1 // !!! 这里的 a 已经声明过了,而不是未声明而直接赋值
 
  全局a = 块内a
  globalThis.a = a // !!! 判断是否需要挂载
 
  console.log(globalThis.a) // 浏览器: 1, Node.js: undefined
  console.log(a) // 1
 
  console.log(globalThis.a) // 浏览器: 1, Node.js: undefined
  console.log(a) // 1
}
console.log(globalThis.a) // 浏览器: 1, Node.js: undefined
console.log(a) // 1

正常的 letconstclass

  • 有块级作用域
  • 无变量提升,有暂时性死区
    • ReferenceError: Cannot access 'a' before initialization
  • 不挂载到 globalThis
  • 同一作用域下,不可重复声明
  • 重新赋值
    • letclass:可以
    • const
      • 不可以,TypeError: Assignment to constant variable.
      • 声明时就要赋值,SyntaxError: Missing initializer in const declaration

箭头函数

function 函数的区别:

  1. this 指向定义环境上下文
  2. 不能作为构造函数
  3. 无原型对象和原型链
  4. arguments
  5. 不能作为 GeneratorFunction

箭头函数是“纯”函数,function 是函数 + 构造器

启示

  • 声明变量使用 let,声明常量使用 const,不要使用 var
  • 函数使用箭头函数,构造器使用 class
  • function 仍可以使用,因为箭头函数只能先声明后使用,而 function 有函数提升且严格模式支持块级作用域