什么是作用域
在 javascript 中限制属性或者方法被访问的范围就可以被称之为作用域。
作用域的类型
| 对象 | 类型 |
|---|---|
| global | 全局作用域 |
| function | 局部作用域 |
| {} | 块级作用域 |
| this | 动态作用域 |
如果一个变量或者表达式不在一个作用域,那么 JavaScript 机制会继续沿着作用域链上查找到全局作用域或刘安琪 window,如果找不到贼不可以被使用。作用域也可以根据代码层级分层,以便于可以访问父作用域,通常是沿着链式的作用域链查找,而不是从父作用域引用子作用域中的变量
全局作用域
变量在函数或者{}外部定义,即为全局变量。在函数或者{}代码块中的变量也是可以拥有全局作用域的
在 ES6 新标准中是不推荐使用全局变量的。
var count = 1;
function print() {
console.log(count);
}
在上面的例子中,count 是在函数外部定义的,它属于全局作用域环境。如果变量在函数内部没有使用 var 关键字声明,它依然是全局变量。
var count = 1;
function print() {
count = 20;
console.log(count);
}
以上示例中 course 在函数内,但是拥有全局作用域,它将作为 global 或者 window 的属性存在。
函数作用域
在函数内部定义的变量就是拒不变量,函数作用域内对外是封闭的,在函数外部无法直接访问函数内部的变量
function print() {
var b = 233;
console.log(b);
}
console.log(b);
如果想读区函数内部变量,可以借助 return 关键字间接读取
function print() {
var b = 233;
creturn b
}
console.log(print());
除此之外还可以通过闭包的方式
function bar(value) {
var testValue = 'inner'
var rusult = testValue + value
function innser() {
return rusult
}
return innser()
}
console.log(bar('fun')) // "innerfun"
通俗的讲,return 是函数对外交流的出口,而 return 可以返回的是函数,根据作用域的规则,函数内部的子函数是可以获取函数作用域内的变量的。
按照上图所示,不难理解,我们可以按照原型链那样去理解作用域链。任何一个作用域链都是一个堆栈,首先进入 script 环境就会把 global object 压入栈底,再按照函数的嵌套关系依次压入堆栈,再执行时按照这个作用域链去查找变量。
块级作用域
在其他编程语言中,块状作用域是很熟悉的概念,但是在 JavaScript 中不被支持,就像上述知识一样,除了全局作用域就是函数作用域,一直没有自己的块状作用域。在 ES6 中已经改变了这个现象,块状作用域得到普及。关于什么是块,只要认识 {} 就可以了。
# demo1
const fn = () => {
let a = 1;
};
console.log(a);
# demo2
var state = true;
if (state) {
let b = 168;
}
console.log(b);
结论
- 用 let、const 关键字声明的变量都拥有块级作用域
动态作用域
在 JavaScript 中很多同学对 this 的指向时而清楚时而模糊,其实结合作用域会对 this 有一个清晰的理解。不妨先来看下这段代码:
window.a = 3;
function fn() {
console.log(this.a);
}
fn.bind({ a: 2 })();
fn();
在这里 bind 已经把作用域的范围进行了修改指向了 { a: 2 },而 this 指向的是当前作用域对象,是不是可以清楚的理解了呢?
接下来我们再思考另一个问题:作用域是在代码编写的时候就已经决定了呢,还是在代码执行的过程中才决定的?
var count = 1
<!-- 这里可以调用count -->
function print () {
<!-- 这里可以调用count -->
}
在看看这段代码,写代码的时候就知道 course 就是全局作用域,函数内部的用 var 定义的变量就是函数作用域。这个也就是专业术语:词法作用域。 通俗的讲变量的作用域是在定义时决定而不是执行时决定,也就是说词法作用域取决于源码,通过静态分析就能确定,因此词法作用域也叫做静态作用域。 相反,只能在执行阶段才能决定变量的作用域,那就是动态作用域。