我可以绕过可选参数而仍然在Javascript中设置rest参数吗?

时间:2018-11-08 16:11:40

标签: javascript ecmascript-6

我有一个带有必需参数(A),一些可选参数(B,C)和其他参数(Z)的函数

import io
import os

from google.cloud import vision

client = vision.ImageAnnotatorClient()

response = client.annotate_image({
  'image': {'source': {'image_uri': '<IMAGE_URI>'}},
  'features': [{'type': vision.enums.Feature.Type.SAFE_SEARCH_DETECTION},
               {'type': vision.enums.Feature.Type.LABEL_DETECTION}]
})

print(response)

在某些情况下,我想在不指定可选参数的情况下调用该函数,但仍然指定其余参数“ Z”

const doTheThing = (a, b = 'B', c = 'C', ...z) => {
  console.log(a, b, c, z);
}

预期输出:

doTheThing('A', ...'Z');

不幸的是,我得到以下信息:

'A', 'B', 'C', 'Z'

我该如何解决这个问题?

2 个答案:

答案 0 :(得分:1)

JavaScript不允许提供命名参数或任何形式的参数跳过,因此无法使用当前形式的函数来执行所需的操作。不过,这里有一些替代方法:

纯JavaScript方法:将配置对象作为参数

代替接受多个参数

func = (a, b, c) => { /* operate with parameters */ } 
func("One", "Two", "Three")

您的函数将改为接受一个对象

func = config => { /* operate with config */ }
func({a: "One", b: "Two", c: "Three"})

这是JavaScript中的常见模式,因为它允许您几乎命名变量,并且不需要以正确的顺序传递它们。它可以轻松传递大量变量,也可以使其也很容易默认它们。

const doTheThing = (config) => {
  const defaultProperties = { 
    b: "B", 
    c: "C"
  }
  
  const {a, b, c, ...rest} = Object.assign({}, defaultProperties, config);
  const z = Object.values(rest); //extract their values, otherwise you get an object
  
  console.log(a, b, c, z);
}

doTheThing({a: "A", x: "X", y: "Y", z: "Z"});

与rest参数一起使用时有点笨拙,但并非不可行。

但是,这确实意味着,如果有很多参数,则可能很难看清可以传递哪些参数以及需要哪些参数。

面向对象的方法:构建器模式

您将创建一个构建器对象-它用于保存值,直到调用final方法为止,此时将接受所有参数并一次性构造一个对象。

这是更多面向对象语言处理大量参数的方式,您甚至可以选择其中的一些参数。在JavaScript中看到这样定义的构建器并不是很常见,但是也不太奇怪。如果您已经使用类,甚至使用TypeScript,则可能更合适。

class DoTheThingBuilder {
  constructor() {
    this.a = null;
    this.b = "B";
    this.c = "C";
    this.z = null;
  }
  
  withA(a) {
    this.a = a;
    
    return this;
  }
  
  withB(b) {
    this.b = b;
    
    return this;
  }
  
  withC(c) {
    this.c = c;
    
    return this;
  }
  
  withEverythingElse(...z) {
    this.z = z;
    
    return this;
  }
  
  doTheActualThing() {
    const {a, b, c, z} = this;
    console.log(a, b, c, z);
  }
}

const builder = new DoTheThingBuilder();

builder
  .withA("A")
  .withEverythingElse("X", "Y", "Z")
  .doTheActualThing();

如您所见,对于某些简单任务而言,这可能非常冗长。对于这个示例来说,这是一个很大的矫kill过正,但是也许在实际使用中,您可能会发现它有所帮助。

我已经偏离了通常的方法-通常,您将设置构建器所需的所有参数,最后调用.build()来构建对象。在这种情况下,我基本上将build重命名为doTheActualThing并执行了该功能。

功能方法:咖喱

currying的概念非常简单-而不是拥有一个可以接受多个参数的函数

func = (a, b, c) => { /* operate with parameters */ }

您有一个带有一个参数的函数,该函数返回一个带有第二个参数的函数,返回另一个函数,依此类推,直到满足所有参数为止,然后执行完整的函数。

func = a => b => c => { /* operate with parameters */ }

在许多方面,这与OO Builder模式的功能等效。

const doTheThing = (a) => 
                   (b = "B") => 
                   (c = 'C') => 
                   (...z) => console.log(a, b, c, z);

doTheThing("A")()()("X", "Y", "Z");

这样,您可以通过不提供第二个和第三个参数来跳过它们,从而获得默认值。它也比生成器短。但是,阅读该功能可能有点奇怪。

答案 1 :(得分:0)

那是不可能的,而且很容易出错。命名参数的关键是要知道它们是什么以及它们以什么顺序出现。

使用object作为函数参数可以实现类似的目的:

const doTheThing = ({ a, b = "B", c = "C", others = {} }) => {
  const params = { a, b, c, ...others }; // this will merge your parameters into one object
  console.log(params);
}

doTheThing({ a: "A", others: { z: "Z" }});

这将记录A,B,C,Z。演示:https://codepen.io/tomekbuszewski/pen/jQqmNL?editors=0011