一行代码,蕴含四个棘手的 JavaScript 概念
前几天,我的同事拿着他在Stack Overflow上找到的一行 JavaScript 代码来问我它是怎么工作的。我觉得这段代码很好地展现了 JavaScript 中四个中高级概念,所以决定也在这里写下我的解释。
有问题的这行代码是这样的
const result = (({ a, c }) =>
({ a, c }))({ a: 1, b: 2, c: 3, d: 4 });
在继续阅读之前,先思考一下,看看你能不能自己想出答案。
准备好了吗?出发!
对象解构
对象解构是指批量地从对象中提取属性,而不是手动访问每个属性并将其赋值给变量。例如,假设某个函数接收一个对象作为参数,而你只想使用该对象的几个属性进行操作,对象解构就能实现这一点。
而不是这样做
const a = myObject.a;
const b = myObject.b;
doStuff(a, b);
我们可以将其简化为
const { a, b } = myObject;
doStuff(a, b);
这样做也能达到同样的效果。这大大缩短了代码量,尤其是在对对象的多个属性进行操作时。这样就省去了每次都编写完整属性路径的麻烦。
这里最巧妙的地方在于,这种解构方法适用于任何存在对象的地方。即使是在为函数赋值输入参数时也是如此。
const myFunction = (myObject) => {
console.log(myObject.a);
console.log(myObject.b);
};
可以写成
const myFunction = ({ a, b }) => {
console.log(a);
console.log(b);
};
对象速记形式
在组合对象时,我们经常会收到来自某个地方的传入参数,对其进行转换,然后返回一个新对象。这通常看起来像这样:
const someDescriptiveName = doStuff(a);
const someOtherDescriptiveName = doOtherStuff(b);
const newObject = {
someDescriptiveName: someDescriptiveName,
someOtherDescriptiveName: someOtherDescriptiveName,
};
如你所见,这种写法非常重复。我们不断地将一个同名变量的内容赋值给一个具有特定键值的属性。幸运的是,还有一种更简洁的写法。
const someDescriptiveName = doStuff(a);
const someOtherDescriptiveName = doOtherStufF(b);
const newObject = {
someDescriptiveName,
someOtherDescriptiveName,
};
我们只需输入一次变量名,JavaScript 就会明白我们想要一个与我们正在使用的变量同名的属性。
箭头函数中的隐式返回
当箭头方法只有 return 语句时,可以将其简化为隐式形式。我们经常编写只返回三元值、Promise 或简单计算结果的方法。在这种情况下,我们不需要完整的代码块来包裹函数内容。
而不是这样做
const multiplyByTwo = (inputNumber) => {
return inputNumber * 2;
};
我们可以删除return关键字并删除花括号(如果返回对象字面量,则用圆括号替换它们)。
const multiplyByTwo = (inputNumber) => inputNumber * 2;
提示:在 Visual Studio Code 中,您可以将文本光标放在箭头函数的箭头部分中间,然后按 Enter 键cmd + .调出快速修复菜单,您可以在其中快速添加或删除函数的大括号。
直接调用匿名函数
这是这四个概念中最少用到的,也可能是最容易让人困惑的。它允许我们立即调用箭头函数,而无需将其赋值给一个变量。
而不是这样做
const myLog = (text) => {
console.log('Hello ' + text);
};
myLog('world');
我们可以直接调用它,无需先赋值。
((text) => {
console.log('hello ' + text);
})('world');
这很少用到,但在某些情况下,当你需要在未标记为异步的上下文中调用异步方法时,它会很有用async。
回到我们那条令人困惑的线
有了这四个部分,我们现在就可以开始拆解这句令人困惑的句子,让它变得通顺易懂。如果你已经忘记了,没关系,这里再列一遍:
const result = (({ a, c }) =>
({ a, c }))({ a: 1, b: 2, c: 3, d: 4 });
我们从后面开始分析,可以看到这是一个立即调用的箭头函数。让我们把这个函数赋值给一个变量,然后调用这个变量。
const myFunction = ({ a, c }) => ({ a, c });
const result = myFunction({ a: 1, b: 2, c: 3, d: 4 });
为了更简洁一些,我们把输入对象移到一个变量里。
const myFunction = ({ a, c }) => ({ a, c });
const myObject = { a: 1, b: 2, c: 3, d: 4 };
const result = myFunction(myObject);
这样已经清晰多了。但我们继续。现在我们把注意力转移到箭头函数上,可以看到我们可以先加上花括号和 return 关键字。
const myFunction = ({ a, c }) => {
return { a, c };
};
const myObject = { a: 1, b: 2, c: 3, d: 4 };
const result = myFunction(myObject);
下一步是移除函数输入参数中的解构。
const myFunction = (inputObject) => {
const a = inputObject.a;
const c = inputObject.c;
return { a, c };
};
const myObject = { a: 1, b: 2, c: 3, d: 4 };
const result = myFunction(myObject);
最后一步是删除函数返回对象的简写形式。
const myFunction = (inputObject) => {
const a = inputObject.a;
const c = inputObject.c;
const newObject = {
a: a,
c: c,
};
return newObject;
};
const myObject = { a: 1, b: 2, c: 3, d: 4 };
const result = myFunction(myObject);
好了,就是这样。我们现在已经剔除了四个神秘的 JavaScript 概念,剩下的只需要基础知识就能理解。
复杂到什么程度才算过于复杂?
这类问题通常因开发者和团队而异。但作为开发者,你的代码应该始终易于阅读,无需过多修改。同时,我们也不能忽略语言中已有的概念,关键在于何时使用它们。
我会这样写这行:
const pickAC = ({ a, c }) => ({ a, c });
const myObject = { a: 1, b: 2, c: 3, d: 4 };
const result = pickAC(myObject);
这样比一行代码更易读,同时又保持了简洁明了。立即调用匿名函数是一个非常罕见的概念,在我看来,它应该只在绝对必要时才使用。不过,每个人都有自己的看法,只要确保团队内部达成共识即可。
文章来源:https://dev.to/perenstrom/four-tricky-javascript-concepts-in-one-line-of-code-3i3p