const cars = [ { model: 'renault', year: 1956 }, { model: 'peugeot', year: 1968 }, { model: 'ford', year: 1977 } ]; const checkForAnyModel = (model) => { return cars.some(car => car.model === model); } console.log(checkForAnyModel('renault')); // 输出 true
5. 提前返回而不是使用 if...else 分支
当我还是学生的时候,就有人教过我:一个函数应该只有一个返回语句,并且只从一个地方返回。如果细心处理,这个方法倒也还好。我这么说也就意味着,我们应该意识到它在某些情况下可能会引起条件式嵌套地狱。如果不受控制,多个分支和 if...else 嵌套将会让我们感到很痛苦。
另一方面,如果代码库很大且包含很多行代码,位于深层的一个返回语句可能会带来问题。现在我们都实行关注点分离和 SOLID 原则,因此,代码行过多这种情况挺罕见的。
举例来解释这个问题。假设我们想要显示所给车辆的模型和生产年份:
const checkModel = (car) => { let result; // 首先,定义一个 result 变量 // 检查是否有车 if(car) { // 检查是否有车的模型 if (car.model) { // 检查是否有车的年份 if(car.year) { result = `Car model: ${car.model}; Manufacturing year: ${car.year};`; } else { result = 'No car year'; } } else { result = 'No car model' } } else { result = 'No car'; } return result; // 我们的单独的返回语句 } console.log(checkModel()); // 输出 'No car' console.log(checkModel({ year: 1988 })); // 输出 'No car model' console.log(checkModel({ model: 'ford' })); // 输出 'No car year' console.log(checkModel({ model: 'ford', year: 1988 })); // 输出 'Car model: ford; Manufacturing year: 1988;'
正如你所看到的,即使本例的问题很简单,上面的代码也实在太长了。可以想象一下,如果我们有更加复杂的逻辑会发生什么事。大量的 if...else 语句。
我们可以重构上面的函数,分解成多个步骤并稍做改善。例如,使用三元操作符,包括 && 条件式等。不过,这里我直接跳到最后,向你展示借助现代 JavaScript 特性和多个返回语句,代码可以有多简洁。
const checkModel = ({model, year} = {}) => { if(!model && !year) return 'No car'; if(!model) return 'No car model'; if(!year) return 'No car year'; // 这里可以任意操作模型或年份 // 确保它们存在 // 无需更多检查 // doSomething(model); // doSomethingElse(year); return `Car model: ${model}; Manufacturing year: ${year};`; } console.log(checkModel()); // 输出 'No car' console.log(checkModel({ year: 1988 })); // 输出 'No car model' console.log(checkModel({ model: 'ford' })); // 输出 'No car year' console.log(checkModel({ model: 'ford', year: 1988 })); // 输出 'Car model: ford; Manufacturing year: 1988;'
在重构版本中,我们包含了解构和默认参数。默认参数确保我们在传入 undefined 时有可用于解构的值。注意,如果传入 null ,函数将会抛出错误。这也是之前那个方法的优点所在,因为那个方法在传入 null 的时候会输出 'No car'。
对象解构确保函数只取所需。例如,如果我们在给定车辆对象中包含额外属性,则该属性在我们的函数中是无法获取的。
根据偏好,开发者会选择其中一种方式。实践中,编写的代码通常介于两者之间。很多人觉得 if...else 语句更容易理解,并且有助于他们更为轻松地遵循程序流程。
6. 使用索引或者映射,而不是 switch 语句
假设我们想要基于给定的国家获取汽车模型。
const getCarsByState = (state) => { switch (state) { case 'usa': return ['Ford', 'Dodge']; case 'france': return ['Renault', 'Peugeot']; case 'italy': return ['Fiat']; default: return []; } } console.log(getCarsByState()); // 输出 [] console.log(getCarsByState('usa')); // 输出 ['Ford', 'Dodge'] console.log(getCarsByState('italy')); // 输出 ['Fiat']
上诉代码可以重构,完全去除 switch 语句。
const cars = new Map() .set('usa', ['Ford', 'Dodge']) .set('france', ['Renault', 'Peugeot']) .set('italy', ['Fiat']); const getCarsByState = (state) => { return cars.get(state) || []; } console.log(getCarsByState()); // 输出 [] console.log(getCarsByState('usa')); //输出 ['Ford', 'Dodge'] console.log(getCarsByState('italy')); // 输出 ['Fiat']
或者,我们还可以为包含可用汽车列表的每个国家创建一个类,并在需要的时候使用。不过这个就是题外话了,本文的主题是关于条件句的。更恰当的修改是使用对象字面量。
const carState = { usa: ['Ford', 'Dodge'], france: ['Renault', 'Peugeot'], italy: ['Fiat'] }; const getCarsByState = (state) => { return carState[state] || []; } console.log(getCarsByState()); // 输出 [] console.log(getCarsByState('usa')); // 输出 ['Ford', 'Dodge'] console.log(getCarsByState('france')); // 输出 ['Renault', 'Peugeot']
7. 使用自判断链接和空合并