如何在javascript中使用函数链接组合?

时间:2016-12-30 08:24:52

标签: javascript functional-programming function-composition

我有这个代码正在构建一个记录不同参数的url端点:

const query = (obj) => {
  let starter = 'query='
  let queryToString = JSON.stringify(obj)
  return `${starter}${queryToString}`
}

const params = (str) => `${str}`

const endpoint = (protocol, base, params, query) => {
  if (!params && !query) return `${base}`
  if (!params) return `${base}${query}`
  return `${protocol}${base}?${params}&${query}`
}

const baseUrl = 'api.content.io'

const protocol = (secure = true) => secure ? 'https://' : 'http://'

let result = endpoint(protocol(), baseUrl, params('limit=5&order=desc'),
  query({
    field: 'title',
    include: 'brands'
  }));

console.log(result)

这会构建一个字符串,如:

https: //api.content.io?limit=5&order=desc&query={"field":"title","include":"brands"}

是否可以重构此代码,以便可以删除endpoint函数中的条件,应用正确的连接字符串并将整个事物链接到函数调用中,如< / p>

Endpoint.chain(protocol(p)).chain(base(b)).chain(params(p)).chain(query(q)).build()

我如何开始这样做?

更新:下面的解决方案非常好,但我想了解功能程序员如何使用Monads的ADT(代数数据类型)来解决这个问题。我们的想法是运行chain个函数,然后运行fold以获取我想要的值

3 个答案:

答案 0 :(得分:2)

你正在寻找这样的东西吗?

链接基本上发生在每个函数调用中返回完整对象时。这里每个函数都设置其给定块的值,并返回该对象以用于它所附加的任何上下文。每个函数都能够处理它接收的值,而value函数将根据设置的参数构建url。

var Endpoint = {
    protocol: protocol,
    base: base,
    params: params,
    query: query,
    value: value
}
function protocol(b) {
    protocol.value = b && 'https://' || 'http://';
    return this;
}
function base(s) {
    base.value = s || '';
    return this;
}
function params(s) {
    params.value = s || '';
    return this;
}
function query(s) {
    query.value = s || '';
    return this;
}
function value(s) {
    return   '$protocol$base$params$params$query'
        .replace('$protocol',protocol.value)
        .replace('$base',base.value)
        .replace('$params',params.value)
        .replace('$query',query.value)
}

Endpoint
    .protocol(true)
    .base('www.foo.com')
    .params('one/two/three')
    .query('?foo=bar')
    .value()
// "https://www.foo.comone/two/three$params?foo=bar"

答案 1 :(得分:2)

你可以使用一个类,它有允许链接的方法(它们返回this):

&#13;
&#13;
class EndPoint {
    constructor() {
        // defaults
        this.prot = 'https://';
        this.bas = 'api.content.io';
        this.qry = '';
        this.par = '';
    }
    secure(on = true) {
        this.prot = on ? 'https://' : 'http://';
        return this;
    }
    base(str) {
        this.bas = str;
        return this;
    }
    query(obj) {
        this.qry = `query=${JSON.stringify(obj)}`
        return this;
    }
    params(str) {
        this.par = str
        return this;
    }
    build() {
        let sep1 = this.qry.length || this.par.length ? '?' : '';
        let sep2 = this.qry.length && this.par.length ? '&' : '';
        return `${this.prot}${this.bas}${sep1}${this.par}${sep2}${this.qry}`;
    }
};

let result = new EndPoint().secure(true).base('www.example.com')
    .params('limit=5&order=desc').query({ field: 'title', include: 'brands' }).build();

console.log(result);
&#13;
&#13;
&#13;

答案 2 :(得分:1)

虽然上述答案可行,但如果您希望语法以Endpoint().开头,则使用EndpointBuilder类和下面的工厂函数。

class EndpointBuilder {

    constructor() {
        this.params = [];
        this.protocol = 'http://';
        this.baseUrl = 'api.endpoint.io';
    }

    base(url) {
        if (url && url.trim().length > 0)
            this.baseUrl = url;
        return this;
    }

    secure() {
        this.protocol = 'https://';
        return this;
    }

    setParam(param, val) {
        if (param && val)
            this.params.push({ param, val });
        return this
    }

    toString() {

        const url = `${this.protocol}${this.baseUrl}`;
        if (this.params.length <= 0)
            return url;

        let qString = '';
        this.params.forEach(p => {
            qString = `${qString}${qString.length > 0 ? '&' : ''}${p.param}=${JSON.stringify(p.val)}`;
        });

        return `${url}?${qString}`;
    }
};

// Endpoint Factory
const Endpoint = function () {
    return new EndpointBuilder();
};

用法示例:

const url1 = Endpoint().setParam('limit', 5).setParam('query', { field: 'title', include: 'brands' }).toString();

const url2 = Endpoint().base('another.endpoint.io').setParam('limit', 5).setParam('order', 'desc').setParam('query', { field: 'title', include: 'brands' }).toString();

const url3 = Endpoint().base('another.endpoint.io').secure().setParam('limit', 5).setParam('order', 'desc').setParam('query', { field: 'title', include: 'brands' }).toString();

console.log(url1);   
// http://api.endpoint.io?limit=5&query={"field":"title","include":"brands"}

console.log(url2);
// http://another.endpoint.io?limit=5&order="desc"&query={"field":"title","include":"brands"}

console.log(url3);
// https://another.endpoint.io?limit=5&order="desc"&query={"field":"title","include":"brands"}