ES6 解构预处理

ES6 destructuring preprocessing

问题

函数参数解构是 ES6 中的一个了不起的特性。 假设我们想要一个名为 ffunction 接受一个 Object,它有一个 a key

function f({ a }) {
    return a;
}

当参数未提供给函数时,我们有默认值以避免 Type Error

function f({ a } = {}) {
    return a;
}

这将有助于以下情况

const a = f(); // undefined

不过,它会在

上失败
const a = f(null); // TypeError: Cannot match against 'undefined' or 'null'.

您可以看到 Babel 如何将函数转译为 ES5 here

可能的解决方案

可以通过参数验证和预处理来避免。在 Python 中我可以使用装饰器,但在 JS 中我们没有将它们标准化,所以现在使用它们不是一个好主意。 但是,假设我们有一个装饰器 checkInputObject,它使用给定的项目列表(或嵌套解构的情况下的树)进行必要的检查并提供默认值。 我们可以通过以下方式使用它而无需 @ 符号

const f = checkInputObject(['a'])(({ a }) => a);

使用 @ 表示法可能看起来像这样

@checkInputObject(['a'])
function f({ a }) {
    return a;
}

我也可以在函数本身中进行所有需要的操作,然后才使用解构,但在这种情况下,我失去了函数参数解构的所有优点(我根本不会使用它)

function f(param) {
    if (!param) {
        return;
    }
    const { a } = param;
    return a;
}

我什至可以实现一些常用函数,例如 checkInputObject,以便在函数内部使用它

const fChecker = checkInputObject(['a']);
function f(param) {
    const { a } = fChecker(param);
    return a;
}

不过,使用附加代码对我来说并不优雅。我希望将不存在的实体分解为 undefined。 假设我们有

function f({a: [, c]) {
    return c;
}

f()的情况下得到undefined就好了。

问题

您是否知道任何使 [嵌套] 解构对不存在的嵌套键具有抵抗力的优雅便捷的方法?

我的担忧如下:似乎这个功能在 public 方法中使用是不安全的,我需要在使用它之前自己进行验证。这就是为什么在私有方法中使用之前它似乎毫无用处。

你不是自己回答了你问的问题吗?

我认为默认参数是更优雅的方式。

function f({a: [,c]}={a:[undefined,undefined]}) {
    return c;
}

f();

我相信我可以通过向您展示不同的方式来帮助您解决这个问题。

您面临的问题与解构无关,因为您将在发送给它的任何函数中遇到它所期望的不同类型。

解决此类问题,例如。解构,您可以使用类型检查解决方案,例如 Flow,它静态检查类型,因此只要您向函数发送错误的内容,它就会在构建时失败。

javascript 的本质是它是一种动态类型语言,因此您可能希望该函数能够处理发送给它的任何参数,在这种情况下,您可以使用动态类型检查,解决方案很多。

函数参数更多的是 "agreement" 你如何使用函数,而不是访问它的参数(毕竟你可以只使用 arguments 来访问它们),因此通过发送不同的你违反了这个协议,无论如何这不是一个好的做法,最好有 1 个函数做 1 件事,并接受尽可能少的复杂参数。

您可以使用 try...catch 并在通用装饰器函数中使用它:

function safe(f) {
   return function (...args) {
       try {
           return f(...args);
       } catch (e) {}; // return undefined
   };
}

function f({ a } = {}) {
    return a;
}

f = safe(f);
console.log(f(null)); // -> undefined
console.log(f( { a:3 } )); // -> 3

在 ES6 中处理这个问题的正确方法是尊重语言处理参数的方式和形状 API 以更好地适应它,而不是以相反的方式。

fn(undefined)中解构参数背后的语义是参数将被替换为默认值,在fn(null)的情况下意味着空参数不会被替换为默认值。

如果数据来自外部并且应该是conditioned/preprocessed/validated,这应该被显式处理,而不是通过解构:

function fn({ foo, bar}) { ... }

fn(processData(data));

function fn(data) {
  const { foo, bar } = processData(data);
  ...
}

fn(data);

应该调用 processData 的地方完全由开发人员自行决定。

由于提议的 ECMAScript 装饰器只是具有特定签名的辅助函数,因此相同的辅助函数可以用于 @ 语法和通常的函数调用,具体取决于项目。

function processDataDecorator(target, key) {
  const origFn = target[key];
  return target[key] = (...args) => origFn.apply(target, processData(...args));
}

class Foo {
  constructor() {
    this.method = processDataDecorator(this, 'method');
  }

  method(data) {...}
}

class Bar {
  @processDataDecorator
  method(data) {...}
}

自 ES5 以来,该语言为默认 属性 值提供的方式是 Object.assigndataundefinednull 还是其他原语并不重要,结果总是一个对象:

function processData(data) {
  return Object.assign({ foo: ..., bar: ...}, data);
}