一、作用域
1. 概念 & 作用
作用域,就是一个代码块所在的区域,它是静态的,在编写代码时就已经确定了
不同于执行上下文,(执行上下文是动态的,执行创建)
举例:
执行上下文时 建房子 时的 打地基
作用域 是 建房子 时的 区域
作用:用来决定代码执行的范围,变量所属的范围
2. 作用域和执行上下文区别
作用域 | 执行上下文 | |
---|---|---|
创建时刻 | 函数定义function a() {} | 函数调用a() |
性质 | 静态的,函数定义了,就不会变化 | 动态的 |
存在时间 | 不会变化 | 调用时创建,调用结束释放 |
联系:
- 上下文环境(对象) 是从属于所在的 作用域
- 全局上下文环境 ==> 全局作用域
- 函数上下文 ==> 对应的函数作用域
二、作用域链
1. 概念
作用域链:
- 作用域链 ,是一个数组结构
- 该结构保存的是 一个个的变量对象
作用域 是人们虚拟出来的,实际上,作用域根本不存在
但是,作用域链是真实存在的
作用:有句话说:找属性去找原型链,找变量去找作用域链
流程:
- 先在自身的作用域的变量对象找
- 没有,沿着作用域链,去上一个作用域的变量对象找
2. 例子解释
看以下代码,我通过断点的方式,来解释这个概念
/* 断点一 */
var a = 2;
function fn1() {
/* 断点二 */
var b = 3;
function fn2() {
/* 断点三 */
var c = 4;
console.log(c) // 4
console.log(b) // 3
console.log(a) // 2
console.log(d) // 报错
}
fn2()
}
fn1()
首先,通过 debug
调试,将代码执行带断点一的地方停下
那么:
- 首先创建全局作用域 和 全局变量对象
global
global
收集了函数fn1
,为他创建局部作用域- 这时,在函数
fn1
建立一个作用域链,里面保存一个变量对象global
然后,执行到断点二的地方停下:
- 首先,在
fn1
的作用域内,创建一个变量对象Local: fn1
Local:fn1
,收集了函数fn2
,为他创建局部作用域- 这时,在函数
fn2
建立一个作用域链,将Local:fn1
插入到链的头部 - 那么,这时作用域链,就有
[ Local:fn1, global ]
那么,这个由 [[Scopes]]
属性存储的变量对象,就是作用域链
3. 笔试题(附解析)
/* 输出什么 */
var x = 10;
function fn() {
console.log(x)
}
function show(f) {
var x = 20
f()
}
show(fn)
答案:10
解析:
- 首先,创建全局作用域和变量对象
global
global
收集函数fn、show
,创建局部作用域,两个的作用域链都只有一个global
对象- 执行代码,
fn, show
创建自身的作用域链,[[scopes]] = [global]
- 执行到
fn()
时,由于要找变量,那么先找自身的作用域的变量对象Local:fn
fn
自身没有x
变量,找作用域链的上一个变量对象global
global
的x
变量为 10- 输出 10
4. 练习
console.log(fn)
var fn = function () {
console.log(fn)
}
fn()
var obj = {
fn2: function () {
console.log(fn2)
}
}
obj.fn2()
答案:
第一道:undefined、 f() {console.log(fn)}
第二道: 报错:fn2 is not function