是否有 "while" 等价物可以用在 JavaScript 的括号内?

Is there a "while" equivalent that can be used inside a parenthetical statement in JavaScript?

我经常发现 JavScript 快捷方式很有用,例如替换 if/else 语句,如

if (a !== 0) {
  b = c;
} else {
  b = d;
}

b = a ? c : d;

或替换 for/next 循环如

for (let i = 0; i < array.length; i++) {
  element = array[i];
  /* do something with element... */
}

array.forEach(element => { /* do something with element... */ });

我特别喜欢这些和其他类似的快捷方式可以在括号语句中组合和使用,例如

array.forEach(e => (x += e ? a : b, e * x));

(有效)

但是,我一直无法找到与括号内的 "while" 语句等效的快捷方式或功能。有这种事吗?

我尝试在括号内使用普通的 while 语句,但出现错误

array.forEach(e => (while (e.length > 160) { e.replace(' ', ''); }, e));

(无效)

我知道上面可以用更长的形式重写,像这样

array.forEach(e => {
  while (e.length > 160) {
     e.replace(' ', '');
  }
  return e;
});

(有效)

但有时我真的更喜欢相当于 while 的内联 shorthand,而不是长版本。有办法吗?

你可以使用递归:

const cutSpace = str => str.length > 160
  ? cutSpace(str.replace(' ', ''))
  : str

const trimmedStrings = array.map(cutSpace)

如果您在循环中执行单个命令,则可以使用 for 循环;

array.forEach(e => for(; e.length > 160; e.replace(' ', ''));return e;})

这可能不会更短,但它很实用:

const array = [
  '       ',
  '      ',
  '     ',
  '    ',
  '   ',
  '  ',
  ' ',
  '',
];

const result = array.map(e => (f => (g => x => f(g(g))(x))(h => x => f(h(h))(x)))(f => e => e.length > 2 && f(e.replace(' ', '')) || e)(e) );

console.log( result );

我将 160 更改为 2 并使用 map 而不是 forEach 以便于演示。

I especially like that these and other similar shortcuts can be combined and used inside parenthetical statements, such as

array.forEach(e => (x += e ? a : b, e * x));

(works)

什么?此代码 "work" 的作用是什么?如果我在审查中看到这段代码,我会立即拒绝它。

  • 谓词中的外部状态突变?
  • 尽管如此,还是在循环中?
  • 更不用说 ab 都没有被使用。
  • 更不用说括号和尾随 e * x 表达式也完全被丢弃了... (?)

太疼了

I tried to use a normal while statement inside parentheses, but I got an error

array.forEach(e => (while (e.length > 160) { e.replace(' ', ''); }, e));

(doesn't work)

嗯,这是一个语法错误。您尝试使用 语句 ,其中只允许 表达式 。像 ifforwhiledo 这样的东西是 语句 。您不能将语句放在 (...) 表达式中。即使你可以在这里使用while表达式,代码仍然不会任何事情。每个计算都被完全丢弃

I know the above can be rewritten in a longer form, like so

array.forEach(e => {
  while (e.length > 160) {
     e.replace(' ', '');
  }
  return e;
});

(works)

"works"?这是如何运作的?从句法上讲没问题,但它实际上 没有做 任何事情。

  • 字符串是不可变的,所以 String.prototype.replace 不会像我想的那样发生变异 e
  • Array.prototype.forEach 忽略迭代器函数中的 return 值
  • Array.prototype.forEach 没有 return 自己的价值

我只能假设你想做这样的事情

let input = [
  'a b c d e f g h',
  'i j k l m n o',
  'p q r s t u',
  'v w x y z'
]

input.forEach(e => {
  while (e.length > 9)
    e = e.replace(' ', '')
  console.log(e)
})

// abcdefg h
// ijklm n o
// pqr s t u
// v w x y z

你看出区别了吗?我正在使用 e = e.replace(...) 因为 replace 不会就地改变输入字符串。另外,我的迭代器实际上正在用值做一些事情 – console.log,尽管它可能很愚蠢

看来您可能没有意识到 Array.prototype.map。像 replace 一样,map 不会改变原始输入——相反,一个新值是 returned。所以这次我们将映射的 return 值分配给一个新变量 output,并在完成后记录它

let input = [
  'a b c d e f g h',
  'i j k l m n o',
  'p q r s t u',
  'v w x y z'
]

let output = input.map(e => {
  while (e.length > 9)
    e = e.replace(' ', '')
  return e
})

console.log(output)
// [
//   "abcdefg h",
//   "ijklm n o",
//   "pqr s t u",
//   "v w x y z"
// ]

最后两个代码片段都不好。第一个有一个 I/O 副作用(forEach 中的 console.log),但第二个是完全纯粹的并且完全是功能性的。 local 突变没有错,在你的程序中使用 while 也没有错,特别是考虑到我所知道的没有 JavaScript VM 支持尾调用优化 - 递归其实不是JS的蜜蜂跪地