重复字符串 – Javascript

什么是最好或最简洁的方法返回一个字符串重复任意次的时间?

以下是我迄今为止的最佳射门:

function repeat(s, n){ var a = []; while(a.length < n){ a.push(s); } return a.join(''); } 

注意新读者:这个答案是旧的,而且不是非常实用 – 它只是“聪明”,因为它使用数组的东西来完成字符串的事情。 当我写下“较少的过程”时,我的意思是“较少的代码”,因为正如其他人在后来的答案中指出的那样,它表现得像猪一样。 所以如果速度对你来说不重要的话。

我会把这个函数直接放到String对象上。 而不是创建一个数组,填充它,并与一个空的字符连接,只需创建一个适当长度的数组,并加入你想要的字符串。 同样的结果,更少的过程!

 String.prototype.repeat = function( num ) { return new Array( num + 1 ).join( this ); } alert( "string to repeat\n".repeat( 4 ) ); 

我测试了所有提议的方法的性能。

这是我得到的最快的变体

 String.prototype.repeat = function(count) { if (count < 1) return ''; var result = '', pattern = this.valueOf(); while (count > 1) { if (count & 1) result += pattern; count >>= 1, pattern += pattern; } return result + pattern; }; 

或作为独立功能:

 function repeat(pattern, count) { if (count < 1) return ''; var result = ''; while (count > 1) { if (count & 1) result += pattern; count >>= 1, pattern += pattern; } return result + pattern; } 

它基于artistoex算法。 这真的很快。 count越大,与传统的new Array(count + 1).join(string)方法相比, count速度就越快。

我只改变了两件事:

  1. 取代pattern = thispattern = this.valueOf() (清除一个明显的类型转换);
  2. if (count < 1)从prototypejs检查到函数顶部添加,以排除在这种情况下不必要的行动。
  3. 应用丹尼斯 答案优化(5-7%加速)

UPD

在这里为有兴趣的人创造一个小小的表演测试场。

变量count 〜0 .. 100:
测试String.repeat()的不同变体的性能http://tinyurl.com/kb3raxr

常数= 1024:
测试String.repeat()的不同变体的性能http://tinyurl.com/k527auo

如果可以的话,使用它,并使之更快)

这一个是非常有效的

 String.prototype.repeat = function(times){ var result=""; var pattern=this; while (times > 0) { if (times&1) result+=pattern; times>>=1; pattern+=pattern; } return result; }; 

这个问题是JavaScript的一个众所周知的/“经典”的优化问题,这是由于JavaScript字符串是“不可变的”,而且甚至单个字符与字符串的连接需要创建,包括内存分配和复制,一个完整的新字符串。

不幸的是,在这个页面上接受的答案是错误的,其中“错误”是指对于简单的一个字符的字符串的性能因子是3x,对于短的字符串重复多次是8x-97x,对于重复的句子是300x,在n趋于无穷大时,将算法复杂度的比值限制在一个极限内。 另外,在这个页面上还有另外一个答案,几乎是正确的(基于过去13年在整个互联网上传播的正确解决方案的许多代和变化之一)。 然而,这个“几乎正确”的解决方案忽略了正确算法的一个关键点,导致50%的性能下降。

JS性能结果为接受的答案,表现最好的其他答案(基于这个答案中的原始算法的降级版本),这个答案使用我的算法创建13年前

〜2000年10月我发表了一个广泛适应,修改,然后最终被理解和遗忘的确切问题的算法。 为了解决这个问题,2008年8月,我发表了一篇文章http://www.webreference.com/programming/javascript/jkm3/3.html,解释了算法,并将其作为简单的通用JavaScript优化的例子。; 到目前为止, Web Reference已经从本文中清除了我的联系信息,甚至是我的名字。 算法再一次被广泛地改编,修改,然后很难理解和被遗忘。

原始字符串重复/乘法JavaScript算法由Joseph Myers,作为Text.js中的文本乘法函数,大约在Y2K; 2008年8月由Web Reference以这种形式发布: http : //www.webreference.com/programming/javascript/jkm3/3.html (本文使用该函数作为JavaScript优化的一个例子,这是唯一用于奇怪名称“stringFill3”。)

 /* * Usage: stringFill3("abc", 2) == "abcabc" */ function stringFill3(x, n) { var s = ''; for (;;) { if (n & 1) s += x; n >>= 1; if (n) x += x; else break; } return s; } 

