返回任意次数的字符串的最佳或最简洁的方法是什么?

以下是我目前为止拍得最好的照片:

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

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

新读者注意:这个答案很老,而且不太实用——它只是“聪明”,因为它使用Array的东西来获取 串事情做完了。当我写“更少的过程”时,我肯定是指 “更少的代码”,因为,正如其他人在随后的回答中指出的那样,它 表演像猪一样。所以如果你看重速度就不要用它。

我将把这个函数直接放到String对象上。与其创建一个数组,填充它,然后用一个空字符连接它,不如创建一个适当长度的数组,然后用你想要的字符串连接它。同样的结果,更少的过程!

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

alert( "string to repeat\n".repeat( 4 ) );

/**  
@desc: repeat string  
@param: n - times  
@param: d - delimiter  
*/

String.prototype.repeat = function (n, d) {
    return --n ? this + (d || '') + this.repeat(n, d) : '' + this
};

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


扩展P.Bailey的解决方案:

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

编辑:感谢jerone为他优雅的++num想法!


这个很有效

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

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

这是我找到的最快的变种。

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;
}

它基于wnrph算法。 它真的很快。与传统的Array(count + 1).join(string)方法相比,计数越大,它的运行速度就越快。

我只改变了两件事:

replace pattern = this with pattern = this. valueof()(清除一个明显的类型转换); 增加if (count < 1)检查从prototypejs到函数的顶部,以排除在这种情况下不必要的操作。 应用优化从丹尼斯的答案(5-7%的速度提高)

UPD

为感兴趣的人准备了一个性能测试场地。

变量计数~ 0 ..100:

常量= 1024:

如果可以的话,使用它,让它更快:)


这可能是最小的递归:-

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

使用分治法的递归解:

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); }
}

各种方法的测试:

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));
    }
}

这是JSLint的安全版本

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

disfated的答案提高了5-7%。

通过在计数> 1处停止来展开循环,并在循环之后执行额外的result += pattnern concat。这将避免循环最终使用以前未使用的模式+=模式,而不必使用昂贵的if检查。 最终的结果是这样的:

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;
};

下面是disfate的小提琴分叉的展开版本:http://jsfiddle.net/wsdfg/


如果您认为所有这些原型定义、数组创建和连接操作都是多余的,那么只需在需要的地方使用一行代码。字符串S重复N次:

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

我随机来到这里,从来没有理由在javascript中重复字符。

我对artistoex的做法印象深刻,对结果感到失望。我注意到最后一个串连接是不必要的,正如丹尼斯也指出的那样。

我注意到一些更多的东西,当玩抽样disfate放在一起。

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.

分叉测试集

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();

然后我加入了丹尼斯的解决方案,并决定看看我是否能找到更多的方法。

由于javascript不能真正优化,提高性能的最好方法是手动避免一些东西。如果我把前4个琐碎的结果从循环中取出,我可以避免2-4个字符串存储,并将最后的存储直接写入结果。

// 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;
}

这比丹尼斯的修复方案平均提高了1-2%。然而,不同的运行和不同的浏览器会显示相当大的差异,这额外的代码可能不值得在前面的两种算法上付出努力。

一个图表

编辑:我这样做主要是在chrome浏览器。Firefox和IE通常对Dennis的偏爱程度为百分之几。


简单的方法:

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

这个问题是JavaScript的一个众所周知的/“经典”优化问题,原因是JavaScript字符串是“不可变的”,即使是将单个字符连接到字符串也需要创建一个完整的新字符串,包括内存分配和复制。

Unfortunately, the accepted answer on this page is wrong, where "wrong" means by a performance factor of 3x for simple one-character strings, and 8x-97x for short strings repeated more times, to 300x for repeating sentences, and infinitely wrong when taking the limit of the ratios of complexity of the algorithms as n goes to infinity. Also, there is another answer on this page which is almost right (based on one of the many generations and variations of the correct solution circulating throughout the Internet in the past 13 years). However, this "almost right" solution misses a key point of the correct algorithm causing a 50% performance degradation.

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

