Answers:
一个主要的陷阱是未定义变量(即未用defvar
和定义的变量)的绑定语义随着改变lexical-binding
:没有它,let
将动态绑定所有内容,但lexical-binding
启用后的未定义变量将按词法进行绑定,如果在当前词法范围内未使用,甚至会完全消失。 。
旧代码有时依赖于此。为了避免对可选功能的严格依赖,它会绑定动态变量,而无需相应的库或声明变量本身:
(let ((cook-eggs-enabled t))
(cook-my-meal))
如果烹饪功能是可选的,则我们不想将不必要的依赖关系强加给用户,因此我们不使用功能(require 'cook)
,而是依靠cook-my-meal
功能的自动加载。
对于人类读者来说,显而易见的是,cook-eggs-enabled
它不是局部变量,而是在cook
此处引用了库中的某些全局动态变量。没有lexical-binding
此代码,将按预期工作: cook-eggs-enabled
动态绑定,无论是否定义。
随着lexical-binding
然而,它打破了: cook-eggs-enabled
现在势必词汇(然后优化掉,因为它没有使用),因此全球动态变量cook-eggs-enabled
是不是曾经在所有的感动,还是 nil
由时间cook-my-meal
被调用,所以我们出人意料地没有任何鸡蛋在我们的饭菜。
幸运的是,这些问题很容易发现:字节编译器自然会在此处警告未使用的词法绑定。
修复很简单:要么添加一个(require 'cook)
(对于实际上并不是真正可选的功能),或者为避免硬性依赖,在您自己的代码中将变量声明为动态变量。defvar
为此有一种特殊形式:
(defvar cook-eggs-enabled)
这定义cook-eggs-enabled
为动态变量,但不影响文档字符串,文档字符串load-history
(以及由此find-variable
和朋友)或其他任何变量,除了变量的绑定性质。
cook-eggs-enabled
是未绑定的时候let
结束?我敢肯定我以前遇到过这样的错误。defvar发生在内let
,let
后来该变量将变量恢复为其初始(无效)状态。