在这篇文章发表后的两个月内,同样的问题被发布到Stack Overflow,直到现在,这个问题的原始算法再次被遗忘。 这个堆栈溢出页面上可用的最佳解决方案是我的解决方案的修改版本,可能分隔几代。 不幸的是,这些修改破坏了解决方案的最优性。 实际上,通过改变我原来的循环结构,修改后的解决方案完全不需要额外的指数复制步骤(因此将正确答案中使用的最大字符串与其自身的额外时间相加,然后丢弃)。

下面接着讨论一些与所有这个问题的答案有关的JavaScript优化,并为所有人带来好处。

技巧:避免引用对象或对象属性

为了说明这种技术是如何工作的,我们使用一个真实的JavaScript函数,它可以创建任意长度的字符串。 正如我们将看到的,可以添加更多的优化!

像这里使用的函数是创建填充以对齐文本列,格式化货币,或填充块数据的边界。 文本生成功能还允许可变长度输入来测试任何其他功能的文本操作。 这个函数是JavaScript文本处理模块的重要组成部分之一。

随着我们的继续,我们将覆盖两个最重要的优化技术,同时将原始代码开发成优化的创建字符串的算法。 最终的结果是我所用的工业强度,高性能功能 – 以JavaScript订单形式对齐项目价格和总计,数据格式化和电子邮件/文本消息格式化以及许多其他用途。

创建字符串的原始代码stringFill1()

 function stringFill1(x, n) { var s = ''; while (s.length < n) s += x; return s; } /* Example of output: stringFill1('x', 3) == 'xxx' */ 

这里的语法是清楚的。 正如你所看到的,在进行更多的优化之前,我们已经使用了局部函数变量。

请注意,有一个无辜的代码中的对象属性s.length引用伤害了它的性能。 更糟糕的是,使用这个对象属性通过假设读者知道JavaScript字符串对象的属性来降低程序的简单性。

这个对象属性的使用破坏了计算机程序的一般性。 该程序假定x必须是一个长度为1的字符串。 这限制了stringFill1()函数的应用,除了单个字符的重复之外。 即使单个字符包含多个字节(如HTML实体) &nbsp;也不能使用&nbsp;

由于不必要的使用对象属性而导致的最糟糕的问题是,如果在空的输入字符串x上测试,该函数将创建一个无限循环。 为了检查一般性,应用程序尽可能少的输入。 当被要求超出可用内存量时,崩溃的程序有借口。 像这样一个程序崩溃时,要求什么都不能生产是不能接受的。 有时候漂亮的代码是有毒的代码。

简单可能是计算机编程的一个模糊的目标,但通常不是。 当一个程序缺乏一般性的合理程度时,说“程序已经足够好了”是无效的。 正如你所看到的,使用string.length属性可以防止这个程序在一般设置下工作,事实上,不正确的程序已经准备好导致浏览器或系统崩溃。

有没有一种方法来提高这个JavaScript的性能,以及照顾这两个严重的问题?

当然。 只要使用整数。

用于创建字符串的优化代码stringFill2()

 function stringFill2(x, n) { var s = ''; while (n-- > 0) s += x; return s; } 

时序代码来比较stringFill1()stringFill2()

 function testFill(functionToBeTested, outputSize) { var i = 0, t0 = new Date(); do { functionToBeTested('x', outputSize); t = new Date() - t0; i++; } while (t < 2000); return t/i/1000; } seconds1 = testFill(stringFill1, 100); seconds2 = testFill(stringFill2, 100); 

到目前为止stringFill2()的成功

stringFill1()需要47.297微秒(百万分之一秒)来填充一个100字节的字符串,而stringFill2()需要27.68微秒来完成同样的事情。 通过避免引用对象属性,这几乎是性能的两倍。

技巧:避免添加短字符串到长字符串

我们以前的结果看起来不错 – 事实上非常好。 改进后的函数stringFill2()要快得多,因为我们使用了前两个优化。 如果我告诉你可以改进的速度比现在快很多倍,你会相信吗?

是的,我们可以完成这个目标。 现在我们需要解释我们如何避免将短字符串附加到长字符串中。

