词法绑定与动态绑定示例

在某些情况下,词法绑定和动态绑定的行为完全一致。 但在另一些场景下,它们会改变程序的语义。 例如,观察下面代码在词法绑定下的运行结果:

;;; -*- lexical-binding: t -*-

(setq x 0)

(defun getx ()
  x)

(setq x 1)

(let ((x 2))
  (getx))
     ⇒ 1

此处 (getx) 的结果为 1。 在词法绑定下,getx 无法访问 let 表达式中的值。 这是因为 getx 的函数体位于当前 let 表达式体之外。 由于 getx 定义在代码顶层(全局作用域,即不在任何 let 体内), 它会查找并使用全局作用域中的 x。执行 getx 时, x 的当前全局值为 1,因此 getx 返回 1。

如果改用动态绑定,行为则不同:

;;; -*- lexical-binding: nil -*-

(setq x 0)

(defun getx ()
  x)

(setq x 1)

(let ((x 2))
  (getx))
     ⇒ 2

此时 (getx) 的结果为 2。 这是因为在动态绑定下,执行 getx 时, x 的栈顶绑定来自 let 表达式。 此时 getx 不会访问 x 的全局值, 因为全局绑定在绑定栈中位于 let 绑定之下。

(部分变量属于“特殊变量(special)”,即使开启 lexical-binding, 它们也始终使用动态绑定。See Initializing a Variable with defvar。)