0
点赞
收藏
分享

微信扫一扫

前端学习:预解析

念川LNSC 2022-02-25 阅读 27

预解析

预解析: 在所有代码开始执行之前, 对代码进行通读并解释, 解释完毕以后再开始执行代码

函数调用问题:

  • 在函数定义的时候, 被装进 "盒子" 内的代码是不会执行的

  • 在函数调用的时候, 代码才会执行

预解析有两部分:

  • 全局预解析:打开页面的时候,会对全局代码进行预解析,但函数体内的代码不管

  • 局部预解析:当你函数调用的时候,会在函数的私有作用域下进行预解析,解析完毕执行函数体内代码

预解析都解释哪些内容:

  • var

  • 声明式函数

var的预解析:

  • 向浏览器内存声明, 有一个变量被定义了, 但是没有赋值

  • 赋值操作是在代码执行阶段才会执行的

 console.log(num)
 var num    //输出undefined

分析:

当你使用一个变量的时候

  • 如果报错: xxx is not defined, 说明这个变量没有定义

  • 如果出现 undefined 说明这个变量定义过, 但是没有赋值

 console.log(num)
    包含两个操作
    1. var num
    2. num = 100
    var num = 100

代码:

   console.log(num)
​
  var num = 100
 
打开浏览器后预解析:
​
•     第 1 行 代码, 不需要预解析
​
•     第 2 行 代码, 有 var 关键字
​
•     进行预解析, var num 在浏览器声明了一个叫做 num 的变量, 但是此时不进行赋值
​
 开始执行代码:
   第 1 行 代码, console.log(num) 访问 num 这个变量
   因为预解析的时候已经声明了 num 变量
   所以此时是 有变量, 但是没有值
   所以出现 undefined
   
    第 2 行 代码, 因为 var num 已经在预解析执行过了
    这里只有 num = 100 这句代码了
    给 num 变量进行赋值
    
      console.log(num)
      var num = 100
        等价于
        var num
        console.log(num)
        num = 100

​
console.log(num) //undefined
console.log(num2) //报错,var num2 = 200 不参加预解析,函数内不预解析
var num = 100
function fn(){
    var num2 = 200 
}
console.log(num)//undefined
if(false){
    var num = 100
}
 console.log(num)//undefined,folse不执行了

预解析 - function

声明式函数会进行预解析

当代码发现声明式函数的时候=> 会在浏览器内存中声明变量名(函数名), 并且被赋值为一个函数

赋值式函数(函数表达式)=> 按照 var 的规则执行

 fn()
    function fn() {
      console.log('hello world')
    }
    fn()
       /*
      1  fn()
      2  function fn() { console.log('hello world') }
      3  fn()
​
      打开浏览器
        + 预解析:
          => 第 1 行 不需要
          => 第 2 行 需要
            -> 在浏览器内声明一个叫做 fn 的变量名, 并且赋值为一个函数 function fn() { console.log('hello world') }
          => 第 3 行 不需要
          => 预解析结束
            -> 此时浏览器内有一个叫做 fn 的函数
​
        + 代码执行
          => 第 1 行, fn()
            -> 把 fn 当做一个函数调用一次
            -> 因为预解析的时候, 已经声明了 fn 变量, 并且赋值为一个函数
            -> 所以这里正常调用
          => 第 2 行, 因为在预解析已经定义过, 直接跳过
          => 第 3 行, fn()
            -> 同 第 1 行
​
      1  fn()
      2  function fn() { console.log('hello world') }
      3  fn()
​
      等价于
​
      1  function fn() { console.log('hello world') }
      2  fn()
      3  fn()
    */
 //fn()
var fn = function () { console.log('你好 世界') }
    fn()
​
    /*
      1  fn()
      2  var fn = function () { console.log('你好 世界') }
      3  fn()
​
      打开浏览器
        + 预解析:
          => 第 1 行, 不需要
          => 第 2 行, 需要
            -> 告诉浏览器定义了一个叫做 fn 的变量, 但是不赋值
          => 第 3 行, 不需要
          => 在预解析结束的时候
            -> 浏览器内只有一个叫做 fn 的变量, 但是还没有赋值也就是 undefined
        + 代码执行
          => 第 1 行, fn()
            -> 把 fn 变量当做一个函数来调用一次
            -> 因为预解析的时候, fn 只是一个 undefined
            -> 这里就是把 undefined 当做一个函数来调用
            -> 报错: xxx is not a function
​
      1  fn()
      2  var fn = function () { console.log('你好 世界') }
      3  fn()
​
      等价于
      1  var fn
      2  fn()
      3  fn = function () { console.log('你好 世界') }
      4  fn()
    */

预解析的重名问题:

当代码中 函数名 和 变量名 重名时候

  • 以函数为准, 仅仅只是在预解析中

  • 不要把函数名和变量名重名

fn()
 function fn (){console.log('fn 函数') }
 fn()
 var fn = 100
 fn()
