试着不用循环撸代码,是件很有趣的事,而且,万一你领会了什么是“数据即代码,代码即数据”呢?
这样做有什么意义吗?
事实上,它可以迫使你从不同的角度寻找解决方法,也许可以找到更好的方法。
当然,使用循环语句没有任何不对的地方。但是,不使用循环的话,有时候可以增加代码的可读性。这一点并不是绝对的,如果完全不使用循环语句的话,代码可读性也许会更差。这需要你根据不同情况去判断。
而且,不用循环的话不只是影响可读性。在这背后隐含着更加深刻的道理。通过了解本文的代码示例,你可以发现,如果不使用循环语句的话,你的代码会更加接近代码即数据的概念。
另外,当你尝试不使用循环语句去编程时,也是一件非常有意思的事情。
示例1: 统计数组中的奇数
假设我们有一个整数数组 arrayOfIntegers,现在需要统计其中奇数的个数:
const arrayOfIntegers = [1, 4, 5, 9, 0, -1, 5];
使用 if
let counter = 0;
arrayOfIntegers.forEach(integer => {
const remainder = Math.abs(integer % 2);
if(remainder === 1) {
counter++;
}
});
console.log(counter);
不用if
let counter = 0;
arrayOfIntegers.forEach(integer => {
const remainder = Math.abs(integer % 2);
counter += remainder;
});
console.log(counter);
不用 if 时,我们巧妙地利用了奇数与偶数的特性,它们除以 2 的余数分别是 0 和 1。
示例2: 判断工作日和周末
给定一个日期(比如 new Date()),判断它是工作日还是周末,分别返回”weekend”和”weekday”。
使用if
const weekendOrWeekday = inputDate => {
const day = inputDate.getDay();
if(day === 0 || day === 6) {
return'weekend';
}
return'weekday';
// Or, for ternary fans:
// return (day === 0 || day === 6) ? 'weekend' : 'weekday';
};
console.log(weekendOrWeekday(newDate()));
不用if
const weekendOrWeekday = inputDate => {
const day = inputDate.getDay();
return weekendOrWeekday.labels[day] || weekendOrWeekday.labels['default'];
};
weekendOrWeekday.labels = {
0: 'weekend',
6: 'weekend',
default: 'weekday'
};
console.log(weekendOrWeekday(new Date()));
你是否发现 if 语句中其实隐含着一些信息呢?它告诉我们哪一天是周末,哪一天是工作日。因此,要去掉 if 语句的话,我们只需要把这些信息写入 weekendOrWeekday.labels 对象,然后直接使用它就好了。
示例3: doubler 函数
写一个 doubler 函数,它会根据参数的类型,进行不同的操作:
- 如果参数是数字,则乘以 2(i.e. 5 => 10, -10 => -20);
- 如果参数是字符串,则每个字符重复 2 次 (i.e. ‘hello’ => ‘hheelloo’);
- 如果参数是函数,则调用 2 次;
- 如果参数是数组,则将每一个元素作为参数,调用 doubler 函数
- 如果参数是对象,则将每个属性值作为参数,调用 doubler 函数
使用 switch
const doubler = input => {
switch(typeof input) {
case 'number':
return input + input;
case 'string':
return input.split('').map(letter => letter + letter).join('');
case 'object':
Object.keys(input).map(key => (input[key] = doubler(input[key])));
return input;
case 'function':
input();
input();
}
};
console.log(doubler(-10));
console.log(doubler('hey'));
console.log(doubler([5, 'hello']));
console.log(doubler({ a: 5, b: 'hello'}));
console.log(doubler(function() {
console.log('call-me');
}));
不用switch
const doubler = input => {
return doubler.operationsByType[typeof input](input);
};
doubler.operationsByType = {
number: input => input + input,
string: input => input.split('').map(letter => letter + letter).join(''),
function: input => {
input();
input();
},
object: input => {
Object.keys(input).map(key => (input[key] = doubler(input[key])));
return input;
}
};
console.log(doubler(-10));
console.log(doubler('hey'));
console.log(doubler([5, 'hello']));
console.log(doubler({ a: 5, b: 'hello'}));
console.log(doubler(function() {
console.log('call-me');
}));
可知,我将每一种参数类型对应的操作绑定到了 doubler.operationsByType,这样不需要 switch 语句,就可以实现 doubler 函数了。
示例4: 数组元素求和
数组如下:
const arrayOfNumbers = [17, -4, 3.2, 8.9, -1.3, 0, Math.PI];
使用循环语句
let sum = 0;
arrayOfNumbers.forEach(number => {
sum += number;
});
console.log(sum);
可知,我们需要通过修改 sum 变量,来计算结果。
不用循环语句
使用 reduce 方法时,就可以避免使用循环语句了:
const sum = arrayOfNumbers.reduce((acc, number) =>
acc + number
);
console.log(sum);
实现递归,同样可以避免使用循环语句:
const sum = ([number, ...rest]) => {
if (rest.length === 0) {
return number || 0;
}
return number + sum(rest);
};
console.log(sum(arrayOfNumbers))
可知,代码中巧妙地使用了一个ES6语法 - 扩展运算符。rest 代表了除去第一个元素之后的剩余数组,它的元素个数会随着一层层递归而减小直至为 0,这样就满足了递归结束的条件。这种写法非常机智,但是在我看来,可读性并没有使用 reduce 方法那么好。
示例5: 将数组中的字符串拼接成句子
数组如下,我们需要过滤掉非字符串元素:
const dataArray = [0, 'H', {}, 'e', Math.PI, 'l', 'l', 2/9, 'o!'];
目标结果是“Hello!”.
使用循环语句
let string = '', i = 0;
while (dataArray[i] !== undefined) {
if (typeof dataArray[i] === 'string') {
string += dataArray[i];
}
i += 1;
}
console.log(string);
不用循环语句
使用 filter 和 join 方法的话,可以避免使用循环语句:
const string = dataArray.filter(e => typeof e === 'string').join('');
console.log(string);
可知,使用 filter 方法还帮助我们省掉了 if 语句。
示例6: 将数组元素变换为对象
数组元素为一些书名,需要将它们转换为对象,并为每一个对象添加 ID:
const booksArray = [
'Clean Code',
'Code Complete',
'Introduction to Algorithms',
];
目标结果如下:
newArray = [
{ id: 1, title: 'Clean Code' },
{ id: 2, title: 'Code Complete' },
{ id: 3, title: 'Introduction to Algorithms' },
];
使用循环语句
const newArray = [];
let counter = 1;
for (let title of booksArray) {
newArray.push({
id: counter,
title,
});
counter += 1;
}
console.log(newArray);
不用循环语句
使用 map 方法的话,可以避免使用循环语句:
const newArray = booksArray.map((title, index) => ({
id: index + 1,
title
}));
console.log(newArray);
一张图片解释 map、filter 和 reduce 的作用