N次执行<something>(声明性语法)


90

Javascript中有没有一种方法可以轻松地编写如下代码:

[1,2,3].times do {
  something();
}

任何可能支持某些相似语法的库?

更新:澄清-我想something()每次数组元素迭代分别被调用1,2和3次


1
我想说的是,JS中没有这样的功能,而这是缺少的前5个功能。它对于测试软件非常有用,比什么都重要。
亚历山大·米尔斯

Answers:


46

这个答案基于Array.forEach,没有任何库,仅基于本地香草

要基本拨打something()3次,请使用:

[1,2,3].forEach(function(i) {
  something();
});

考虑以下功能:

function something(){ console.log('something') }

支出将是

something
something
something

要完成此问题,可以使用以下方法something()分别调用1、2和3次:

是2017年,您可以使用ES6:

[1,2,3].forEach(i => Array(i).fill(i).forEach(_ => {
  something()
}))

或在旧版本的ES5中:

[1,2,3].forEach(function(i) {
  Array(i).fill(i).forEach(function() {
    something()
  })
}))

在两种情况下,支出都会

支出将是

something

something
something

something
something
something

(一次,然后两次,然后3次)


18
这是不正确的,因为它不能满足问题的这一部分:“我希望something()被调用1,2和3次”。使用此代码something仅被调用3次,应被调用6次。
伊恩·纽森

然后,我猜它已被选为最佳答案,因为它可能是一个不错的起点。
–vinyl

3
您也可以根据实际索引的需要使用[...Array(i)]Array(i).fill()
Guido Bouman

如果您对传递的任何参数不感兴趣,请使用.forEach(something)
kvsm

85

只需使用一个循环:

var times = 10;
for(var i=0; i < times; i++){
    doSomething();
}

3
谢谢!我想从声明性语法中受益(就像茉莉花一样)
BreakPhreak 2012年

是的,但是功能性的for循环声明式语法也会更好
Alexander Mills

69

可能的ES6替代品。

Array.from(Array(3)).forEach((x, i) => {
  something();
});

而且,如果您希望“分别被调用1,2和3次”。

Array.from(Array(3)).forEach((x, i) => {
  Array.from(Array(i+1)).forEach((x, i2) => {
    console.log(`Something ${ i } ${ i2 }`)
  });
});

更新:

取自未定义的填充数组

这似乎是创建初始数组的一种更优化的方法,我还对其进行了更新,以使用@ felix-eve建议的第二个参数映射函数。

Array.from({ length: 3 }, (x, i) => {
  something();
});

3
我要警告一下,如果您只是快速编写一些脚本,这没关系,但是性能太差了,因此请不要将其用于密集递归或完全用于生产。
nverba

如果您打算使用ES6,则可以使用map()代替forEach()
Andy Ford

3
如果简洁是目标(实际上,即使不是),则传递函数而不是调用它:Array.from(Array(3)).forEach(something)
kvsm18年

1
也可以与react表达式渲染一起使用。
乔什·沙基

4
Array.from()有一个可选的第二个参数mapFn,该参数允许您在数组的每个元素上执行映射函数,因此无需使用forEach。您可以这样做:Array.from({length: 3}, () => somthing() )
菲利克斯·夏娃

19

由于您提到了下划线:

假设f您要调用的函数是:

_.each([1,2,3], function (n) { _.times(n, f) });

会成功的 例如,使用f = function (x) { console.log(x); },您将进入控制台: 0 0 1 0 1 2


确实,我以为你想分开。
ggozad 2012年

2
_(3).times(function(n){return n;});应该可以。请参阅此处的文档。
2014年

18

lodash

_.each([1, 2, 3], (item) => {
   doSomeThing(item);
});

//Or:
_.each([1, 2, 3], doSomeThing);

或者,如果您想做N次

const N = 10;
_.times(N, () => {
   doSomeThing();
});

//Or shorter:
_.times(N, doSomeThing);

请参考此链接进行lodash安装


12

创建一个数组,fill所有带有undefinedso map方法的项都可以工作:

Array.fill 没有IE支持

Array(5).fill().map(()=>{ 
   // Do this 5 times:
   console.log(111) 
})


使用老式的崇敬循环:

for( let i=5; i--; ){
   // Do this 5 times:
   console.log(222) 
}


