变量的声明赋值
var a = 1;
上面的代码先声明变量 a
,然后在变量 a
与数值 1
之间建立引用关系,称为将数值 1
“赋值”给变量 a
。以后,引用变量名 a
就会得到数值 1
。最前面的 var
,是变量声明命令。它表示通知解释引擎,要创建一个变量 a
。
- 实际变量的声明和赋值,是分开的两个步骤,如下
var a;
a = 1;
- 如果只是声明变量而没有赋值,则该变量的值是
undefined
。undefined
是一个特殊的值,表示“无定义”。
var a;
a // undefined
- 没有写
var
去声明变量,不写var
的做法,会创建全局变量,a = 1
等同于window.a = 1
var a = 1;
// 基本等同
a = 1;
多次声明,后者会覆盖前者
什么是变量提升
直觉上会认为 JavaScript
代码在执行时是由上到下一行一行执行的。但实际上这并不完全正确,有一种特殊情况会导致这个假设是错误的。
示例如下
- 考虑以下代码:
a = 2;
var a;
console.log(a);
console.log(..)
声明会输出什么呢?
很多人会认为是
undefined
,因为var a
声明在a = 2
之后,他们自然而然地认为变量被重新赋值了,因此会被赋予默认值undefined
。但是,真正的输出结果是2
。
- 考虑另外一段代码:
console.log(a);
var a = 2;
你可能会认为这个代码片段也会有同样的行为而输出
2
。还有人可能会认为,由于变量a
在使用前没有先进行声明, 因此会抛出ReferenceError
异常。不幸的是两种猜测都是不对的。输出来的会是undefined
。
为什么会出现这种情况?
JavaScript
引擎的工作方式是,先解析代码,获取所有被声明的变量,然后再一行一行地运行。
这造成的结果,就是所有的变量的声明语句,都会被提升到代码的头部,而赋值或其他运行逻辑会留在原地,这就叫做变量提升
包括变量和函数在内的所有声明都会在任何代码被执行前首先被处理,这种现象称为提升。
变量声明提升:
JavaScript
的变量提升是针对var
的,而let
和const
不存在变量提升这一特性(let
与const
具有一个临时死区的概念)- 通过
var
定义的变量,在定义语句之前就可以访问到 值:undefined
- 变量提升就是变量会被提升到作用域的最顶上去,也就是该变量不管是在作用域的哪个地方声明的,都会提升到作用域的最顶上去。
JS
解释器会找出需要提升的变量和函数,并且给他们提前在内存中开辟好空间,函数的话会将整个函数存入内存中,变量只声明并且赋值为undefined
,- 如果变量一直都没有声明过,则会抛出
ReferenceError
,比如直接输出:console.log(b) // Uncaught ReferenceError: b is not defined
- 当你看到
var a = 2
; 时,可能会认为这是一个声明。但JavaScript
实际上会将其看成两个声明:var a;
和a = 2
;。- 第一个定义声明是在编译阶段进行的。第二个赋值声明会被留在原地等待执行阶段
- 处理如下
a = 2;
var a;
console.log(a);
// 上面这段代码会被 js 处理成如下代码
var a;
a = 2;
console.log(a);
函数声明提升
JavaScript
引擎将函数名视同变量名,所以采用function
命令声明函数时,整个代码块会提升到它所在的作用域的最开始执行- 通过
function
声明的函数,在之前就可以直接调用 - 函数提升只会提升函数声明,而不会提升函数表达式。
fx() // fx is a great girl 之前之后都可调用
function fx () {
console.log('fx is a great girl')
}
fx() // fx is a great girl 之前之后都可调用
- 函数表达式声明的函数
console.log(fx) // undefined
var fx = function () {
console.log('fx is a great girl')
}
// ======================
fx() // 不是 ReferenceError, 而是 TypeErr
var fx = function () {
console.log('fx is a great girl')
}
这段程序中的变量标识符
fx()
被提升并分配给所在作用域(在这里是全局作用域),因此fx()
不会导致ReferenceError
。
但是fx
此时并没有赋值(如果它是一个函数声明而不是函数表达式,那么就会赋值)。fx()
由于对undefined
值进行函数调用而导致非法操作, 因此抛出TypeError
异常。
函数声明和变量声明使用同一个变量名称
函数的优先级高于变量的优先级
// 会输出fx定义的函数
console.log(fx)
function fx () {
console.log('fx is a great girl')
}
var fx = 'fx'
console.log(fx) // fx
多个同名函数声明
由最后面的函数声明来替代前面的
fx() // fx is a great girl
function fx () {
console.log('fx')
}
function fx () {
console.log('fx is a great girl')
}
示例
示例一
每个作用域都会进行提升操作
var a = 100
function fn () {
console.log(a)
var a = 200
console.log(a)
}
fn()
console.log(a)
var a
console.log(a)
var a = 300
console.log(a)
这段代码将会依次输出 undefined 200 100 100 300
在
fn()
函数中由于声明了var a = 200
, 所以var a
会被提升到fn
的作用域顶端,第一输出则为undefined
示例二
下面这段代码,由于 es6
之前,js
是没有块级作用域的,所以 if
中声明的 a
变量会被当成全局变量处理
var a = 1
if (true) {
var a = 2
}
console.log(a) // 2
示例三
var a = 10
function fx () {
console.log(a) // undefined
var a = 20
console.log(a) // 20
}
fx()
console.log(a) // 10
// ===
var a = 10
function fx () {
console.log(a) // 10
a = 20
console.log(a) // 20
}
fx()
console.log(a) // 20
第二段代码
fx()
中的a
没有使用var
定义,会造成fx
函数中没有变量声明,所以fx
里面访问的变量a
,其实都是访问的全局变量a
,a = 20
又相当于给全局变量a
重新赋值20
示例四
即使 if
语句的条件是 false
,也一样不影响 a
变量提升
function fx () {
console.log(a) // undefined
if (false) {
var a = 1
}
console.log(a) // undefined
console.log(b) // Uncaught ReferenceError: b is not defined
}
fx()
示例五
function fx () {
console.log('fx is a great girl')
}
var fx
console.log(typeof fx) // function
// =======================
function fx () {
console.log('fx is a great girl')
}
var fx = 'good girl'
console.log(typeof fx) // string
// =======================
console.log(typeof fx) // function fx(){}函数提升
var fx = 'good girl'
function fx () {
console.log('fx is a great girl')
}
console.log(typeof fx) // string
// =======================
console.log(typeof fx) // function
var fx
function fx () {
console.log('fx is a great girl')
}
console.log(typeof fx) // function
示例六
if(!(fx in window)) {
var fx = 1
}
console.log(fx) // undefined
示例七
var c = 1
function c(c) {
console.log(c)
}
c(2) // c is not a function
// ========
c(2) // 2
var c = 1
function c(c) {
console.log(c)
}
console.log(c) // 1
// ========
var c = function(c) {
console.log(c)
}
c(2) // 2
var c = 1
// ========
var c = function(c) {
console.log(c)
}
var c = 1
c(2) // Uncaught TypeError: c is not a function
// ========
c(2) // Uncaught TypeError: c is not a function
var c = function(c) {
console.log(c)
}
var c = 1