- 浏览器: Google Chrome 124.0.6367.119 (arm64)
- Node.js: v20.12.2
混乱的 var
和 function
造成混乱原因有:
- 语言初期设计的缺陷,如自动提升、自动挂载到
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
正常的 let
、const
、class
- 有块级作用域
- 无变量提升,有暂时性死区
ReferenceError: Cannot access 'a' before initialization
- 不挂载到
globalThis
- 同一作用域下,不可重复声明
- 重新赋值
let
、class
:可以const
:- 不可以,
TypeError: Assignment to constant variable.
- 声明时就要赋值,
SyntaxError: Missing initializer in const declaration
- 不可以,
箭头函数
与 function
函数的区别:
this
指向定义环境上下文- 不能作为构造函数
- 无原型对象和原型链
- 无
arguments
- 不能作为 GeneratorFunction
箭头函数是“纯”函数,function
是函数 + 构造器
启示
- 声明变量使用
let
,声明常量使用const
,不要使用var
- 函数使用箭头函数,构造器使用
class
- 但
function
仍可以使用,因为箭头函数只能先声明后使用,而function
有函数提升且严格模式支持块级作用域