zoukankan      html  css  js  c++  java
  • 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. 函数声明和变量声明在一起时,函数首先被提升,然后才是变量。
  • 相关阅读:
    括号匹配的检验
    学习过程中遇到的比较有价值的博客--整理
    Spring+SpringMVC+MyBatis的pom.xml依赖
    Hibernate内容详解
    Struts2的拦截器配置
    Maven构建Struts2项目
    Mybatis增删改查,Demo整合
    简单Java类 全网最详细讲解 !!!
    Javaoop 遇到的问题
    Bootstrap 前端框架 遇到的问题 解决方案
  • 原文地址:https://www.cnblogs.com/yzy521/p/14213324.html
Copyright © 2011-2022 走看看