暂时,我运行了50k次 uuid函数以确保它从未复制过uuid。所以我分析了顶部循环和底部循环,只是为了踢球,如果我不傻的话,它们只是使用chrome dev工具正常页面加载的中间运行,我认为使用Array.indexOf()它的〜12亿比较加生成50k uuid亿。newschool = 1st-5561.2ms 2nd-5426.8ms | oldschool = 1st-4966.3ms / 2nd-4929.0ms故事的寓意,如果您不在十亿以上的范围内,您将永远不会注意到运行200、1k甚至10k次做某事的差异。想像有人可能像我一样好奇。
rifi2k '19

这是正确的,并且已经知道很多年了。提出不同的方法不是为了提高速度,而是为了获得较旧的浏览器支持。
vsync

2
显然,每个阅读此线程的人都知道您没有提供示例来比较它们的速度。我只是碰巧用它们进行了一些测试,并发现我会分享一些信息,以便将来有人会觉得有趣。我不是很正确,因为我只是在显示信息时并没有真正回答一个问题,而是提醒您不要做循环的速度,因为您只能做一些事情而无论如何都会花费几毫秒。也不是很清楚,因为一年前的相同测试可能会因为浏览器一直在变化而使速度降低50%。
rifi2k

9

您还可以通过以下方式进行销毁操作

[...Array(3)].forEach( _ => console.log('do something'));

或者如果您需要索引

[...Array(3)].forEach(( _, index) => console.log('do something'));

7

如果您不能使用Underscorejs,则可以自己实现。通过将新方法附加到Number和String原型上,可以做到这一点(使用ES6箭头功能):

// With String
"5".times( (i) => console.log("number "+i) );

// With number variable
var five = 5;
five.times( (i) => console.log("number "+i) );

// With number literal (parentheses required)
(5).times( (i) => console.log("number "+i) );

您只需要创建一个函数表达式(具有任何名称)并将其分配给您想要以以下方式访问它的任何属性名称(在原型上):

var timesFunction = function(callback) {
  if (typeof callback !== "function" ) {
    throw new TypeError("Callback is not a function");
  } else if( isNaN(parseInt(Number(this.valueOf()))) ) {
    throw new TypeError("Object is not a valid number");
  }
  for (var i = 0; i < Number(this.valueOf()); i++) {
    callback(i);
  }
};

String.prototype.times = timesFunction;
Number.prototype.times = timesFunction;

1
我将不得不重新研究修补原型的糟糕程度,但通常情况很好
Alexander Mills

2

有一个很棒的库叫做Ramda,它类似于Underscore和Lodash,但是功能更强大。

const R = require('ramda');

R.call(R.times(() => {
    console.log('do something')
}), 5);

Ramda包含许多有用的功能。请参阅Ramda文档


我喜欢这个库,它是一种现代而优雅的FP解决方案。
momocow

1

您可以使用数组长度来执行任务次数。

var arr = [1,2,3];

for(var i=0; i < arr.length; i++){
    doSomething();
}

要么

 var arr = [1,2,3];

 do
 {


 }
 while (i++ < arr.length);


1
times = function () {
    var length = arguments.length;
    for (var i = 0; i < length ; i++) {
        for (var j = 0; j < arguments[i]; j++) {
            dosomthing();
        }
    }
}

您可以这样称呼它:

times(3,4);
times(1,2,3,4);
times(1,3,5,7,9);

+1-利用本地JavaScript功能调用带有可变数量参数的函数。无需额外的库。不错的解决方案
RustyTheBoyRobot 2012年

1
// calls doSomething 42 times
Array( 42 ).join( "x" ).split( "" ).forEach( doSomething );

// creates 42 somethings
var somethings = Array( 42 ).join( "x" ).split( "" ).map( () => buildSomething(); );