/* 这时候fn是一个数值了,100把函数覆盖了,把一个数值当做函数来调用,报错 fn is not a function */
 /*
      1  fn()
      2  function fn() { console.log('fn 函数') }
      3  fn()
      4  var fn = 100
      5  fn()
​
      打开浏览器
        + 预解析
          => 第 2 行, 在浏览器声明了一个叫做 fn 的变量, 并且赋值为一个函数
          => 第 4 行, 在浏览器声明了一个叫做 fn 的变量, 不赋值
          => 在预解析过程中, 变量 和 函数 重名, 以函数为准
            -> 在预解析结束的时候. 浏览器内存中只有一个 fn 变量, 保存的是函数
        + 代码执行
          => 第 1 行, fn()
            -> 因为预解析的时候, fn 就是一个函数
            -> 正常调用
          => 第 2 行, 预解析已经定义了, 直接跳过
          => 第 3 行, fn()
            -> 和 第 1 行 一样
          => 第 4 行, var fn 已经在预解析执行过了
            -> 此时剩下 fn = 100
            -> 给 fn 变量从新赋值, 100 就把 函数覆盖了
          => 第 5 行, fn() 
          
            -> 把一个 数值 当做函数来调用
            -> 报错: fn is not a function
    */
fn()
    var fn = 100
    fn()
    function fn() { console.log('fn 函数') }
    fn()
     /*
      1  fn()
      2  var fn = 100
      3  fn()
      4  function fn() { console.log('fn 函数') }
      5  fn()

      打开浏览器
        + 预解析
          => 第 2 行, 在浏览器声明一个叫做 fn 的变量, 不赋值
          => 第 4 行, 在浏览器声明一个叫做 fn 的变量, 并赋值为一个函数
          => 在预解析过程中, 函数 和 变量 重名, 以函数为准
          => 此时 浏览器中只有一个 fn 变量, 值是一个函数
        + 代码执行
          => 第 1 行, fn()
            -> 因为预解析的时候, fn 就是一个函数, 正常调用
          => 第 2 行, var fn 预解析已经执行过
            -> 此时剩下 fn = 100
            -> 给 fn 从新赋值为 100, 覆盖掉本身保存的函数
          => 第 3 行, fn()
            -> 把 一个数值类型 当做函数来调用
            -> 报错: fn is not a function
    */

函数内的预解析

预解析教会了我们什么

  1. 函数名和变量名不要重名

  2. 不管是函数还是变量, 进行先声明后使用

  3. 在函数内, 不要定义和形参一样的函数名

函数的两个阶段都做了什么

函数定义阶段

  • 在堆内存中开辟一段存储空间

  • 把函数体内的代码当做 字符串 放在开辟出来的存储空间内

  • 把函数名放在栈内存中, 把堆内存中的空间地址赋值给栈内存的变量

函数调用阶段

  • 按照栈内存中变量存储的地址找到函数的存储空间

    -> 如果这个空间不是一个函数存储空间, 那么直接报错 xxx is not a function

  • 在调用栈中开辟一段新的 函数执行空间

    -> 把函数存储空间内的形参, 代码全部复制过来

  • 在这个执行空间内, 进行形参的赋值

    -> 赋值是在函数调用空间内执行的

  • 在这个执行空间内, 进行函数内代码的预解析

    -> 对函数体内存储的一段字符串进行通读并解释

  • 把函数体内的 字符串, 当做 js 代码来执行

    -> 如果是合法的 js 代码, 那么直接执行

    -> 如果不合法, 此时才会报错

  • 代码执行完毕以后, 此次开辟的函数执行空间销毁

变量的赋值: 把一个变量内存储的数据百分之百的复制一份给到另一个变量

 var a = 100
    var b = a
    console.log(a)
    console.log(b)

    a = 200
    console.log(a)
    console.log(b)

在函数的调用过程中

+ 先形参赋值 还是 先预解析

+ 结论: 先形参赋值后预解析

=> 不要在函数内定义和形参一样的函数名

=> 这样你的形参就没有意义了

 function fn(a) {
      console.log(a)
      function a() { console.log('我是 a 函数') }
    }
    fn(100)
      /*
      function fn(a) {
        console.log(a)
        function a() { console.log('我是 a 函数') }
      }
      fn(100)

      打开浏览器
        + 预解析
          => 在浏览器全局声明了一个叫做 fn 的变量, 保存了一个函数
        + 代码执行
          => 函数定义直接调用
          => fn(100)
          => fn 函数代码执行过程
            -> 假设先形参赋值后预解析
              + 首先给 a 形参赋值为 数值 100
              + 接下来进行函数内预解析的时候, 定义了一个叫做 a 的变量, 并且赋值为一个函数
              + 在赋值为函数的时候, 就会把 100 覆盖
              + 在之后执行代码的时候, console.log(a) 打印出来的是 函数体
            -> 假设先预解析在形参赋值
              + 首先进行函数内的预解析
              + 在预解析过程中, 在函数内定义了一个叫做 a 的变量, 并且赋值为一个函数
              + 接下来进行形参赋值的过程中, 给 a 赋值为 100, 此时就会把函数覆盖掉
              + 在之后执行代码的时候, console.log(a) 打印出来的就是 100

    */

    
举报

相关推荐

0 条评论