预解析的知识

预解析:

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

+ 函数调用问题

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

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

=> 预解析有两部分

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

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

预解析都解释哪些内容:

+ var

+ 声明式函数

var 的预解析

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

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

分析:

+ 当你使用一个变量的时候

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

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

// console.log(num)

// 包含两个操作

// 1. var num

// 2. num = 100

// var num = 100

代码:

1 console.log(num)

2 var num = 100

打开浏览器

预解析:

第 1 行 代码, 不需要预解析

第 2 行 代码, 有 var 关键字

进行预解析, var num 在浏览器声明了一个叫做 num 的变量, 但是此时不进行赋值

开始执行代码:

第 1 行 代码, console.log(num) 访问 num 这个变量

=> 因为预解析的时候已经声明了 num 变量

=> 所以此时是 有变量, 但是没有值

=> 所以出现 undefined

第 2 行 代码, 因为 var num 已经在预解析执行过了

=> 这里只有 num = 100 这句代码了

=> 给 num 变量进行赋值

1 console.log(num)

2 var num = 100

等价于

1 var num

2 console.log(num)

3 num = 100

                预解析 - 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()

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. 在函数内, 不要定义和形参一样的函数名

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

函数定义阶段

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

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

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

函数调用阶段

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

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

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

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

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

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

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

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

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

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

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

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

// function fn(a, b) {

// var res = a + b

// return res

// }

// var r1 = fn(10, 20)

// var r2 = fn(100, 200)

// console.log(res)
 

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

// 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

                                    预解析的面试题

this : 不管函数在哪定义, 不管函数怎么定义, 直接去看怎么调用的

变量 : 不管函数怎么调用, 就按照定义位置去逐层查看

// 面试题1:

// var a = b = 10

// a = 20

// b = 20

// console.log(a)

// console.log(b)

var a = b = 10

a = 20

b = 20

console.log(a)

console.log(b)

预解析:

+ 在浏览器定义了一个叫做 a 的变量

代码执行:

+ b = 10

=> 涉及到的叫做变量赋值机制

=> 直到全局都没有 b 这个变量, 把 b 定义为全局变量, 在进行赋值

+ a = b

=> 因为 b = 10 的执行, 导致全局有了 b 变量

=> 把 b 变量保存的值赋值给 a

=> 此时 a 也是 10

+ ...

// 面试题2:

// var a = b

// a = 20

// b = 20

// console.log(a)

// console.log(b)

/*

var a = b

a = 20

b = 20

console.log(a)

console.log(b)

预解析:

+ 在浏览器定义了一个叫做 a 的变量, 没赋值

代码执行:

+ a = b,

=> 访问 b 变量的值赋值给 a

=> 涉及变量访问机制, b 没有定义过, 报错

=> b is not defined

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我是打工人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值