预解析
预解析: 在所有代码开始执行之前, 对代码进行通读并解释, 解释完毕以后再开始执行代码
函数调用问题:
-
在函数定义的时候, 被装进 "盒子" 内的代码是不会执行的
-
在函数调用的时候, 代码才会执行
预解析有两部分:
-
全局预解析:打开页面的时候,会对全局代码进行预解析,但函数体内的代码不管
-
局部预解析:当你函数调用的时候,会在函数的私有作用域下进行预解析,解析完毕执行函数体内代码
预解析都解释哪些内容:
-
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 */
函数内的预解析
预解析教会了我们什么
-
函数名和变量名不要重名
-
不管是函数还是变量, 进行先声明后使用
-
在函数内, 不要定义和形参一样的函数名
函数的两个阶段都做了什么
函数定义阶段
-
在堆内存中开辟一段存储空间
-
把函数体内的代码当做 字符串 放在开辟出来的存储空间内
-
把函数名放在栈内存中, 把堆内存中的空间地址赋值给栈内存的变量
函数调用阶段
-
按照栈内存中变量存储的地址找到函数的存储空间
-> 如果这个空间不是一个函数存储空间, 那么直接报错 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 */