使用循环创建对象链

create object chaining using loop

有没有办法从循环中创建链式对象? 例如输入:

["table1","table2","table3"]

输出:

  db
  .select(fields)
  .from(table)
  .innerJoin("table1")
  .innerJoin("table2")
  .innerJoin("table3")

另一个输入:

 ["table1","table2","table3","table4","table5"]

输出:

  db
  .select(fields)
  .from(table)
  .innerJoin("table1")
  .innerJoin("table2")
  .innerJoin("table3")
  .innerJoin("table4")
  .innerJoin("table5")

现在我不知道该怎么做,除了使用 eval,这不是我想做的事。

我需要这个来使用 knex 连接多个表,所以如果有任何其他方法可以这样做,我会很高兴:)

我认为研究函数式编程会对您有所帮助。

我在下方使用了来自互联网 link 的管道函数,但您可以使用 lodash/Ramda/underscore 或任何您喜欢的实用程序库。

我在这里使用的两个主要概念是柯里化和管道。

What is 'Currying'? Currying is when you break down a function that takes multiple arguments into a series of functions that take part of the arguments we are also making use of curring which is returning a function from another function to compose new functions.

https://medium.com/@venomnert/pipe-function-in-javascript-8a22097a538e A pipe function takes an n sequence of operations; in which each operation takes an argument; process it; and gives the processed output as an input for the next operation in the sequence. The result of a pipe function is a function that is a bundled up version of the sequence of operations.

我们在这里做的是获取一个数组,创建一个新的函数数组,我们要将其应用于一个值。

所以我们想在数据库上执行大量连接。

join => con => con.innerJoin(join);

正在获取一个值,即 "table1" 并返回一个函数,该函数接受一个调用连接的数据库连接,并为下一个连接调用 returns。

const joins = toJoin.map(join => con => con.innerJoin(join)); 这创建了传递给管道的函数数组。

const db = ({
  select: function (field) {
    console.log('select', field)
    return db
  },
  from: function (table) {
    console.log('from', table)
    return db;
  },
  innerJoin: function (join) {
    console.log('innerJoin', join)
    return db;
  }
});

const _pipe = (a, b) => (arg) => b(a(arg));
const pipe = (args) => [].slice.apply(args).reduce(_pipe);

function joinInnerMultiple(table, fields, toJoin) {
  const joins = toJoin.map(join => con => con.innerJoin(join));

  return pipe(joins)(db.select(fields).from(table)); 
}


joinInnerMultiple("User", "uuid", ["table1", "table2", "table3", "table4", "table5"])

链接的工作方式是,每个方法 return 都是一个对象,下一个方法是 属性。这意味着您可以使用类似 reduce() 的方法来继续调用对象上的下一个方法 return 从前一个。

reduce() 接受一个初始对象,您可以传递该对象以使事情顺利进行。类似于:

var tables = ["table1","table2","table3"]
let res = tables.reduce((res, table) => res.innerJoin(table), db.select(fields).from(table))

为了了解这是如何工作的,我们可以创建一个假的 db 对象,其中包含 return 链中下一个方法的对象的所有方法。 innerJoin 方法只是将参数添加到值 属性:

// fake db object with these methods
const db = {
  select(t) {
    this.val = [] // initialize val
    return this
  },
  from(t) {
    return this
  },
  innerJoin(name) {
    this.val.push("Added: " + name)
    return this
  }
}

var tables = ["table1","table2","table3"]
// call innerjoin for each of the tables
// this is the same as chaining them
let res = tables.reduce((res, table) => res.innerJoin(table), db.select().from())

// values where accumlated in the val property
console.log(res.val)