作用域
作用域种类分为 全局作用域、函数作用域、块级作用域
作用域定义:
作用域是在代码运行时的某些特定部分中变量,函数和对象的可访问性。
简单说:作用域决定了代码区块中变量和其他资源的可见性。
作用域用处:
隔离变量,不同作用域下同名变量不会有冲突。
全局作用域
全局作用域:代码在程序的任何地方都能被访问,window对象的内置属性都拥有全局作用域
在代码中任何地方都能访问到的对象拥有全局作用域,一般来说以下几种情形拥有全局作用域:
最外层函数和在最外层函数外面定义的变量拥有全局作用域
var outVariable = "good" // 最外层变量
function test() { // 最外层函数
var inVariable = "goodData" // 内层变量
function test1(){
console.log(inVariable)
}
test1()
}
console.log(outVariable); //good
test(); //goodData
console.log(inVariable); //inVariable is not defined
test1(); //innerFun is not defined
所有末定义直接赋值的变量自动声明为拥有全局作用域
function test(){
name ="good"
var name = "food"
}
test();
console.log(name) // good
所有 window 对象的属性拥有全局作用域
一般情况下,window 对象的内置属性都拥有全局作用域,例如 window.name、window.location、window.top 等等。
例子:
作用域有上下级关系,上下级关系的确定就看函数是在哪个作用域下创建的。如上,fn作用域下创建了bar函数,那么“fn作用域”就是“bar作用域”的上级。
函数作用域
函数作用域:是指声明在函数内部的变量,在固定的代码域中可以访问。最常见的例如函数内部。
function test(){
var name = "good"
function test1(){
console.log(name) // undefined
}
}
test()
块级作用域
块级作用域可通过新增命令 let 和 const 声明,所声明的变量在指定块的作用域外无法被访问。
静态作用域
静态作用域是指函数的作用域在函数定义时就已经确定了。
// 静态作用域:
var a = 10;
function fn() {
var b = 1;
console.log(a + b);
}
fn(); // 11
动态作用域
动态作用域是指函数的作用域在运行时才确定。
// 动态作用域:
function foo() {
console.log(a);
}
function bar() {
var a = 3;
foo();
}
var a = 2;
bar(); // 2;
作用域链
作用域链的意义:查找变量(确定变量来自于哪里,变量是否可以访问)
当访问一个变量时,解释器会首先在当前作用域查找标示符,如果没有找到,就去父作用域找,直到找到该变量的标示符或者不在父作用域中,这就是作用域链
简单来说,查找一个变量来自哪里,能否被访问,需要以下四步:
- 1.查看当前作用域,如果当前作用域声明了这个变量,可以直接访问
- 2.查找当前作用域的上级作用域,也就是当前函数的上级函数,看看上级函数中有没有声明,有就返回变量,没有继续下一步
- 3.再查找上级函数的上级函数,直到全局作用域为止,有则返回,无则继续
- 4.如果全局作用域中也没有,我们就认为这个变量未声明(xxx is not defined)
这四步操作就描述了整个作用域链及作用域链如何查找变量的过程。
var x = 10
function fn() {
console.log(x)
}
function show(f) {
var x = 20
(function() {
f() //10,而不是20
})()
}
show(fn)
用处:保证对执行环境有权访问的所有变量和函数的有序访问。
额外扩展
自由变量
当前作用域没有定义的变量,这成为自由变量。自由变量的值如何得到 —— 向父级作用域寻找
var a = 10
function test(){
console.log(a) // 10
}
要到创建这个函数的那个作用域中取值,这里强调的是“创建”,而不是“调用”,切记切记——其实这就是所谓的"静态作用域"
作用域与执行上下文
区别:执行上下文在运行时确定,随时可能改变;作用域在定义时就确定,并且不会改变。