【译】Javascript 的 RORO 模式

892 阅读4分钟

原文:www.tinyblog.dev/blog/2020-0… 翻译:大地

代码可读性是件大事,开发者往往花费大量时间在阅读代码上,有别人的代码,自己的代码,还有我们从未见过的代码。所以提高代码可读性能为自己和别人节省大量的时间。虽然有时候会以牺牲性能为代价,但还是要尽力保证可读性

一种我推崇的 Javascript 模式是 RORO(Receive an Object, Return and Object) 模式。该模式的要点是,函数总应该接收一个对象参数并返回一个对象结果。通过解构参数和返回值,以便更好的表示究竟是什么进入函数,什么返回出去。这种接收一个对象参数的写法,是我在 Python 中的 kwargs 中获得的经验,我很喜欢这个特性,因为它能很容易让我知道到底是什么进入了函数,也就是参数标签化、语义化。同样,Javascript 的 RORO 模式也给了我们这样的能力。

考虑下面函数调用:

const item = await getItemFromCollection(54391, 'shop');

这是一种我全力避免的一种写法。这个函数有着足够语义化的名字,我们暂且假设函数功能是在一个集合中获取某项。但是,函数的其他参数代表什么意思呢?或许可以尝试通过已有的经验推断,54391像是某种id,'shop' 像是集合的名字。我们不能通过这两个信息确定其功能,要想完全理解,还是需要去看源码找函数声明:

async function getItemFromCollection(id, collectionName) {

好吧,在花费了一点时间后找到函数声明了。但如果使用 RORO 模式,便可以省略这一步:

async function getItemFromCollection({ id, collectionName }) {
  // do something
};

const item = await getItemFromCollection({ 
  id: 54391, 
  collectionName: 'shop',
});

通过立即解构参数对象,可以获得同函数声明大部分相同的语义,并在使用的时候可以传递具名参数。所以仅仅通过函数调用就能清楚的知道不同参数的区别,也就没有必要去上下文中找函数声明了。参数上下文现在就变成了函数调用的一部分,也就更加具有可读性,并能减少跳跃性。

使用这种模式的另一个重要原因是我看不惯这种传递布尔型参数的方式:

someFunctionCall(false);

我一点也不喜欢看到这样的函数,布尔型在它们本身值之外提供了非常少的信息,我看到过有人这样优化过:

const variableNameDescribingBooleansPurpose = false;
someFunctionCall(variableNameDescribingBooleansPurpose);

someFunctionCall(/* comment describing the booleans purpose*/ false)

其实最好的方法其实是传递对象,并立刻解构。通过与函数调用绑定描述,也就没有必要做其他提高可读性的必要了,它本身就够了。

someFunctionCall({ booleanPurpose: false });

该模式包含两部分,现在已经讨论了接受对象参数(Receive an Object)部分,还要讨论返回(Return an Object)一个对象。其实两者基本相同。就是函数返回一个对象,调用后立即解构来获得我们想要的数据。这个就是一种在某些语言中常见的特性而在Javascript 不具备的:单一返回,获得多个结果。

async function runProcess({ processName }) {
  // run process on your server
  // maybe you decide to cache the results of this process running
  return { result, wasCached };
};

// destructuring off the result we can now have multiple items return from our function
const { result, wasCached } = await runProcess({ processName });
if (wasCached) {
  // run another process
}

个人来说,接受对象参数比返回对象更能使我受益。多返回固然不错,但大多数情况返回一个就够了。这时,解构一个返回值比我们直接把单一返回放到变量中就显得有些冗余了。但有时也很方便,所以我不会一直使用。

综上,最有用的就是'命名参数'。尤其是多参数和布尔型参数的函数,这会非常有用。返回对象在合适的场景下也不错。但这些都是为了我们的 Javascript 函数更加灵活。我开始遵循这种模式是在学习了 Python 和 Go 之后,并发现了这些语言的亮点。所以,从其他地方获取灵感和想法也是学习和提高的重要方式。