JS 里存在两类块
,一种是 block {}
,另一种是 function body (){}
(包括 method),这两种块对不同的变量声明会有不同的作用域效果,下面就来看看。
var
if (false) {
var a = 1
}
console.log(a) // undefined
var
是一种历史悠久的变量,他不认识 block {}
,会直接提升到第一个 function body 区,即使是 Dead-code 也不例外。
let
, const
let a = 1
if (true) {
const a = 2
}
console.log(a) // 1
let
和 const
严格存在于当前 block 内,不会发生任何变量提升。
function
if (false) {
function foo() {}
}
console.log(foo) // undefined
function
也是一种定义变量的方式,他和 var
一样悠久,提升规则和 var
几乎一样,但有两点不同:
const a = 1
if (false) {
var a = 2 // SyntaxError: Identifier 'a' has already been declared
}
const foo = 1
if (true) {
function foo() {}
}
console.log(foo) // 1
var
会不择手段地提升,碰到 const
, let
之类硬茬就会爆炸;而 function
则温和很多。console.log(a, foo()) // undefined, 2
var a = 1
function foo() {
return 2
}
var
只送一个 undefined
不同,function
会连着定义一起送出去,我们可以 看起来 在定义之前就使用他。实际上,可以认为 function
连着内容一起被提升到了当前函数块的顶部执行。另外,当整个 function
作为表达式而不是语句存在时,其名称只会被限定在函数体内可以访问:
$button.onclick = function hello() {
console.log(hello.name) // 'hello'
}
console.log(hello) // ReferenceError: hello is not defined
class
const A = 1
if (true) {
class A {}
A = 2
}
console.log(A) // 1
class
创造的变量约等于 let
,不过需要注意的是,在 class
的静态表达式中是可以直接访问到其类名的,而 let
需要先执行完 class
,反而不能这么使用:
class A {
static a = A.name // 'A'
static b = this.name // can also use `this`
}
let A = class {
static a = A.name // ReferenceError: A is not defined
static b = this.name // 'A'
}
注意到最后一个例子,A.b
可以拿到 'A'
,这是因为匿名类会被自动赋予左边的变量名。当左边不是一个变量的时候该类的名字就是空了:
console.log(
class {
static b = this.name
}.b,
) // ''