javascript 变量提升 函数有吗 怎么用?

1.JS代码执行顺序

我们直觉上会认为JS的代码在执行时是由上到下一行一行执行的,但实际并不完全正确,下面的例子会证明:

a = 'haha'
var a
console.log(a)

 

上面的代码会输出什么呢?

如果按照我们认为的由上到下一行一行执行,那么应该输出undefined,但是实际结果是'haha'

接着再看一个代码:

console.log(a)
var a = 'haha'

 

 

那这个输出的是什么?

鉴于上面代码表现出来的非自上而下的特点,有可能认为是’haha’。或者有认为变量a没有声明,所以会报错,但实际结果是undefined

为什么会是这样呢?到底发生什么?让我们带着这个问题看下去

2.变量提升

2.1 编译

我们要知道,引擎在解释JS代码之前首先要对代码进行编译,在编译阶段中有一部分工作就是找到所有的声明,并用合适的作用域将他们关联起来。

JS执行的流程图大致如下:
在这里插入图片描述

所以总结来说,包括变量和函数在内的所有声明在编译阶段都会首先被处理,然后才是代码被执行的阶段。

当我们看到var a = 'haha'时候认为它是一个声明,但其实在JS中它会被看做两个声明,var aa = 'haha'var a是定义声明,是在编译阶段进行;a = 'haha'是赋值声明,会留在原地等待执行阶段。

2.2 解释上面问题

接着我们再看回第一个例子:

a = 'haha'
var a
console.log(a)

 

 

通过上面说明,可以知道会先处理定义声明,所以整个代码会以如下形式进行处理:

var a //在编译阶段进行变量提升
a = 'haha'
console.log(a)

 

 

第二个例子也是相同处理

console.log(a)
var a = 'haha'

 

 

在这个例子中也会进行变量提升,所以整个代码会以如下形式进行处理:

var a 
console.log(a)
a = 'haha'

 

3.函数提升

3.1 基础用法

看完变量提升,再看一下函数声明如何进行提升

foo()
function foo() {
  console.log('haha')
}

 

 

看完上面的例子,我想大家也能猜到了结果,就是’haha’。
因为foo函数的声明被提升了,所以第一行中的调用可以正常执行。

3.2 作用域提升

接下来再看一个例子

foo()
function foo() {
  console.log(a)
  var a = 'haha'
}

 

 

这个会输出什么呢?
答案是:undefined

在这个例子中,不只有函数提升,还有变量提升。要注意的是,每个作用域都会进行提升操作,所以foo()函数自身也会在内部对var a进行提升,但是只能在这个作用域中进行提升,并不能提升到整个代码的最上方。

因此这段代码会以如下形式进行处理:

function foo() {
  var a
  console.log(a)
  a = 'haha'
}
foo()

 

 

3.3 函数表达式

foo()
var foo = function bar() {
  console.log('haha')
}

 

 

这个代码执行结果是:TypeError: foo is not a function

这是因为变量标识foo被提升并分配给所在作用域,所以不会出现ReferenceError的错误,但是foo此时没有赋值,也就是此时foo是undefined,所以执行foo()是对undefined值进行函数调用,因此结果为TypeError。

这个例子证明了:函数声明会被提升,但是函数表达式不会被提升

3.4 具名函数表达式

foo()
bar()
var foo = function bar() {
  console.log('haha')
}

 

 

从上面我们知道,执行foo()会是typeError,那执行bara()呢?
答案是:ReferenceError: bar is not defined

这是因为:即使是具名的函数表达式,名称标识符在赋值之前也无法在所在作用域中使用。

因此这段代码会以如下形式进行处理:

var foo

 foo() //typeError
 bar() //ReferenceError
 
 foo = function bar() {
  console.log('haha')
 }

 

 

4. 函数优先

函数声明和变量声明都会提升,但是在处理声明中是 函数首先被提升,然后才是变量。

foo()
var foo 
function foo() {
    console.log(1)   
}
foo = function () {
    console.log(2)
}

 

 

这个例子的结果是1。
这个代码会以近似如下形式进行处理:

function foo() {
    console.log(1)   
}
foo()
foo = function () {
    console.log(2)
}

 

 

注意:var foo虽然会出现在foo()之前,但是因为重复的声明,所以被忽略了。

5.实战练习

经过上面说明,应该对变量提升有了大致了解,那么让我们再看几个例子,看看是否真正理解

5.1 例子1:

var getName = function () { 
    console.log('4');
};
function getName() { 
    console.log(5);
}
getName();    

 

 

想想这个结果是什么?

让我们分析一下这个提升过程,这个代码会以近似如下形式进行处理:

function getName() { 
    console.log(5);
}

var getName //被忽略

getName = function () { 
    console.log('4');
};

getName(); 

 

 

  1. getName函数和变量getName被提升
  2. 然后var getName因为重复声明被忽略
  3. 最后函数表达式getName会覆盖函数getName

所以最后实际执行的是函数表达式getName = function () { console.log('4'); };,结果为’4’

5.2 例子2

function showName() {
  console.log('name1');
}
showName();
function showName() {
  console.log('name2');
}
showName(); 

 

 

想想这个结果是什么?

有了上面经验,这个就更好理解了,这个代码会以近似如下形式进行处理:

function showName() {
  console.log('name1');
}
function showName() {
  console.log('name2');
}
showName();
showName();

 

所以结果一目了然,结果为 name2 name2

6.总结

通过上面的内容,我们可以进行一个简单的总结:

  1. 变量提升,是指在 JavaScript 代码执行过程中,JavaScript 引擎把变量的声明部分和函数的声明部分提升到代码开头的“行为”。变量被提升后,会给变量设置默认值,这个默认值就是我们熟悉的 undefined。
  2. JS的代码执行顺序是先进行声明处理,然后进行赋值等其他操作。
  3. 提升的过程就像是把变量和函数声明从他们在代码中出现的位置“移动”到了最上面。另外只有定义的声明本身会被提升,而赋值或其他运行逻辑会留在原地
  4. 函数声明和变量声明在一起时,函数首先被提升,然后才是变量。
posted @ 2020-12-30 20:21  人心不古  阅读(96)  评论(0编辑  收藏  举报