或(通过https://stackoverflow.com/a/20066663/275501

Array.apply(null, {length: 42}).forEach( doSomething );

1
var times = [1,2,3];

for(var i = 0; i < times.length;  i++) {
  for(var j = 0; j < times[i];j++) {
     // do something
  }
}

使用jQuery .each()

$([1,2,3]).each(function(i, val) {
  for(var j = 0; j < val;j++) {
     // do something
  }
});

要么

var x = [1,2,3];

$(x).each(function(i, val) {
  for(var j = 0; j < val;j++) {
     // do something
  }
});

编辑

您可以使用纯JS进行以下操作:

var times = [1,2,3];
times.forEach(function(i) {
   // do something
});

0

只需使用嵌套循环(可能包含在函数中)

function times( fct, times ) {
  for( var i=0; i<times.length; ++i ) {
    for( var j=0; j<times[i]; ++j ) {
      fct();
    }
  }
}

然后像这样调用它:

times( doSomething, [1,2,3] );

0

这些答案都很好,而且IMO @Andreas是最好的,但是在JS中很多次我们都必须异步执行操作,在这种情况下,异步可以解决:

http://caolan.github.io/async/docs.html#times

const async = require('async');

async.times(5, function(n, next) {
    createUser(n, function(err, user) {
        next(err, user);
    });
}, function(err, users) {
    // we should now have 5 users
});

这些“时间”功能对于大多数应用程序代码而言非常有用,但对于测试应该有用。


0
const loop (fn, times) => {
  if (!times) { return }
  fn()
  loop(fn, times - 1)
}

loop(something, 3)

0

给定一个功能something

function something() { console.log("did something") }

原型中times添加了一个新方法Array

Array.prototype.times = function(f){
  for(v of this) 
    for(var _ of Array(v))
      f();
}

这段代码:

[1,2,3].times(something)

输出此:

did something
did something
did something
did something
did something
did something

认为哪一个可以回答您的最新问题(5年后),但我想知道在数组上进行这项工作有多有用?效果是否与call相同[6].times(something),后者又可以写成:

for(_ of Array(6)) something();

(尽管将_用作垃圾变量可能会破坏lodash或下划线,如果您正在使用它)


1
将自定义方法添加到本机JS对象被认为是不好的做法。
Lior Elrom '18 / 08/29

您可以至少使用letin for (let _ of Array(6)) something()来防止lodash的破坏。
Ciro Santilli郝海东冠状病六四事件法轮功

0

Array.from(ES6)

function doSomthing() {
    ...
}

像这样使用它:

Array.from(Array(length).keys()).forEach(doSomthing);

要么

Array.from({ length }, (v, i) => i).forEach(doSomthing);

要么

// array start counting from 1
Array.from({ length }, (v, i) => ++i).forEach(doSomthing);

0

使用Array.from.forEach

let length = 5;
Array.from({length}).forEach((v, i) => {
  console.log(`#${i}`);
});


0

假设我们可以使用诸如扩展运算符之类的ES6语法,那么我们将要做的事情与集合中所有数字之和的次数一样多。

在这种情况下,如果次数等于[1,2,3],则总次数将为6,即1 + 2 + 3。

/**
 * @param {number[]} times
 * @param {cb} function
 */
function doTimes(times, cb) {
  // Get the sum of all the times
  const totalTimes = times.reduce((acc, time) => acc + time);
  // Call the callback as many times as the sum
  [...Array(totalTimes)].map(cb);
}

doTimes([1,2,3], () => console.log('something'));
// => Prints 'something' 6 times

如果构造和扩展数组的逻辑不明显,那么这篇文章应该会有所帮助


0

TypeScript实现:

对于那些对如何实施String.times以及Number.times以安全类型和兼容的方式感兴趣的人thisArg,请继续:

declare global {
    interface Number {
        times: (callbackFn: (iteration: number) => void, thisArg?: any) => void;
    }
    interface String {
        times: (callbackFn: (iteration: number) => void, thisArg?: any) => void;
    }
}

Number.prototype.times = function (callbackFn, thisArg) {
    const num = this.valueOf()
    if (typeof callbackFn !== "function" ) {
        throw new TypeError("callbackFn is not a function")
    }
    if (num < 0) {
        throw new RangeError('Must not be negative')
    }
    if (!isFinite(num)) {
        throw new RangeError('Must be Finite')
    }
    if (isNaN(num)) {
        throw new RangeError('Must not be NaN')
    }

    [...Array(num)].forEach((_, i) => callbackFn.bind(thisArg, i + 1)())
    // Other elegant solutions
    // new Array<null>(num).fill(null).forEach(() => {})
    // Array.from({length: num}).forEach(() => {})
}

String.prototype.times = function (callbackFn, thisArg) {
    let num = parseInt(this.valueOf())
    if (typeof callbackFn !== "function" ) {
        throw new TypeError("callbackFn is not a function")
    }
    if (num < 0) {
        throw new RangeError('Must not be negative')
    }
    if (!isFinite(num)) {
        throw new RangeError('Must be Finite')
    }
    // num is NaN if `this` is an empty string 
    if (isNaN(num)) {
        num = 0
    }

    [...Array(num)].forEach((_, i) => callbackFn.bind(thisArg, i + 1)())
    // Other elegant solutions
    // new Array<null>(num).fill(null).forEach(() => {})
    // Array.from({length: num}).forEach(() => {})
}

可以在此处找到指向TypeScript Playground的链接以及一些示例。

这篇文章实现了由以下人员发布的解决方案:AndreasBergströmvinylOzay DumanSeregPie

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.