作用域和预解析
一、作用域
变量使用的范围。
1.1 函数(局部)作用域
在局部作用域声明的变量,只能在函数内部被访问,不同函数和外部无法直接访问。函数的形参也是局部变量。
1.2 块作用域
{}内部声明的变量,不同代码块之间的变量无法互相访问。
- let 声明的变量和 const 声明的常量会产生块作用域,var 不会产生块作用域;
1.3 全局作用域
在全局申明的变量,可以在任何地方使用。
- 为 window 对象动态添加的属性默认为全局的;
- 函数中未使用任何关键字声明的变量为全局变量;
1.4 作用域链
变量查找机制:执行代码时,会优先在当前作用域中查找变量,如果当前作用域查找不到,则会逐级查找父级作用域直到全局作用域。
1.5 var、let 和 const
- let 和 var 都用于声明变量,都可以只定义,不赋值。
- const用于声明常量,常量名通常大写,必须初始化,不可修改值;
- let 声明的变量和 const 声明的常量会产生块作用域,var 不会产生块作用域;
- var 可重复声明一个变量,let /const 不可重复声明一个变量;
- var 声明的变量相当于window的属性;
- var 声明的变量可提升,let/const 不存在变量提升,不允许声明之前使用。
- let/const 会形成暂时性死区,所以需要先定义,再使用。
- 开发中不修改的值,数组和对象,建议用const声明,对于引用型数据,const声明的变量里面存的是地址。
- 基本数据类型的值或者引用类型的地址发生变化的时候用let。
1.6 闭包
子函数可以访问父函数中的局部变量,即使在父函数关闭以后。
- 场景:一个作用域有权访问另一个作用域的局部变量。
- 作用:延申变量的使用范围。
- 缺点:容易引起内存泄漏。
- 语法
<script>
function f1() {
let num = 1
// 子函数作为返回值的形式
return function () {
console.log(num)
}
}
let n = f1()
n()
</script>
- 回调函数也能访问函数内部的局部变量(作为函数的参数)
<script>
function fn(n){
let num=1
n(num)
}
fn (function(a){console.log(a)})
</script>
二、预解析和函数参数
2.1 预解析
- 代码在执行之前,先要进行 预解析,解析变量和函数:将声明的变量和带有名字的函数,提升到当前作用域最前面,只声明和定义,不赋值和调用。
- 变量名和函数名相同,函数优先被预解析。
2.1.1 变量提升
变量提升:变量声明之前可访问;变量被提前预解析,只声明,不赋值。
- var 声明的变量要进行变量提升;
- let声明的变量也会被预解析,但是未声明、初始化之前不可使用,所以我们说let 声明的变量不存在变量提升。
- 变量提升出现在相同作用域当中。
<script>
// 代码执行时,先进行预解析,将声明的变量提升到作用域最前面,只声明,不赋值
// 内存:var n
// 内存: let m
// 在这里变量已经被声明,没有赋值,可访问,结果为 undefined
console.log(n)
var n = 1
// m已被预解析,没有赋值,不可访问
console.log(m)
let m = 0
</script>
2.1.2 函数提升
函数提升:函数定义之前可访问;函数被提前预解析,只定义,不调用。
- 有名字的函数才会进行预解析,匿名函数和表达式函数不会被预解析;
- 函数提升出现在相同作用域当中。
<script>
// 函数预解析 函数已经被定义,不调用
/* 内存:function fn() {
console.log('今天天气真好!')
} */
fn()
function fn() {
console.log('今天天气真好!')
}
</script>
<script>
// 预解析
/* function fn() {
// 内存: var n
console.log(n)
var n = 3
}
var n */
console.log(n)
fn()
var n = 1
function fn() {
// 内存: var n
console.log(n)
var n = 3
}
</script>
2.2 函数参数
2.2.1 参数默认值
<script>
// 声明函数时为形参赋值即为参数的默认值
// 调用函数时没有传入对应实参时,参数的默认值被当做实参传入
function fn(num1 = 1, num2 = 2) {
let sum = num1 + num2
console.log(sum)
}
fn()
// 如果参数未自定义默认值时,参数的默认值为 undefined
function fun(num1, num2) {
let sum = num1 + num2
console.log(num1, num2, sum)
}
fun()
// 调用函数时若有实参传入,以传入参数为准
function fn(num1 = 1, num2 = 2) {
let sum = num1 + num2
console.log(sum)
}
fn(3, 5)
</script>
2.2.2 动态参数
arguments 对象:是函数内部内置的伪数组变量,用于接收调用函数时传入的所有实参。
- 作用:动态获取函数的实参。
- 使用场景:参数不固定的时候用。
<script>
function fn(){
console.log(arguments)
}
fn(1,2,3,4,5,6)
function fun(){
for(let i=0;i<arguments.length;i++){
console.log(arguments[i])
}
}
fun('welcome','to','wonderlan',1,2,3)
</script>
2.2.3 剩余参数
//... 置于最末函数形参之前,用于获取剩余的实参
<script>
// ...n 表示n接收所有剩余的参数,以数组的形式存储,并返回数组长度
function fn(m,...n){
console.log(m)
console.log(n)
}
fn(1,2,3,4,5,6,7)
// 此情况下等同于arguments的用法
function fun(...m){
console.log(m)
}
fun(1,2,3,4,5,6,7)
</script>