与我们原来的功能相比,短期行为看起来相当不错。 计算机科学家喜欢分析函数或计算机程序算法的“渐近行为”,这意味着通过用更大的输入来测试它的长期行为。 有时候,如果不进行进一步的测试,就不会意识到计算机程序可以改进的方法。 为了看看会发生什么,我们将创建一个200字节的字符串。

stringFill2()显示的问题

使用我们的定时函数,我们发现200字节的字符串的时间增加到62.54微秒,而100字节的字符串的时间增加到27.68。 看起来,做两倍的工作时间应该增加一倍,而不是三倍或四倍。 从编程经验来看,这个结果似乎很奇怪,因为如果有的话,函数应该稍微快一点,因为工作更有效率(每个函数调用200字节,而不是每个函数调用100字节)。 这个问题与JavaScript字符串的阴险属性有关:JavaScript字符串是“不可变的”。

不可变意味着一旦创建了字符串就不能改变它。 通过一次添加一个字节,我们不会再花费一个字节的努力。 我们实际上是重新创建整个字符串再加上一个字节。

实际上,为了给一个100字节的字符串增加一个字节,需要101个字节的工作。 我们来简单分析一下创建一个N字节的字符串的计算成本。 添加第一个字节的成本是1个计算单位。 添加第二个字节的成本不是一个单位,而是两个单位(将第一个字节复制到新的字符串对象以及添加第二个字节)。 第三个字节需要3个单位的成本等。

C(N) = 1 + 2 + 3 + ... + N = N(N+1)/2 = O(N^2) 。 符号O(N^2)的读数为N的平方大O,这意味着长期的计算成本与字符串长度的平方成正比。 要创建100个字符需要10,000个工作单元,创建200个字符需要4万个工作单元。

这就是为什么创建超过100个字符的200个字符的两倍多。 事实上,它应该花费四倍的时间。 我们的编程经验是正确的,因为对于较长的字符串而言,这项工作的效率稍高一些,因此只花了大约三倍的时间。 一旦函数调用的开销变得微不足道,我们正在创建一个字符串多长时间,实际上需要四倍的时间来创建一个字符串两倍的长度。

(历史记录:此分析不一定适用于源代码中的字符串,例如html = 'abcd\n' + 'efgh\n' + ... + 'xyz.\n' ,因为JavaScript源代码编译器可以在将字符串合并为一个JavaScript字符串对象之前将这些字符串连接在一起,就在几年前,当加载由加号连接的长字符串的源代码时,JavaScript的KJS实现会冻结或崩溃,因为计算时间为O(N^2)制作超过Konqueror Web浏览器或使用KJS JavaScript引擎核心的Safari的网页并不困难,当我开发标记语言和JavaScript标记语言解析器时,首先遇到了这个问题,然后我发现了当我为JavaScript包含脚本编写脚本时导致问题的原因。)

显然,这种性能的急剧下降是一个巨大的问题。 我们如何处理它,因为我们不能改变JavaScript的方式来处理字符串作为不可变的对象吗? 解决方法是使用一种算法,尽可能少地重新创建字符串。

为了澄清,我们的目标是避免向长字符串添加短字符串,因为为了添加短字符串,整个长字符串也必须被复制。

该算法如何工作以避免将短字符串添加到长字符串中

这是减少新字符串对象创建次数的好方法。 将较长的字符串连接在一起,以便一次将多个字节添加到输出中。

例如,要创建一个长度为N = 9的字符串:

 x = 'x'; s = ''; s += x; /* Now s = 'x' */ x += x; /* Now x = 'xx' */ x += x; /* Now x = 'xxxx' */ x += x; /* Now x = 'xxxxxxxx' */ s += x; /* Now s = 'xxxxxxxxx' as desired */ 

这样做需要创建一个长度为1的字符串,创建一个长度为2的字符串,创建一个长度为4的字符串,创建一个长度为8的字符串,最后创建一个长度为9的字符串。我们保存了多少成本?

旧成本C(9) = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 9 = 45

新的成本C(9) = 1 + 2 + 4 + 8 + 9 = 24

