1、概念
JS的语言特性
:解释型语言- 没有
编译过程
,但在执行时,有翻译行为
,让:计算机能理解我们写的代码 - 所以
js
产生了预解析
的机制
- 没有
预解析包含
:
a.变量声明提升
和函数声明提升
两部分
b. 面试时:也可能会分开问这两个,预解析
是一个很广泛的概念,包含了这两部分的内容
2、预解析功能
2.1、语法检查
语法检查
:- 当
解释器
准备解释代码前,会预先对所有的Js代码
进行语法检查
,如果发现存在语法错误,那么所有代码都不会执行 - 然后报出
语法错误
,语法错误的级别也是最高的
- 当
语法错误示例
:
function (){} // 函数创建时没有添加函数名
var a = 10; // 错用了中文符号 这里应该为英文的分号;
... 等等
2.2、声明提升
解释
:- 为了拥有更好的性能,代码在执行的时候,会把所有的
声明行为
放在代码解释的最前方; JS
中所有的变量声明
行为、函数声明
行为都需要和内存
进行交互操作
:- 这个交互操作叫做:
I(input)/O(output)操作
- 这个交互操作叫做:
- 在代码开始解释前,也就是预解析时,会把所有涉及内存的
io操作
统一完成,来加快我们的内存操作速度,
- 为了拥有更好的性能,代码在执行的时候,会把所有的
也就是:
- 每次都单独开启放入变量等操作会比较浪费时间
- 那么一次性开启放入所有,在一次性结束,对整体的性能是比较好的,也比较省时
声明提升会导致
:- 我们看到的代码,并不是真实代码的摆放位置
- 因为无论把声明的
变量
或函数
代码写在哪里,都会在执行代码之前给提升到最前面去,然后优先执行
- 示例:
var a = 10;
无论是声明的位置在哪里,都会提前到所有代码最前面,提前进行声明
3、变量声明提升详解
解释:
- 代码执行分成两个阶段:1、
预解析
2、代码翻译并执行
- 注意:这里的
代码翻译
是指计算机翻译成计算机语言执行(这里可能不完全对,理解即可)
预解析
- 第一步:语法检查,检查语法是否出错,如果有错误,代码则全部不会执行
- 第二步:声明提升
示例:var a = 10
// 1、预解析规则中会把变量声明拆分成两个部分:
// 1)var a; ==> 声明变量行为,会发生声明提升,位置放在预解析;
// 2)a = 10; ==> 赋值行为,放置在书写位置
// 2、解释:
// 1)我们看到的代码是var a = 10; 实际是:a = 10,原位置保留
// 2)var a 是在预解析的位置去执行了,在实际执行代码的过程中,下面的代码只存在赋值行为,
代码翻译并执行
4、变量声明提升详解
解释:
- 代码执行分成两个阶段:1、
预解析
2、代码翻译并执行
- 注意:这里的
代码翻译
是指计算机翻译成计算机语言执行(这里可能不完全对,理解即可)
预解析
- 第一步:语法检查,检查语法是否出错,如果有错误,代码则全部不会执行
- 第二步:声明提升
- 注意:
函数声明提升
会把整体函数
都提升到预解析
部分去,但是!赋值式函数
不是整体提升- 这里要注意:用
var
声明变量后的提升,只是提升了变量声明部分,函数赋值部分没有进行提升
function foo(){
alert("hello world"); // 虽然是写在这里的,但是会整体提升至最前面的预解析部分
}
代码翻译并执行
5、声明提升的影响
5.1、变量声明提升的影响
// 1、先确定一个报错机制:变量没有声明直接使用会报错(也就是:引用错误) => 这里并未声明变量b,所以直接引用报错
console.log( b );
// 2、因为声明提升机制:我们在变量声明之前使用变量不会报错,只是打印变量的结果为undefined
// 1)代码是从上到下执行的,这里先引用了,然后再声明赋值,但实际的运行顺序为:
// 1、var c 被放置在预解析的位置
// 2、代码运行到console.log( c ),变量c只是声明了,但还未执行到赋值的代码块
// 3、所以打印结果为undefined,并不会报错
console.log( c );
var c = 10;
// 3、注意:这个机制会带来很多问题,所以:应该遵循`先声明后调用`的原则来使用变量
// 1)示例:
console.log( c + 10 );
var c = 10
// 2)解释:这里的计算结果为NaN,会导致运算错误,如果这个变量后面还有运算的话,会很麻烦,导致很多错误,所以一定要遵循:先声明后调用的原则!
5.2、函数声明提升的影响
// 1、对`声明式函数`的影响:
// 1)这里先调用是可以执行的,因为函数的声明提升是整体提升到最前面的预解析的
foo();
// 2)不会报错
function foo(){
alert("foo函数调用!")
}
// 2、对`赋值式函数`的影响:
// 1)赋值式函数步不可以在前方调用!!!
// 2)因为:用var声明变量后的提升,只是提升了变量声明部分,函数赋值部分没有进行提升 => var poo,此时调用poo(),由于不是整体提升的,
// poo会被当成变量,而变量使用(),会报错 => Uncaught TypeError: poo is not a function,poo不是一个方法/函数
// 3)示例:
poo(); // poo 此时是undefined,因为声明为赋值
var poo = function(){
alert("poo函数调用!")
}
6、补充
6.1、预解析只会在当前script标签中提升
1、解释:
1)预解析只会在当前script标签中提升,无法提升下一个script的内容
2)访问和声明提升是两码事,访问是可以跨的,提升是不行的,访问是可以访问另一个script的函数名
2、示例:
解释:预解析只管在同一个script进行提升 只能提升到当前script的最上面
<script>
aa()
function aa(){
console.log("age is 100")
}
</script>
<script>
// 上面的调用是不管这里的
function aa(){
console.log("age is 200")
}
</script>
6.2、函数内部变量/函数提升的局限
function test(a,b){
var result = a+b
console.log(result) // 调用函数后,函数内部可以正常打印结果
}
test(1,2)
console.log(result) // 这里会报错 => 因为函数内部resul变量只能提升到函数内部的最上面,如果在函数内部定义的var或者是函数内部定义的函数也只能提升到函数的最上面
6.3、函数内部声明提升
提升的顺序是:
1、把var name提上到函数最前面
2、打印name => 此时是undefined
3、赋值"小明"给name
4、再次打印name => 此时结果为"小明"
<script>
function test(){
console.log(name); // undefined
var name = "小明"
console.log(name) // "小明"
}
test()
</script>