~ October 2000 I published an algorithm for this exact problem which was widely adapted, modified, then eventually poorly understood and forgotten. To remedy this issue, in August, 2008 I published an article http://www.webreference.com/programming/javascript/jkm3/3.html explaining the algorithm and using it as an example of simple of general-purpose JavaScript optimizations. By now, Web Reference has scrubbed my contact information and even my name from this article. And once again, the algorithm has been widely adapted, modified, then poorly understood and largely forgotten.

原始字符串重复/乘法JavaScript算法由 Joseph Myers,大约Y2K作为text .js中的文本相乘函数; 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;
}

Within two months after publication of that article, this same question was posted to Stack Overflow and flew under my radar until now, when apparently the original algorithm for this problem has once again been forgotten. The best solution available on this Stack Overflow page is a modified version of my solution, possibly separated by several generations. Unfortunately, the modifications ruined the solution's optimality. In fact, by changing the structure of the loop from my original, the modified solution performs a completely unneeded extra step of exponential duplicating (thus joining the largest string used in the proper answer with itself an extra time and then discarding it).

下面将讨论与此问题的所有答案相关的一些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的无辜引用会损害它的性能。更糟糕的是,这个object属性的使用假设读者知道JavaScript字符串对象的属性,从而降低了程序的简单性。

使用这个object属性破坏了计算机程序的通用性。程序假设x必须是一个长度为1的字符串。这限制了stringFill1()函数的应用,只能重复单个字符。即使单个字符也不能使用,如果它们包含多个字节,如HTML实体&nbsp;

这种不必要的使用object属性所导致的最糟糕的问题是,如果在空输入字符串x上测试,该函数会创建一个无限循环。为了检查通用性,请将程序应用于尽可能少的输入。当程序被要求超过可用内存量时崩溃是有理由的。像这样的程序在被要求不生成任何东西时崩溃是不可接受的。有时候漂亮的代码是有毒的代码。

简单性可能是计算机编程的一个模糊目标,但通常不是。当一个程序缺乏任何合理程度的通用性时,说“这个程序就目前而言已经足够好了”是不成立的。如你所见,使用字符串。长度属性会阻止此程序在常规设置下工作,事实上,不正确的程序会导致浏览器或系统崩溃。

有没有办法在解决这两个严重问题的同时提高JavaScript的性能呢?

当然可以。只使用整数。

创建字符串的优化代码

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()出现的问题

Using our timing function, we find that the time increases to 62.54 microseconds for a 200-byte string, compared to 27.68 for a 100-byte string. It seems like the time should be doubled for doing twice as much work, but instead it's tripled or quadrupled. From programming experience, this result seems strange, because if anything, the function should be slightly faster since work is being done more efficiently (200 bytes per function call rather than 100 bytes per function call). This issue has to do with an insidious property of JavaScript strings: JavaScript strings are "immutable."

不可变意味着一旦创建了字符串就不能更改。通过一次增加一个字节,我们不会多消耗一个字节的精力。我们实际上是在重新创建整个字符串,再加上一个字节。

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

C(n) = 1 + 2 + 3 +…+ n = n (n +1)/2 = o (n²)符号O(N²)发音为大O(N²),这意味着长期运行的计算成本与字符串长度的平方成正比。创造100个字符需要1万个单位的工作,创造200个字符需要4万个单位的工作。

这就是为什么创建200个字符所花费的时间是创建100个字符的两倍多。事实上,它应该花四倍的时间。我们的编程经验是正确的,因为对于更长的字符串,工作完成得稍微更有效率,因此只需要大约三倍的时间。一旦函数调用的开销对于我们所创建的字符串的长度变得可以忽略不计,那么创建一个两倍长的字符串实际上需要四倍的时间。

(Historical note: This analysis doesn't necessarily apply to strings in source code, such as html = 'abcd\n' + 'efgh\n' + ... + 'xyz.\n', since the JavaScript source code compiler can join the strings together before making them into a JavaScript string object. Just a few years ago, the KJS implementation of JavaScript would freeze or crash when loading long strings of source code joined by plus signs. Since the computational time was O(N^2) it wasn't difficult to make Web pages which overloaded the Konqueror Web browser or Safari, which used the KJS JavaScript engine core. I first came across this issue when I was developing a markup language and JavaScript markup language parser, and then I discovered what was causing the problem when I wrote my script for JavaScript Includes.)