请注意,我们必须将长度为1的字符串添加到长度为0的字符串中,然后将长度为1的字符串添加到长度为1的字符串,然后将长度为2的字符串添加到长度为2的字符串,然后将长度为4的字符串转换为长度为4的字符串,然后是长度为8的字符串转换为长度为1的字符串,以获得长度为9的字符串。我们正在做的事情可以概括为避免将短字符串添加到长字符串或其他字,试图连接在一起的字符串是相等或接近相等的长度。

对于旧的计算成本,我们找到了一个公式N(N+1)/2 。 有新的成本公式吗? 是的,但是很复杂。 重要的是它是O(N) ,所以加倍的字符串长度大约会使工作量增加一倍,而不是翻两番。

实现这个新想法的代码几乎和计算成本的公式一样复杂。 当你阅读它时,请记住>>= 1意味着向右移动1个字节。 所以如果n = 10011是一个二进制数,那么n >>= 1结果是n = 1001

您可能不会识别的代码的另一部分是按位和操作符,写成& 。 如果n & 1的最后一个二进制数字为1,则表达式n & 1真;如果n的最后一个二进制数字为0,则表达式为假。

新的高效的stringFill3()函数

 function stringFill3(x, n) { var s = ''; for (;;) { if (n & 1) s += x; n >>= 1; if (n) x += x; else break; } return s; } 

未经训练的眼睛看起来很丑陋,但表现却不亚于可爱。

让我们看看这个函数的表现如何。 看到结果后,很可能永远不会忘记O(N^2)算法和O(N)算法之间的区别。

stringFill1()需要88.7微秒(百万分之一秒)来创建一个200字节的字符串, stringFill2()需要62.54,而stringFill3()只需要4.608。 是什么让这个算法好多了? 所有这些函数都利用了局部函数变量,但是利用第二个和第三个优化技术为stringFill3()性能增加了二十倍。

更深入的分析

是什么让这个特殊的功能把水的竞争打败了呢?

正如我所提到的,这两个函数stringFill1()stringFill2()运行得非常缓慢的原因是JavaScript字符串是不可变的。 内存不能被重新分配,以允许一次添加一个字节到JavaScript存储的字符串数据中。 每次在字符串的末尾添加一个字节,整个字符串就会从头到尾重新生成。

因此,为了提高脚本的性能,必须提前计算长度较长的字符串,并将两个字符串提前连接起来,然后递归地构建所需的字符串长度。

例如,要创建一个16个字母的字符串,首先需要预先计算一个两字节的字符串。 然后,这两个字节的字符串将被重新使用来预先计算一个四字节的字符串。 然后,四字节字符串将被重新使用来预先计算一个八字节的字符串。 最后,将重新使用两个八字节的字符串来创建所需的16字节的新字符串。 总共需要创建四个新的字符串,长度为2,长度为4,长度为8,长度为16,总成本为2 + 4 + 8 + 16 = 30。

从长远来看,这个效率可以通过以相反的顺序相加并且使用从第一项a1 = N开始并具有r = 1/2的公共比率的几何级数来计算。 几何级数的和由下式给出: a_1 / (1-r) = 2N

这比添加一个字符来创建一个长度为2的新字符串更有效率,从而创建一个长度为3,4,5等等的新字符串,直到16为止。以前的算法使用了一次添加一个字节的过程,总成本为n (n + 1) / 2 = 16 (17) / 2 = 8 (17) = 136

很明显,136比30大得多,所以以前的算法需要花费更多的时间来构建一个字符串。

为了比较这两种方法,你可以看到递归算法(也称为“分而治之”)在一个长度为123,457的字符串上有多快。 在我的FreeBSD计算机上,在stringFill3()函数中实现的这个算法创建了0.001058秒的字符串,而原始的stringFill1()函数创建了0.0808秒的字符串。 新的功能快了76倍。

随着琴弦长度的增加,琴弦的性能差异也随之增大。 在创建更大和更大的字符串的限制中,原始函数的行为大致类似于C1 (常数)次N^2 ,并且新函数的行为像C2 (常量)乘以N

从我们的实验中,我们可以确定C1的值为C1 = 0.0808 / (123457)2 = .00000000000530126997 ,而C2的值为C2 = 0.001058 / 123457 = .00000000856978543136 。 在10秒内,新功能可以创建一个包含1,166,890,359个字符的字符串。 为了创建这个相同的字符串,旧的函数需要7,218,384秒的时间。

