今天看到一個非常生動的使用閉包概念的代碼片段,決定記錄下來,這個代碼出自 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
在學習閉包概念的時候,對里面提到的一句話不是非常理解:閉包可以捕捉作用域內(nèi)的變量。一直沒有找到特別好的實際用例,在這個代碼片段里就能比較清晰的理解了。 access 階段聲明了一個延時函數(shù)的閉包,這個閉包在 log 階段進行調(diào)用。如果不用閉包的話,代碼會是什么樣子呢?
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 階段之間共享變量,這樣代碼會顯得很臃腫,如果使用閉包進行作用域變量的捕捉,那么代碼就會寫得簡潔優(yōu)雅。
如何修改閉包內(nèi)的變量?這里我依然引用 Kong 2.x 的插件代碼,比方說我們要開發(fā)一個插件的 Admin API ,我們需要在插件目錄下創(chuàng)建 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_endpoint 和 get_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
可以看到,這里其實是返回了一個閉包,那么我們該如何修改這個閉包的參數(shù)呢?就是創(chuàng)建一個外部的函數(shù),在這個函數(shù)里調(diào)用這個閉包,從而修改:
GET = function(self, ...)
self.params.caches = { id = self.params.id }
return endpoints.get_entity_endpoint(caches_schema)(self, ...)
end,