这是我参与2022首次更文挑战的第7天,活动详情查看:2022首次更文挑战」。
- 作用域
- 作用域链
- 词法作用域(静态作用域)
- 动态作用域
- 全局作用域
- 局部作用域
- 块级作用域
作用域
想象成一块区域,这块区域中,定义了各种变量对象等,这些变量的访问读取只能在这个区域内,这个区域外部的是没权限访问的!
var bee = '我是全局的'
function fn(){
var baa = '我在函数里面呢'
console.log(bee)
}
fn()// 我是全局的
console.log(baa);//Uncaught ReferenceError: baa is not defined
上面代码中,打印baa时会报错,因为baa是定义在函数fn局部作用域里面的,外部是没有的。
作用域链
从上面代码可以发下,在函数fn中打印外部定义的变量 bee 是没有问题的,也就是说局部作用域内是可以访问到外部作用域声明的变量的。
当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain)。作用域链的用途,是 保证对执行环境有权访问的所有变量和函数的有序访问。作用域链的前端,始终都是当前执行的代码所 在环境的变量对象。
作用域链相当于内部作用域与外部作用域沟通的桥梁,当前执行上下文(也就是执行环境,咱也不知道为啥这么搞多种叫法,有知道的同志可以悄摸儿告诉我😄)位于作用域链的最顶端
当要读取一个变量时,从作用域链顶端开始查找,如果找不到就继续向外层查找,最外层也就是全局环境了,如果还是找不到的话,就会抛出错误了。
var dda = '我是全局'
function fn(){
var dda = '我是fn'
console.log(dda)
}
fn(); // 我是fn
上面代码中,打印结果是定义在fn中的值,当fn函数执行时,会创建对应的变量对象加到作用域链前端,搜索标识符从沿着作用域链从前端开始逐级搜索,找到搜索就结束了。
修改一下代码:
var dda = '我是全局'
function fn(){
var dda = '我是fn'
function fn2(){
console.dir(fn2)
}
fn2()
console.dir(fn)
}
fn()
以上代码会输出什么呢?
函数有一个内部属性
[[Scopes]], 函数创建时,所有的父变量对象都存在这里,在当前函数调用时,再把其对应的变量对象也加到作用域链前端。
词法作用域
首先,语言分为静态作用域和动态作用域两大类
在JavaScript中呢,静态作用域就是词法作用域(lexical scoping),词法作用域又分成函数作用域和块级作用域。
词法作用域的特点就是:在定义的时候,作用域就已经确定了。
来看一个🌰
var value = 1;
function foo() {
console.log(value);
}
function bar() {
var value = 2;
foo();
}
bar(); // 1
上面的代码,输出结果为1,有问题吗?没有问题。输出1正好验证了我们前面说的。
函数在创建的时候,其作用域就已经确定了。
上面foo函数是在全局定义的,刚刚的作用域链中提到的[[Scopes]],在函数创建时,它外部的变量对象就已经被加入其中了,foo被定义在全局中,那么在bar函数作用域中再调用,它除了自身创建的变量对象外,外部只有全局对象。
动态作用域
动态作用域是运行时才决定的。
词法作用域关注函数在何处声明,而动态作用域关注函数从何处调用。
函数内部的this,就是谁调用就指向谁。
var name = 'www'
function fn(){
console.log(this.name)
}
var obj = {
name: 'oob',
func: fn
}
fn(); //www
obj.func(); //oob
函数作用域
在es6之前真正能划分作用域的就是函数的大括号{},除了全局作用域(常说的window),就是函数作用域了。
每个函数都有自己的执行环境。当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。 而在函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境。
块级作用域
什么是块级作用域?
简单的说,{} 这个花括号里面的作用域就是块级作用域。
这是es6新增的一个概念;
es6中提供了let 与 count 关键字,它们声明的范围就是块级作用域