这几乎是三个月,相比之下十秒!

我只是回答(迟了几年),因为我对这个问题的原始解决方案已经在互联网上浮动了10多年了,显然还是很少有人记得这一点。 我想通过写一篇关于它的文章,我会帮助:

高速JavaScript的性能优化/页3

不幸的是,这里提出的其他一些解决方案仍然是一些需要三个月才能产生相同数量的输出,以便在10秒钟内产生适当的解决方案。

我想花时间在这里重现这篇文章的一部分,作为Stack Overflow的规范答案。

请注意,这里最好的算法显然是基于我的算法,可能是从其他人的第三代或第四代适应中继承而来的。 不幸的是,这些修改导致了性能下降。 我在这里提出的解决方案的变体可能不明白我for (;;)表达式的困惑for (;;)这个表达式看起来像用C语言编写的服务器的主要无限循环,而这个表达式的设计只是为了让循环控制有一个仔细定位的break语句,避免指数级地复制字符串一个额外的不必要的时间的最紧凑的方法。

好消息! Harmony(ECMAScript 6)接受 String.prototype.repeat

 > "yo".repeat(2) "yoyo" 

该方法在最近版本的V8中可用,由Node.js,Chrome(从版本41开始支持String.repeat )和Opera使用。 较新版本的Safari和Firefox似乎也有支持,但Internet Explorer不支持。 有关最新列表,请参阅MDN:String.prototype.repeat>浏览器兼容性 。