显然,这种性能的快速下降是一个巨大的问题。既然我们不能改变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,表达式n & 1的值为真;如果n的最后一位二进制数字为0,表达式n & 1的值为假。

新的高效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²)算法和O(N)算法之间的区别。

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

深入的分析

是什么让这个特殊的功能在竞争中脱颖而出呢?

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

因此,为了提高脚本的性能,必须预先通过将两个字符串连接在一起来计算更长的字符串长度,然后递归地构建所需的字符串长度。

例如,要创建一个16个字母的字节字符串,首先要预先计算两个字节的字符串。然后,两个字节的字符串将被重用,以预计算一个四字节的字符串。然后,4字节的字符串将被重用以预计算8字节的字符串。最后,两个8字节的字符串将被重用,以创建所需的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²,而新函数的行为像C2(常数)乘以N。

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

这几乎是3个月,而不是10秒钟!

我之所以回答这个问题(晚了几年),是因为我对这个问题的最初解决方案已经在互联网上流传了10多年,显然仍然没有被少数记得它的人理解。我想在这里写一篇关于它的文章会有所帮助:

高速JavaScript的性能优化/ Page 3

不幸的是,这里介绍的其他一些解决方案仍然需要花费3个月才能产生与正确的解决方案在10秒内生成的相同数量的输出。

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

Note that the best-performing algorithm here is clearly based on my algorithm and was probably inherited from someone else's 3rd or 4th generation adaptation. Unfortunately, the modifications resulted in reducing its performance. The variation of my solution presented here perhaps did not understand my confusing for (;;) expression which looks like the main infinite loop of a server written in C, and which was simply designed to allow a carefully-positioned break statement for loop control, the most compact way to avoid exponentially replicating the string one extra unnecessary time.


小提琴:http://jsfiddle.net/3Y9v2/

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

人们将这一问题过于复杂,甚至浪费了表现。数组?递归?你在跟我开玩笑吧。

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

编辑。我做了一些简单的测试,与artistoex / disfated和其他一些人发布的按位版本进行比较。后者只是稍微快一点,但内存效率高了几个数量级。对于单词'blah'的1000000次重复,使用简单的串联算法(上面),Node进程的容量达到46兆字节,而使用对数算法只有5.5兆字节。后者绝对是正确的选择。为了清晰起见,我将其转发:

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

简单的递归连接

我只是想试一试,然后做了这个:

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

ditto( "foo", "", 128 );

我不能说我想得太多,它可能表明:-)

可以说这样更好

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

"foo".ditto( 128 );

这很像已经发布的答案-我知道。

但为什么要递归呢?

还有一点默认行为呢?

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

"foo".ditto();

因为,尽管非递归方法将处理任意大的重复而不达到调用堆栈限制,但它要慢得多。

为什么我要费心添加更多的方法,而这些方法还不及已经发布的那些方法的一半聪明呢?

一部分是为了自娱自乐,一部分是为了用最简单的方式指出,我知道剥猫皮的方法有很多种,根据具体情况,显然最好的方法很可能并不理想。

一个相对快速和复杂的方法可能会在某些情况下有效地崩溃和烧毁,而一个更慢,更简单的方法可能最终会完成工作。

有些方法可能只是利用漏洞,因此很容易被修复而不复存在,而其他方法可能在所有条件下都工作得很好,但构造得太复杂,以至于人们根本不知道它是如何工作的。

“就算我不知道它是怎么工作的又怎样?!”

严重吗?

JavaScript有一个最大的优势;它对不良行为具有高度的容忍度,而且非常灵活,它会竭尽全力地返回结果,而如果它崩溃了,可能对每个人都更好!

“能力越大,责任越大”;-)

但更严肃和重要的是,尽管像这样的一般问题确实会以聪明的答案形式带来令人生畏的东西,如果没有其他的话,扩展了一个人的知识和视野,最终,手头的任务-使用结果方法的实际脚本-可能需要比建议的更少或更聪明一点。

这些“完美”的算法很有趣,但“一刀切”很少会比量身定制更好。

这篇布道是由于你睡眠不足和一时兴趣所致。 开始编码吧!


好消息!String.prototype.repeat现在是JavaScript的一部分。

