js 变量、函数重复声明和变量提升浅析

第一阶段:看山是山,看水是水

先从简单的例子开始

// 示例1
var a = 1
console.log(a) // 1
var a = 2
console.log(a) // 2

通过两个var声明了两次a变量,结果看起来像是后声明同名变量会覆盖之前的声明,是这样吗?

再看这个

// 示例2
a = 1
console.log(a) // 1
var a
console.log(a) // 1

如果是覆盖,a应该是undefined才对,所以不应是覆盖,那为什么第一个例子中会输出1和2呢?

 

第二个阶段:看山不是山,看水不是水

// 示例3
console.log(a) //undefined
var a = 1
console.log(a) // 1

可见,在a声明之前打印a是undefined,我们知道在使用一个变量时必须声明,不然后报错,如

console.log(a)

界面会直接白屏报错,如下:

js 变量、函数重复声明和变量提升浅析_第1张图片

综合第二阶段的两个例子,我们发现,在js中会发生变量的提升现象(hoisting),即可以在变量使用之后才去声明一个变量,那在js中,到底是怎么处理声明的呢?

 

第三阶段:看山还是山,看水还是水

带着这个疑问,去翻阅js相关书籍文档,原来浏览器拿到一段js代码时不是立即逐行执行,而是分为编译和执行两个步骤

  • 编译阶段会将这段代码中需要声明的变量存入当前作用域中(本篇只讨论变量的提升,作用域等相关知识自行查阅)
  • 执行阶段会逐行执行这段代码

所以编译阶段的存在造成了这种变量提升的现象,明白了这一点,我们再回头看上面的几个例子,可以类似的转换成

// 示例1
var a
var a // 重复声明会被忽视
a = 1
console.log(a) // 1
a = 2
console.log(a) // 2


// 示例2
var a 
a = 1
console.log(a) // 1
console.log(a) // 1


// 示例3
var a 
console.log(a) //undefined
a = 1
console.log(a) // 1

有的同学会将变量提升和变量赋值搞混,js中,变量提升指的是变量声明的提升,赋值还是按照代码中的顺序逐行执行,如示例1中所示。

到此我们对变量提升有了一定的了解,那函数的提升呢?

// 示例4
fn(10) // 10

function fn (a) {
    console.log(a)
}

由此可见,函数的声明也会有提升的现象,与变量提升一致,也是在编译阶段处理声明。

定义一个函数有两种常用方式,一种是函数式声明,一种是表达式声明

// 函数式声明
function fn1(a) {
    console.log(a)
}

// 表达式式声明
var fn2 = function(a) {
    console.log(a)
}

通过示例4我们知道函数式声明可以函数提升,那表达式声明呢?

console.log(test) // undefined

test(10) // TypeError: test is not a function
var test = function(a) {
    console.log(a)
}

测试发现会报错

因为test在实际声明之前是undefined(编译之后的结果),undefined当然不能作为一个函数调用,就会报错,这就是两种声明方式的区别之一。表达式声明理论上是一个变量声明,只不过是把一个函数赋值给声明的变量而不是基本类型值。

以上就是变量以及函数的声明提升,那么变量重复定义时会发生什么呢,特别是同一变量不同类型的声明,如下:

// 重复声明1
console.log(a) // function a() {console.log(123)}
var a
console.log(a) // function a() {console.log(123)}
function a() {
    console.log(123)
}
console.log(a) // function a() {console.log(123)}

// 重复声明2
console.log(a) // function a() {console.log(123)}
function a() {
    console.log(123)
}
console.log(a) // function a() {console.log(123)}
var a
console.log(a) // function a() {console.log(123)}

可见,函数声明会优先于变量声明,以上代码可转换为

// 重复声明1以及2转换
function a() {
    console.log(123)
}
var a // 重复a的声明,被忽略
console.log(a) // function a() {console.log(123)}
console.log(a) // function a() {console.log(123)}
console.log(a) // function a() {console.log(123)}

以上就是本篇的核心内容,下面通过几个示例考验一下大家的理解

// 测试题 
function testa (a) {
    console.log(a)
    var a = 'hello'
    console.log(a)
    function a () {
        console.log(null)
    }
    console.log(a)
}
function testb (b) {
    console.log(b)
    function b() {
        console.log(b)
    }
    b()
}
function testc (c) {
    console.log(c)
    c = function () {
        console.log(c)
    }
    console.log(c)
    c()
}

testa(null)
testb(10)
testc()

如果以上测试题能够准确清晰的得出结果,那么恭喜你,已经掌握了关于变量提升和函数提升的相关知识。

你可能感兴趣的:(js 变量、函数重复声明和变量提升浅析)