MDN提出了以下的polyfill:

 if (!String.prototype.repeat) { String.prototype.repeat = function(count) { 'use strict'; if (this == null) { throw new TypeError('can\'t convert ' + this + ' to object'); } var str = '' + this; count = +count; if (count != count) { count = 0; } if (count < 0) { throw new RangeError('repeat count must be non-negative'); } if (count == Infinity) { throw new RangeError('repeat count must be less than infinity'); } count = Math.floor(count); if (str.length == 0 || count == 0) { return ''; } // Ensuring count is a 31-bit integer allows us to heavily optimize the // main part. But anyway, most current (August 2014) browsers can't handle // strings 1 << 28 chars or longer, so: if (str.length * count >= 1 << 28) { throw new RangeError('repeat count must not overflow maximum string size'); } var rpt = ''; for (;;) { if ((count & 1) == 1) { rpt += str; } count >>>= 1; if (count == 0) { break; } str += str; } return rpt; } } 

扩大P.Biley的解决方案 :

 String.prototype.repeat = function(num) { return new Array(isNaN(num)? 1 : ++num).join(this); } 

这样你就可以避免意外的参数类型:

 var foo = 'bar'; alert(foo.repeat(3)); // Will work, "barbarbar" alert(foo.repeat('3')); // Same as above alert(foo.repeat(true)); // Same as foo.repeat(1) alert(foo.repeat(0)); // This and all the following return an empty alert(foo.repeat(false)); // string while not causing an exception alert(foo.repeat(null)); alert(foo.repeat(undefined)); alert(foo.repeat({})); // Object alert(foo.repeat(function () {})); // Function 

编辑:他的优雅++num主意给杰罗恩的信贷!

String.prototype.repeat现在是ES6标准版。

 'abc'.repeat(3); //abcabcabc 
 /** @desc: repeat string @param: n - times @param: d - delimiter */ String.prototype.repeat = function (n, d) { return --n ? this + (d || '') + this.repeat(n, d) : '' + this }; 

这是如何使用分隔符多次重复字符串。

使用Array(N+1).join("string_to_repeat")

这个问题的答案有5%-7%的提高。

通过停止count > 1来展开循环,并在循环之后执行额外的result += pattnern concat。 这将避免循环最终以前未使用的pattern += pattern而不必使用昂贵的if-check。 最终结果如下所示:

 String.prototype.repeat = function(count) { if (count < 1) return ''; var result = '', pattern = this.valueOf(); while (count > 1) { if (count & 1) result += pattern; count >>= 1, pattern += pattern; } result += pattern; return result; }; 

这里是disfated的小提琴分解版本: http : //jsfiddle.net/wsdfg/

 function repeat(s, n) { var r=""; for (var a=0;a<n;a++) r+=s; return r;} 

Tests of the various methods:

 var repeatMethods = { control: function (n,s) { /* all of these lines are common to all methods */ if (n==0) return ''; if (n==1 || isNaN(n)) return s; return ''; }, divideAndConquer: function (n, s) { if (n==0) return ''; if (n==1 || isNaN(n)) return s; with(Math) { return arguments.callee(floor(n/2), s)+arguments.callee(ceil(n/2), s); } }, linearRecurse: function (n,s) { if (n==0) return ''; if (n==1 || isNaN(n)) return s; return s+arguments.callee(--n, s); }, newArray: function (n, s) { if (n==0) return ''; if (n==1 || isNaN(n)) return s; return (new Array(isNaN(n) ? 1 : ++n)).join(s); }, fillAndJoin: function (n, s) { if (n==0) return ''; if (n==1 || isNaN(n)) return s; var ret = []; for (var i=0; i<n; i++) ret.push(s); return ret.join(''); }, concat: function (n,s) { if (n==0) return ''; if (n==1 || isNaN(n)) return s; var ret = ''; for (var i=0; i<n; i++) ret+=s; return ret; }, artistoex: function (n,s) { var result = ''; while (n>0) { if (n&1) result+=s; n>>=1, s+=s; }; return result; } }; function testNum(len, dev) { with(Math) { return round(len+1+dev*(random()-0.5)); } } function testString(len, dev) { return (new Array(testNum(len, dev))).join(' '); } var testTime = 1000, tests = { biggie: { str: { len: 25, dev: 12 }, rep: {len: 200, dev: 50 } }, smalls: { str: { len: 5, dev: 5}, rep: { len: 5, dev: 5 } } }; var testCount = 0; var winnar = null; var inflight = 0; for (var methodName in repeatMethods) { var method = repeatMethods[methodName]; for (var testName in tests) { testCount++; var test = tests[testName]; var testId = methodName+':'+testName; var result = { id: testId, testParams: test } result.count=0; (function (result) { inflight++; setTimeout(function () { result.start = +new Date(); while ((new Date() - result.start) < testTime) { method(testNum(test.rep.len, test.rep.dev), testString(test.str.len, test.str.dev)); result.count++; } result.end = +new Date(); result.rate = 1000*result.count/(result.end-result.start) console.log(result); if (winnar === null || winnar.rate < result.rate) winnar = result; inflight--; if (inflight==0) { console.log('The winner: '); console.log(winnar); } }, (100+testTime)*testCount); }(result)); } } 

Here's the JSLint safe version

 String.prototype.repeat = function (num) { var a = []; a.length = num << 0 + 1; return a.join(this); }; 

For all browsers

This is about as concise as it gets :

 function repeat(s, n) { return new Array(n+1).join(s); } 

If you also care about performance, this is a much better approach :

 function repeat(s, n) { var a=[],i=0;for(;i<n;)a[i++]=s;return a.join(''); } 

If you want to compare the performance of both options, see this Fiddle and this Fiddle for benchmark tests. During my own tests, the second option was about 2 times faster in Firefox and about 4 times faster in Chrome!

For moderns browsers only :

In modern browsers, you can now also do this :

 function repeat(s,n) { return s.repeat(n) }; 

This option is not only shorter than both other options, but it's even faster than the second option.

Unfortunately, it doesn't work in any version of Internet explorer. The numbers in the table specify the first browser version that fully supports the method :

在这里输入图像描述

 function repeat(pattern, count) { for (var result = '';;) { if (count & 1) { result += pattern; } if (count >>= 1) { pattern += pattern; } else { return result; } } } 

You can test it at JSFiddle . Benchmarked against the hacky Array.join and mine is, roughly speaking, 10 (Chrome) to 100 (Safari) to 200 (Firefox) times faster (depending on the browser).

This may be the smallest recursive one:-

 String.prototype.repeat = function(n,s) { s = s || "" if(n>0) { s += this s = this.repeat(--n,s) } return s} 

Fiddle: http://jsfiddle.net/3Y9v2/

 function repeat(s, n){ return ((new Array(n+1)).join(s)); } alert(repeat('R', 10)); 

Simple recursive concatenation

I just wanted to give it a bash, and made this:

 function ditto( s, r, c ) { return c-- ? ditto( s, r += s, c ) : r; } ditto( "foo", "", 128 ); 

I can't say I gave it much thought, and it probably shows 🙂

This is arguably better

 String.prototype.ditto = function( c ) { return --c ? this + this.ditto( c ) : this; }; "foo".ditto( 128 ); 

And it's a lot like an answer already posted – I know this.

But why be recursive at all?

And how about a little default behaviour too?

 String.prototype.ditto = function() { var c = Number( arguments[ 0 ] ) || 2, r = this.valueOf(); while ( --c ) { r += this; } return r; } "foo".ditto(); 

Because , although the non recursive method will handle arbitrarily large repeats without hitting call stack limits, it's a lot slower.

Why did I bother adding more methods that aren't half as clever as those already posted?

Partly for my own amusement, and partly to point out in the simplest way I know that there are many ways to skin a cat, and depending on the situation, it's quite possible that the apparently best method isn't ideal.

A relatively fast and sophisticated method may effectively crash and burn under certain circumstances, whilst a slower, simpler method may get the job done – eventually.

Some methods may be little more than exploits, and as such prone to being fixed out of existence, and other methods may work beautifully in all conditions, but are so constructed that one simply has no idea how it works.

"So what if I dunno how it works?!"

真的吗?

JavaScript suffers from one of its greatest strengths; it's highly tolerant of bad behaviour, and so flexible it'll bend over backwards to return results, when it might have been better for everyone if it'd snapped!

"With great power, comes great responsibility" 😉

But more seriously and importantly, although general questions like this do lead to awesomeness in the form of clever answers that if nothing else, expand one's knowledge and horizons, in the end, the task at hand – the practical script that uses the resulting method – may require a little less, or a little more clever than is suggested.

These "perfect" algorithms are fun and all, but "one size fits all" will rarely if ever be better than tailor made.

This sermon was brought to you courtesy of a lack of sleep and a passing interest. Go forth and code!

Firstly, the OP's questions seems to be about conciseness – which I understand to mean "simple and easy to read", while most answers seem to be about efficiency – which is obviously not the same thing and also I think that unless you implement some very specific large data manipulating algorithms, shouldn't worry you when you come to implement basic data manipulation Javascript functions. Conciseness is much more important.

Secondly, as André Laszlo noted, String.repeat is part of ECMAScript 6 and already available in several popular implementations – so the most concise implementation of String.repeat is not to implement it 😉

Lastly, if you need to support hosts that don't offer the ECMAScript 6 implementation, MDN's polyfill mentioned by André Laszlo is anything but concise.

So, without further ado – here is my concise polyfill:

 String.prototype.repeat = String.prototype.repeat || function(n){ return n<=1 ? this : this.concat(this.repeat(n-1)); } 

Yes, this is a recursion. I like recursions – they are simple and if done correctly are easy to understand. Regarding efficiency, if the language supports it they can be very efficient if written correctly.

From my tests, this method is ~60% faster than the Array.join approach. Although it obviously comes nowhere close disfated's implementation, it is much simpler than both.

My test setup is node v0.10, using "Strict mode" (I think it enables some sort of TCO ), calling repeat(1000) on a 10 character string a million times.

Use Lodash for Javascript utility functionality, like repeating strings.

Lodash provides nice performance and ECMAScript compatibility.

I highly recommend it for UI development and it works well server side, too.

Here's how to repeat the string "yo" 2 times using Lodash:

 > _.repeat('yo', 2) "yoyo" 

Just another repeat function:

 function repeat(s, n) { var str = ''; for (var i = 0; i < n; i++) { str += s; } return str; } 

ES2015 has been realized this repeat() method!

http://www.ecma-international.org/ecma-262/6.0/#sec-string.prototype.repeat
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat
http://www.w3schools.com/jsref/jsref_repeat.asp

 /** * str: String * count: Number */ const str = `hello repeat!\n`, count = 3; let resultString = str.repeat(count); console.log(`resultString = \n${resultString}`); /* resultString = hello repeat! hello repeat! hello repeat! */ ({ toString: () => 'abc', repeat: String.prototype.repeat }).repeat(2); // 'abcabc' (repeat() is a generic method) // Examples 'abc'.repeat(0); // '' 'abc'.repeat(1); // 'abc' 'abc'.repeat(2); // 'abcabc' 'abc'.repeat(3.5); // 'abcabcabc' (count will be converted to integer) // 'abc'.repeat(1/0); // RangeError // 'abc'.repeat(-1); // RangeError 

Recursive solution using divide and conquer:

 function repeat(n, s) { if (n==0) return ''; if (n==1 || isNaN(n)) return s; with(Math) { return repeat(floor(n/2), s)+repeat(ceil(n/2), s); } } 

I came here randomly and never had a reason to repeat a char in javascript before.

I was impressed by artistoex's way of doing it and disfated's results. I noticed that the last string concat was unnecessary, as Dennis also pointed out.

I noticed a few more things when playing with the sampling disfated put together.

The results varied a fair amount often favoring the last run and similar algorithms would often jockey for position. One of the things I changed was instead of using the JSLitmus generated count as the seed for the calls; as count was generated different for the various methods, I put in an index. This made the thing much more reliable. I then looked at ensuring that varying sized strings were passed to the functions. This prevented some of the variations I saw, where some algorithms did better at the single chars or smaller strings. However the top 3 methods all did well regardless of the string size.

Forked test set

http://jsfiddle.net/schmide/fCqp3/134/

 // repeated string var string = '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789'; // count paremeter is changed on every test iteration, limit it's maximum value here var maxCount = 200; var n = 0; $.each(tests, function (name) { var fn = tests[name]; JSLitmus.test(++n + '. ' + name, function (count) { var index = 0; while (count--) { fn.call(string.slice(0, index % string.length), index % maxCount); index++; } }); if (fn.call('>', 10).length !== 10) $('body').prepend('<h1>Error in "' + name + '"</h1>'); }); JSLitmus.runAll(); 

I then included Dennis' fix and decided to see if I could find a way to eek out a bit more.

Since javascript can't really optimize things, the best way to improve performance is to manually avoid things. If I took the first 4 trivial results out of the loop, I could avoid 2-4 string stores and write the final store directly to the result.

 // final: growing pattern + prototypejs check (count < 1) 'final avoid': function (count) { if (!count) return ''; if (count == 1) return this.valueOf(); var pattern = this.valueOf(); if (count == 2) return pattern + pattern; if (count == 3) return pattern + pattern + pattern; var result; if (count & 1) result = pattern; else result = ''; count >>= 1; do { pattern += pattern; if (count & 1) result += pattern; count >>= 1; } while (count > 1); return result + pattern + pattern; } 

This resulted in a 1-2% improvement on average over Dennis' fix. However, different runs and different browsers would show a fair enough variance that this extra code probably isn't worth the effort over the 2 previous algorithms.

A chart

Edit: I did this mostly under chrome. Firefox and IE will often favor Dennis by a couple %.

Simple method:

 String.prototype.repeat = function(num) { num = parseInt(num); if (num < 0) return ''; return new Array(num + 1).join(this); } 

People overcomplicate this to a ridiculous extent or waste performance. Arrays? Recursion? You've got to be kidding me.

 function repeat (string, times) { var result = '' while (times-- > 0) result += string return result } 

Edit. I ran some simple tests to compare with the bitwise version posted by artistoex / disfated and a bunch of other people. The latter was only marginally faster, but orders of magnitude more memory-efficient. For 1000000 repeats of the word 'blah', the Node process went up to 46 megabytes with the simple concatenation algorithm (above), but only 5.5 megabytes with the logarithmic algorithm. The latter is definitely the way to go. Reposting it for the sake of clarity:

 function repeat (string, times) { var result = '' while (times > 0) { if (times & 1) result += string times >>= 1 string += string } return result } 

If you think all those prototype definitions, array creations, and join operations are overkill, just use a single line code where you need it. String S repeating N times:

 for (var i = 0, result = ''; i < N; i++) result += S; 

This is what you're looking for I hope. See the original implementation I found in the source code of node.js.

 function repeatString(str, len) { return Array.apply(null, { length: len + 1 }).join(str).slice(0, len) } 

Concatenating strings based on an number.

 function concatStr(str, num) { var arr = []; //Construct an array for (var i = 0; i < num; i++) arr[i] = str; //Join all elements str = arr.join(''); return str; } console.log(concatStr("abc", 3)); 

希望有所帮助!