"yo".repeat(2);
// returns: "yoyo"

除Internet Explorer外,所有主流浏览器都支持该方法。有关最新列表,请参见MDN: String.prototype.repeat >浏览器兼容性。

MDN为没有支持的浏览器提供了一个填充。


首先,OP的问题似乎是关于简明性的——我理解的意思是“简单易读”,而大多数答案似乎是关于效率的——这显然不是一回事,而且我认为,除非你实现了一些非常具体的大型数据操作算法,否则当你实现基本的数据操作Javascript函数时,不应该担心。简洁更重要。

其次,正如André Laszlo所指出的,字符串。repeat是ECMAScript 6的一部分,并且已经在几个流行的实现中可用——因此是String最简洁的实现。重复是不执行的;-)

最后,如果您需要支持不提供ECMAScript 6实现的主机,André Laszlo提到的MDN的polyfill一点也不简洁。

所以,废话不多说——下面是我简洁的填充:

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

是的,这是递归。我喜欢递归——它们很简单,如果操作正确,很容易理解。关于效率,如果语言支持它,如果写得正确,它们可以非常高效。

从我的测试来看,这种方法比Array快60%。加入的方法。虽然它显然与disfate的实现相去甚远,但它比两者都要简单得多。

我的测试设置是节点v0.10,使用“严格模式”(我认为它支持某种TCO),对一个10个字符的字符串调用repeat(1000)一百万次。


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

'abc'.repeat(3); //abcabcabc

使用数组(N + 1) . join(“string_to_repeat”)


使用Lodash来实现Javascript的实用功能,比如重复字符串。

Lodash提供了良好的性能和ECMAScript兼容性。

我强烈推荐它用于UI开发,它在服务器端也很好用。

下面是如何使用Lodash重复字符串“yo”2次:

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

适用于所有浏览器

这是最简洁的:

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

如果你也关心性能,这是一个更好的方法:

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

如果要比较这两个选项的性能,请参阅此Fiddle和此Fiddle以获得基准测试。在我自己的测试中,第二个选项在Firefox中大约快2倍,在Chrome中大约快4倍!

仅适用于现代浏览器:

在现代浏览器中,你现在也可以这样做:

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

这个选项不仅比其他两个选项都要短,而且比第二个选项还要快。

不幸的是,它不能在任何版本的ie浏览器中运行。表中的数字指定了完全支持该方法的第一个浏览器版本:


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

你可以在JSFiddle上测试它。以hacky Array为基准。粗略地说,我的速度是10倍(Chrome)到100倍(Safari)到200倍(Firefox)(取决于浏览器)。


基于数字连接字符串。

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));

希望有帮助!


ES-Next有很多方法

1. ES2015/ES6已经实现了这个repeat()方法!

/** * 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

2. ES2017/ES8新增String.prototype.padStart()

Const STR = 'abc '; Const times = 3; const newStr = str. padstartlength * times, str.toUpperCase()); console.log(' newStr = ', newStr); // "newStr =" "ABC ABC ABC "

3.ES2017/ES8新增String.prototype.padEnd()

Const STR = 'abc '; Const times = 3; const newStr = str. padend (str。length * times, str.toUpperCase()); console.log(' newStr = ', newStr); // "newStr =" "abc abc abc "

refs

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

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padEnd


只是另一个重复函数:

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

在ES8中,你也可以使用padStart或padEnd。如。

var str = 'cat';
var num = 23;
var size = str.length * num;
"".padStart(size, str) // outputs: 'catcatcatcatcatcatcatcatcatcatcatcatcatcatcatcatcatcatcatcatcatcatcat'

要在指定次数内重复一个字符串,可以使用JavaScript中内置的repeat()方法。

下面是一个重复以下字符串4次的例子:

const name = "king";

const repeat = name.repeat(4);

console.log(repeat);

输出:

"kingkingkingking"

或者我们可以创建自己版本的repeat()函数,如下所示:

function repeat(str, n) {
  if (!str || !n) {
    return;
  }

 let final = "";
  while (n) {
    final += s;
    n--;
  }
  return final;
}

console.log(repeat("king", 3))

(最初发布于https://reactgo.com/javascript-repeat-string/)