gpt4 book ai didi

javascript - clojurescript 相当于 javascript 的模块模式

转载 作者:行者123 更新时间:2023-11-30 20:37:53 25 4
gpt4 key购买 nike

我刚刚开始在 node-js 端使用 clojurescript。我用它来构建将在节点上运行的命令行工具。现在我已经设置了我的概念验证并且或多或少地做了我想做的事情,是时候更好地组织代码了。

在 JS 上,当我需要类似于已配置的 http 客户端的东西时,我通常会导出一个函数,该函数接受基本参数并返回一个对象,该对象具有绑定(bind)到该参数的方法(通常使用显示模块模式)。类似于在 OOP 上创建新实例的东西。这是我如何在 JS 上执行此操作的一个小示例:

const request = require('request')
module.exports = (user, pass, baseUrl) => {
const client = request.defaults({baseUrl, auth: {user, pass}})

const getSomething = (name) => client.get('resources/' + name)
const createSomething = (name, options) => client.post('resources', {...})
return { getSomething, createSomething }
}

但是在 clojurescript 上我找不到合适的方法。所有定义都是在编译时计算的顶级声明,并且制作上述结构需要使用客户端参数声明我的所有函数,然后部分应用它们并在逻辑主体上使用它们。这可能是这样的:

(ns some-client [:require ["request" :as request]])
(defn get-something [client, name]
(.get client (str "resources/" name)))
(defn create-something [client, name, options]
(.post client (str "resources") {:name name :data: options}))

(defn make-client [usr, pass, baseUrl]
(let [client (.defaults request {:auth {:user usr :pass pass} :baseUrl baseUrl})]
{:get-something (partial get-something client)
:create-something (partial create-something client)}))

这可能看起来还不错,但是一旦您需要在另一个所有功能都需要此类客户端的地方使用它,事情就会开始变得困惑。您将需要在所有功能上接受客户端,如果其他命名空间只是您需要在另一个地方使用的功能集合,您将被迫遵循返回客户端创建者的相同模式,接受客户端您依赖并确保将其传递给可能需要它的每个功能。我可以变得像这样可怕:

(ns other-helper)

(defn trivial-stuff [client name bla]
(let [get-something (client :get-something)]
(get-something name))) ; make things like filtering and that


(defn more-trivial-stuff [client name bla]
(let [get-something (client :get-something)])
(get-something name)) ; make things like filtering and that


(defn non-trivial-stuff [client name bla]
(->>
(trivial-stuff client name bla)
(more-trivial-stuff client name)))

(defn more-non-trivial-stuff [client name bla]
(->>
(trivial-stuff client name bla)
(more-trivial-stuff client name)))

(defn compile-utils [client]
{:more-non-trivial (partial more-non-trivial-stuff client)
:non-trivial (partial non-trivial-stuff client)})

我无法为客户端制作任何 def,因为我将在运行时需要凭据,所以我必须接受所有这些东西作为参数并绑定(bind)结果,对我来说这看起来像大量样板代码和重复代码根本无法维护。

clojurians 有更好的方法吗?在这方面有任何风格指南吗?这是我第二次接触 clojurescript,起初它看起来非常吸引人,但一旦你开始构建重要的东西,它就会开始变得困惑。

注意:为了简单起见,我没有管理任何 js 互操作或使用 channel 进行异步处理。我只是将 js 对象声明为普通的 cljs 映射并将所有内容都视为同步的,但是包括 js 互操作和所有这些东西会使事情变得更糟。

编辑(澄清):

我的问题不是这在 clojure 上是否可行,我知道这是可能的,因为 CLJS 和 JS 共享所需的功能集以使其成为可能。然而,在完全不同的语言上使用相同的模式不仅感觉不对,而且由于 lisp 语法,它看起来也很难看。我能想到的其他替代方案看起来也很丑陋,并且涉及大量重复,因为它需要让客户使用它并在每个函数上传递它,这会导致非常重复和分散注意力的代码。

为了弄清楚我将如何在 js 上使用它,它会像这样

const makeClient = require('./my-http')
const client = makeClient('my-user','my-pass','http://google.es')
client.getSomething('dude')

如您所见,我可以根据需要创建具有不同设置的客户端,我什至可以进行一些解构并仅选择我需要的方法,因为它们根本不依赖于它们的绑定(bind)。

最佳答案

注意:我还没有“愤怒地”使用过 Clojure/Script,所以这对我来说也是一种学习经历 :) 不幸的是,我还没有在 REPL 中验证代码。

如果我没理解错的话,JS模块模式就是一个函数,返回两个函数的字典。在你的代码中的某个时刻,你“创建”了这个模块,也许给它一个名字,然后你在你的代码中传递它,就像这样:

let client = require("mymodule")("user", "pass", "http://example.com");
client.getSomething("foo")

您可以使用 ClojureScript 做同样的事情:

 (defn create-client [user pass base]
(let [get-something (fn [name] ...)
create-something (fn [name other] ...)]
{:get get-something :create create-something}))

(let [client (create-client "user" "pass" "http://example.com")]
((client :get) "foo"))

现在可以说这可能看起来有点笨拙,但它是完全相同的代码:关闭几个变量,将两个函数粘贴在一个映射中,从映射中获取一个函数并调用它。


你的问题的第二部分看起来是关于全局状态的——你必须随身携带 client 对象,而且感觉很笨重。我不认为它在 Javascript 中看起来更好?

let client = require("mymodule")("user", "pass", "http://example.com");

let trivialStuff = (client, name, blah) => { client.getSomething(name); ... };

let moreTrivialStuff = (client, name, blah) => { client.getSomething(name); ... };

let nonTrivialStuff = (client, name, blah) => {

let result = trivialStuff(client, name, blah)
return moreTrivialStuff(client, name, result)
}

即您仍在传递 client。您可以在初始化后使其成为模块级变量,但随后您将失去在运行时创建两个不同客户端的能力。

您是说使用揭示模块模式还会公开 nonTrivialStuff,因此您可以执行 client.nonTrivialStuff()


如何创建一个 namespace ,其中包含所有需要client 的函数(它可能只是一个包含 JS requests 客户端的普通映射),并直接使用它们?

例如

(ns some-client [:require ["request" :as request]])

(defn make-client [usr pass base-url]
{:client (.defaults request {:auth {:user usr :pass pass} :baseUrl baseUrl})}) ;; you might want to use #js here, since you usually cannot pass CLJS maps to JS directly

(defn get-something [client name]
(.get (client :client) (str "resources/" name)))

(defn create-something [client name options]
(.post (client :client) (str "resources") {:name name :data options}))

然后在其他命名空间中:

(ns other-code [:require [some-client :as client]])

(def c (atom nil))

;; get user and pass from runtime, then call
(reset! c (client/make-client user pass base-url))

;; use like so
(client/get-something @c "name")

我选择将 JS 客户端对象放入 CLJS 映射中以实现灵 active ——将来您可能希望将更多数据添加到该映射中。当然,客户端代码不会更改,因为它应该将其视为不透明值。

关于javascript - clojurescript 相当于 javascript 的模块模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49611955/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com