【问题标题】:How to force a variable to be local in coffeescript?如何强制变量在咖啡脚本中是本地的?
【发布时间】:2014-11-09 08:07:18
【问题描述】:

给定以下代码:

outer=1
f=->
  local=1
  outer=0
  local+outer

coffeescript 为local 创建一个var,但重新使用outer

var f, outer;

outer = 1;

f = function() {
  var local;
  local = 1;
  outer = 0;
  return local + outer;
};

这是您的期望。

但是,如果您在函数中使用局部变量,则该变量是否声明为局部变量取决于外部范围。我知道这是一个特性,但它导致了一些错误,因为我必须检查所有外部范围是否有同名的变量(在我的函数之前声明)。我想知道是否有一种方法可以通过将变量声明为本地来防止这种类型的错误?

【问题讨论】:

    标签: coffeescript


    【解决方案1】:

    当您没有使用适当的描述性变量名称时,通常会出现这种错误。也就是说,有一种方法可以隐藏外部变量,尽管接受的答案是这样的:

    outer=1
    f=->
      do (outer) ->
        local=1
        outer=0
        local+outer
    

    这会创建一个 IIFE,其中一个参数是 outer。函数参数会像 var 关键字一样隐藏外部变量,因此这将具有您期望的行为。然而,就像我说的,你真的应该更描述性地命名你的变量。

    【讨论】:

    • do 非常有助于冻结外部变量的状态。但是,我认为意外使用外部变量的问题是咖啡脚本范围设计方式所固有的......
    • 在我使用你描述的do 的那一刻,我知道outer 正在遮蔽外部变量。在这种情况下,我可以通过使用另一个名称来避免它。我说的是不小心将变量命名为外部变量。如果您将函数移动到另一个地方并且突然有一个从外部范围继承的变量,否则这可能会很棘手......我正在寻找一种模式来确保我想要的所有局部变量都是本地的。 .. 可以将do 用于所有局部变量,但这很奇怪....
    • @Michael_Scharf 我知道这可能无法解决您的问题(这就是为什么我建议使用更具描述性的名称 - 实际上,我从未真正遇到过这个问题,所以我怀疑它是可以避免的)。但是,接受的答案是“不可能故意隐藏外部变量”,这显然不是真的,所以我觉得有必要纠正它。
    【解决方案2】:

    不,CoffeeScript 中明确提供该功能(强调我的):

    这种行为实际上与 Ruby 的局部变量作用域相同。因为您无法直接访问 var 关键字,不可能故意隐藏外部变量,您只能引用它。因此,如果您正在编写一个深度嵌套的函数,请注意不要意外重用外部变量的名称。

    【讨论】:

    • 我明白了。例如,在 python 中,您可以访问外部变量但不能修改它们(在 python 3.x 中有一个关键字 nonlocal 可以做到这一点)。我认为,外部变量的隐式修改可能容易出错......
    【解决方案3】:

    您可以使用反引号将纯 JavaScript 注入到 CoffeeScript 中:

    outer=1
    f=->
      local=1
      `var outer=0`
      local+outer
    

    在大多数情况下,我会尽量避免这种情况,而是更愿意重命名外部变量,在它们的名称中指明它们的范围/上下文。但是,有时这很有帮助,例如,在使用 debug 模块时,我总是希望有一个 debug() 函数可用于日志记录,如下例所示:

    #logging fn for module setup and other global stuff
    debug = require("debug")("foo")
    
    class Bar
      #local logging fn to allow Bar instances to log stuff
      `var debug = require("debug")("foo:bar")`
    

    如果你想至少保留纯 JS,只需声明变量,然后使用 CoffeeScript 赋值:

      `var debug`; debug = require("debug") "foo:bar"
    

    示例编译为:

    // Generated by CoffeeScript 1.7.1 -- removed empty lines for SO answer
    var Bar, debug;    
    debug = require("debug")("foo");    
    Bar = (function() {
      function Bar() {}    
      var debug;    
      debug = require("debug")("foo:bar");    
      return Bar;    
    })();
    

    我更喜欢这种直接声明变量的方式,而不是(恕我直言)更慢且可读性更低的 IIFE hack。

    【讨论】:

      【解决方案4】:

      正如 Aaron 所指出的,阴影确实是可能的:

      outer=1
      f=->
        do (outer) ->
          local=1
          outer=0
          local+outer
      

      由于本地函数内部不需要外部值,因此可以使用null 对其进行初始化,以防万一变量outer 从外部范围中删除(这会导致错误)。

      #outer=1
      f=->
        do (outer=null) ->
          local=1
          outer=0
          local+outer
      

      【讨论】:

      • +1。但我可能会使用outer=undefined,除非需要null 值。
      【解决方案5】:
      important = 10 # global
      
      main = ->
          agentId = '007'
          console.log 'global `important`', important # refers to the global variable
      
          ((important) -> # place the local variables as the arguments
              important = 20
              console.log 'local `important`', important # locally scoped
              console.log 'parent scope value', agentId # even access variables from the above scopes
          )() # not passing anything, so the local varibales would be left undefined at first
      
          console.log 'global `important`', important # the global variable remains untouched
      
      main()
      

      【讨论】:

      • 你可以写do (important) -> ...而不是((important) -> ...)()。这为您节省了一些“()”。两个版本都编译为相同的 javascript。
      • @Michael_Scharf 我想这是错误的,因为它转换为 (function(important) { ... })(important) 因为我不打算传递任何参数
      • 绕过的方法是 do (important=undefined) ->。是的,do 语法使它更加简洁。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-04-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-15
      • 1970-01-01
      • 2011-10-13
      相关资源
      最近更新 更多