ECMAScript系列之作用域

74 阅读4分钟

什么是作用域

在 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 定义的变量就是函数作用域。这个也就是专业术语:词法作用域。 通俗的讲变量的作用域是在定义时决定而不是执行时决定,也就是说词法作用域取决于源码,通过静态分析就能确定,因此词法作用域也叫做静态作用域。 相反,只能在执行阶段才能决定变量的作用域,那就是动态作用域。

推荐阅读