Lua 闭包(Closure)

今天看到一个非常生动的使用闭包概念的代码片段,决定记录下来,这个代码出自 Kong 的插件 rate-limiting :

function RateLimitingHandler:access(conf)
  -- ...

  kong.ctx.plugin.timer = function()
    local ok, err = timer_at(0, increment, conf, limits, identifier, current_timestamp, 1)
    if not ok then
      kong.log.err("failed to create timer: ", err)
    end
  end
end

function RateLimitingHandler:log(_)
  if kong.ctx.plugin.timer then
    kong.ctx.plugin.timer()
  end
end

在学习闭包概念的时候,对里面提到的一句话不是非常理解:闭包可以捕捉作用域内的变量。一直没有找到特别好的实际用例,在这个代码片段里就能比较清晰的理解了。 access 阶段声明了一个延时函数的闭包,这个闭包在 log 阶段进行调用。如果不用闭包的话,代码会是什么样子呢?

function RateLimitingHandler:access(conf)
  -- ...

  kong.ctx.plugin.limits = limits
  kong.ctx.plugin.identifier = identifier
  kong.ctx.plugin.timestamp = current_timestamp
end

function RateLimitingHandler:log(_)
  local limits = kong.ctx.plugin.limits
  local identifier = kong.ctx.plugin.identifier
  local current_timestamp = kong.ctx.plugin.timestamp

  local ok, err = timer_at(0, increment, conf, limits, identifier, current_timestamp, 1)
  if not ok then
    kong.log.err("failed to create timer: ", err)
  end
end

我们需要使用 kong.ctx.plugin 来在 access 和 log 阶段之间共享变量,这样代码会显得很臃肿,如果使用闭包进行作用域变量的捕捉,那么代码就会写得简洁优雅。

如何修改闭包内的变量?这里我依然引用 Kong 2.x 的插件代码,比方说我们要开发一个插件的 Admin API ,我们需要在插件目录下创建 api.lua :

local endpoints = require "kong.api.endpoints"

local kong = kong
local caches_schema = kong.db.caches.schema

return {
  ["/caches"] = {
    schema = caches_schema,
    methods = {
      GET = endpoints.get_collection_endpoint(caches_schema),
    },
  },
  ["/caches/:cache_id"] = {
    schema = caches_schema,
    methods = {
      GET = function(self, ...)
        self.params.devices = { id = self.params.id }
        return endpoints.get_entity_endpoint(caches_schema)(self, ...)
      end,
    },
  },
}

首先看下这里的 get_collection_endpointget_entity_endpoint 方法是什么东西( kong/api/endpoints.lua ):

local function get_entity_endpoint(schema, foreign_schema, foreign_field_name, method, is_foreign_entity_endpoint)
  return function(self, db, helpers)
    -- ...
  end
end

可以看到,这里其实是返回了一个闭包,那么我们该如何修改这个闭包的参数呢?就是创建一个外部的函数,在这个函数里调用这个闭包,从而修改:

GET = function(self, ...)
  self.params.caches = { id = self.params.id }
  return endpoints.get_entity_endpoint(caches_schema)(self, ...)
end,

你可能感兴趣的:(Lua 闭包(Closure))