返回

程序是怎样记住自己的状态?

前端

程序之所以能够实现足够复杂的功能,很大程度上是因为程序能够记住运行的状态。比如运行过程中的变量值,函数的声明等等。如果没有状态,程序的功能性将会受到很大的限制。程序查找状态的规则就叫做作用域。

尽管我们将 JavaScript 归为动态语言或者解释性语言,但是 JavaScript 仍然具有作用域的概念。只不过 JavaScript 的作用域规则与其他语言,比如 Java、C++,并不相同。在 JavaScript 中,作用域是由执行上下文(execution context)决定的。

在解释 JavaScript 的作用域之前,我们首先要明确执行上下文(execution context)的概念。执行上下文可以理解为一个执行环境,它规定了代码的执行环境。一个执行上下文中包含了变量对象(variable object)、作用域链(scope chain)、this 的值以及其他一些状态信息。

在 JavaScript 中,函数被调用时会创建一个新的执行上下文,并且这个执行上下文会成为当前执行上下文。函数执行完毕后,当前执行上下文就会被销毁。全局执行上下文是第一个被创建的执行上下文,它也是所有其他执行上下文的父级执行上下文。

执行上下文与作用域的关系非常密切。作用域就是执行上下文中的变量对象。变量对象中存储着变量的值,以及函数的声明。在 JavaScript 中,变量的作用域是由它所在的执行上下文决定的。

作用域分为两种,一种是词法作用域,一种是动态作用域。词法作用域是指变量的作用域由它在代码中声明的位置决定,而动态作用域是指变量的作用域由它在运行时所在的执行上下文决定。

JavaScript 采用词法作用域,这意味着变量的作用域由它在代码中声明的位置决定。一旦变量被声明,它的作用域就确定了,并且不会改变。例如,以下代码中,变量 x 的作用域是函数 foo()

function foo() {
  var x = 10;
}

foo();

console.log(x); // ReferenceError: x is not defined

在上面的代码中,变量 x 在函数 foo() 中声明,因此它的作用域是函数 foo()。当函数 foo() 被调用后,变量 x 就被创建,并且值被设置为 10。当函数 foo() 执行完毕后,变量 x 就被销毁,因此在函数 foo() 之外的代码中无法访问变量 x

闭包是 JavaScript 中一个非常重要的概念。闭包是指一个函数及其内部作用域的组合。闭包允许函数访问其内部作用域中的变量,即使这个函数已经被调用并返回。

闭包的实现原理是,当函数被调用时,它会创建一个新的执行上下文,并且这个执行上下文会成为当前执行上下文。当函数执行完毕后,当前执行上下文就会被销毁,但是函数内部的变量仍然存在于内存中。这是因为函数内部的变量存储在函数的词法作用域中,而词法作用域不会被销毁。

闭包可以用来实现许多强大的功能,比如事件处理、私有变量、模块化开发等等。闭包也是 JavaScript 中最难理解的概念之一,但是只要理解了闭包的实现原理,你就能掌握闭包的使用方法。