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后来该变量将变量恢复为其初始(无效)状态。