9.作用域及作用域链
作用域
简单来说,作用域就是变量起作用的范围和区域。作用域的目的是隔离变量,保证不同作用域下同名变量不会冲突。
JS中有三种作用域
- 全局作用域
- 函数作用域
- 块作用域
全局作用域
最外层变量及函数
jsvar num = 10 function f1() { var num2 = 20 function f2() { console.log(num2) } console.log(num) } console.log(num) f1() console.log(num2) // not defined console.log(f2) // not defined
num
变量和f1
函数在任何地方都可以访问到,反之,不在当前作用域下的变量只能载当前作用域下使用。不使用var声明的变量
jsfunction f1() { num = 10 var num2 = 20 } f1() console.log(num) // global console.log(num2) // num2 is not defined
不使用var声明的变量会进行变量提升(提到全局作用域下),所以
num
变量在任何作用域下都可以访问到。有时候,我们将不使用var声明的变量称为隐式全局变量window对象所有属性和函数
window对象代表整个浏览器窗口,window对象具有双重角色
JS访问浏览器的接口
window对象下的所有属性和函数都拥有全局作用域。例如
setTimeout
、alert
等ECMAScript中规定的global对象
在全局作用域中使用var所创建的变量都会作为window对象的属性保存;全局作用域中所有的函数都会作为window对象的方法保存
函数作用域
在函数内部定义的变量,拥有函数作用域
var name = '小明'
function sayHi() {
var str = 'hello world'
console.log(str)
}
function showName(myName) {
// 形参也是局部变量
console.log(myName)
}
sayHi() // 'hello world'
showName(name) // '小明'
console.log(str) // not defined
console.log(myName) // not defined
str
和myName
都是函数内部定义的变量(形参也算函数内部定义的变量),他们的作用域仅限于函数内部,全局作用域中不会访问到。
函数调用时创建,结束后销毁。每调用一次产生一个新的作用域,之间相互独立。
块作用域
使用let或const声明的变量,如果被一个大括号括住,那么这个大括号括住的变量就形成了块级作用域。
{
let a = 10
console.log(a)
}
console.log(a) // 报错
块级作用中定义的变量只在当前块中生效
作用域链
只要是代码,就至少拥有一个作用域
var num = 10
function fn() {
var num = 20
function fun() {
console.log(num)
}
}
fn()
上述代码有三个作用域:全局作用域、fn函数作用域、fun函数作用域
作用域链关系如下:
试图在fun函数里访问变量num时:
fun函数作用域内无此变量 -> 上级作用域(fn函数作用域)有此变量,打印
我们把作用域层层嵌套,形成的关系叫做作用域链,作用域链也就是查找变量的过程。
查找变量的过程:当前作用域 --> 上一级作用域 --> 上一级作用域 .... --> 直到找到全局作用域 --> 还没有(报错)
作用域模型
作用域模型分两种
词法作用域
词法作用域是作用域的成因,也称静态作用域。代码在书写时就已经确定作用域了。
var num = 10;
function f1(){
console.log(num)
}
function f2(){
var num = 20;
f1()
}
f2();
根据词法作用域流程如下:f2函数调用 --> f1函数调用 --> 在f1作用域中寻找局部变量num --> 没找到,根据书写位置到上级作用域(全局作用域)查找 --> 发现num,打印10