如何在JavaScript中反转string?

当传递给带有return语句的函数时,如何在JavaScript中反转string(或in-place)? 所有没有使用内置的function? .reverse() .charAt()

只要你处理简单的ASCII字符,而且你很乐意使用内置的函数,那么这将起作用:

 function reverse(s){ return s.split("").reverse().join(""); } 

如果您需要支持UTF-16或其他多字节字符的解决scheme,请注意,此function会给出无效的Unicodestring或看起来有趣的有效string。 你可能想要考虑这个答案 。

以下技术(或类似)通常用于在JavaScript中反转string:

 // Don't use this! var naiveReverse = function(string) { return string.split('').reverse().join(''); } 

事实上,迄今为止发布的所有答案都是这种模式的变体。 但是,这个解决scheme有一些问题。 例如:

 naiveReverse('foo 𝌆 bar'); // → 'rab    oof' // Where did the `𝌆` symbol go? Whoops! 

如果您想知道为什么会发生这种情况,请阅读JavaScript的内部字符编码 。 (TL; DR: 𝌆是一个星号,JavaScript将它作为两个单独的代码单元公开)

但还有更多:

 // To see which symbols are being used here, check: // http://mothereff.in/js-escapes#1ma%C3%B1ana%20man%CC%83ana naiveReverse('mañana mañana'); // → 'anãnam anañam' // Wait, so now the tilde is applied to the `a` instead of the `n`? WAT. 

testingstring反向实现的好string如下 :

 'foo 𝌆 bar mañana mañana' 

为什么? 因为它包含一个星号( 𝌆 )( 在JavaScript中用代理对代表 )和一个组合标记(最后一个mañana实际上由两个符号组成:U + 006E拉丁小字母N和U + 0303合并字节) 。

代理对出现的顺序不能颠倒,否则星形符号将不会再出现在“反转”string中。 这就是为什么你在前面例子的输出中看到了这些 标记的原因。

组合标记总是适用于前一个符号,因此必须将主符号(U + 006E LATIN小字母N)作为整体标记(U + 0303合并TILDE)。 颠倒它们的顺序将使组合标记与string中的另一个符号配对。 这就是为什么示例输出有而不是ñ

希望这解释了为什么到目前为止发布的所有答案都是错误的


为了回答你最初的问题 – 如何正确地反转JavaScript中的一个string – 我写了一个小的JavaScript库,它能够识别Unicode的string反转。 它没有我刚刚提到的任何问题。 图书馆被称为Esrever ; 它的代码在GitHub上,它可以在任何JavaScript环境中运行。 它带有一个shell实用程序/二进制文件,所以你可以很容易地从你的terminal反向string,如果你想。

 var input = 'foo 𝌆 bar mañana mañana'; esrever.reverse(input); // → 'anañam anañam rab 𝌆 oof' 

至于“就地”部分,请参阅其他答案。

 String.prototype.reverse=function(){return this.split("").reverse().join("");} 

要么

 String.prototype.reverse = function() { var s = ""; var i = this.length; while (i>0) { s += this.substring(i-1,i); i--; } return s; } 

整个“反转string”是一个陈旧的面试问题C程序员,而被他们采访过的人(为了报复,也许呢?)会问。 不幸的是,这是“就地”的一部分,因为几乎任何托pipe语言(JS,C#等)中的string都使用不可变的string,因此无法分配任何新内存来移动string的整个想法。

尽pipe上面的解决scheme确实会颠倒一个string,但是如果不分配更多的内存,就不会这样做,从而不能满足条件。 您需要直接访问分配的string,并能够操纵其原始内存位置,以便能够将其反转。

就我个人而言,我真的很讨厌这类面试的问题,但不幸的是,我相信我们会继续看到他们未来几年。

详细分析和十种不同的方法来反转string及其性能细节。

http://eddmann.com/posts/ten-ways-to-reverse-a-string-in-javascript/

这些实现的性能:

每个浏览器的性能最佳

  • Chrome 15 – 实现1和6
  • Firefox 7 – 实现6
  • IE 9 – 实施4
  • 歌剧12 – 实施9

以下是这些实现:

实施1:

 function reverse(s) { var o = ''; for (var i = s.length - 1; i >= 0; i--) o += s[i]; return o; } 

实施2:

 function reverse(s) { var o = []; for (var i = s.length - 1, j = 0; i >= 0; i--, j++) o[j] = s[i]; return o.join(''); } 

实施3:

 function reverse(s) { var o = []; for (var i = 0, len = s.length; i <= len; i++) o.push(s.charAt(len - i)); return o.join(''); } 

实施4:

 function reverse(s) { return s.split('').reverse().join(''); } 

实施5:

 function reverse(s) { var i = s.length, o = ''; while (i > 0) { o += s.substring(i - 1, i); i--; } return o; } 

实施6:

 function reverse(s) { for (var i = s.length - 1, o = ''; i >= 0; o += s[i--]) { } return o; } 

实施7:

 function reverse(s) { return (s === '') ? '' : reverse(s.substr(1)) + s.charAt(0); } 

实施8:

 function reverse(s) { function rev(s, len, o) { return (len === 0) ? o : rev(s, --len, (o += s[len])); }; return rev(s, s.length, ''); } 

实施9:

 function reverse(s) { s = s.split(''); var len = s.length, halfIndex = Math.floor(len / 2) - 1, tmp; for (var i = 0; i <= halfIndex; i++) { tmp = s[len - i - 1]; s[len - i - 1] = s[i]; s[i] = tmp; } return s.join(''); } 

实施10

 function reverse(s) { if (s.length < 2) return s; var halfIndex = Math.ceil(s.length / 2); return reverse(s.substr(halfIndex)) + reverse(s.substr(0, halfIndex)); } 

好像我迟到了3年

不幸的是,你不能这样指出。 请参阅JavaScriptstring不可变吗? 我需要JavaScript中的“string生成器”吗?

你可以做的下一个最好的事情是创build一个“视图”或“包装器”,它接受一个string并重新实现你正在使用的stringAPI的任何部分,但假装string是颠倒过来的。 例如:

 var identity = function(x){return x}; function LazyString(s) { this.original = s; this.length = s.length; this.start = 0; this.stop = this.length; this.dir = 1; // "virtual" slicing // (dir=-1 if reversed) this._caseTransform = identity; } // syntactic sugar to create new object: function S(s) { return new LazyString(s); } //We now implement a `"...".reversed` which toggles a flag which will change our math: (function(){ // begin anonymous scope var x = LazyString.prototype; // Addition to the String API x.reversed = function() { var s = new LazyString(this.original); s.start = this.stop - this.dir; s.stop = this.start - this.dir; s.dir = -1*this.dir; s.length = this.length; s._caseTransform = this._caseTransform; return s; } //We also override string coercion for some extra versatility (not really necessary): // OVERRIDE STRING COERCION // - for string concatenation eg "abc"+reversed("abc") x.toString = function() { if (typeof this._realized == 'undefined') { // cached, to avoid recalculation this._realized = this.dir==1 ? this.original.slice(this.start,this.stop) : this.original.slice(this.stop+1,this.start+1).split("").reverse().join(""); this._realized = this._caseTransform.call(this._realized, this._realized); } return this._realized; } //Now we reimplement the String API by doing some math: // String API: // Do some math to figure out which character we really want x.charAt = function(i) { return this.slice(i, i+1).toString(); } x.charCodeAt = function(i) {  return this.slice(i, i+1).toString().charCodeAt(0); } // Slicing functions: x.slice = function(start,stop) { // lazy chaining version of https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/slice if (stop===undefined) stop = this.length; var relativeStart = start<0 ? this.length+start : start; var relativeStop = stop<0 ? this.length+stop : stop; if (relativeStart >= this.length) relativeStart = this.length; if (relativeStart < 0) relativeStart = 0; if (relativeStop > this.length) relativeStop = this.length; if (relativeStop < 0) relativeStop = 0; if (relativeStop < relativeStart) relativeStop = relativeStart; var s = new LazyString(this.original); s.length = relativeStop - relativeStart; s.start = this.start + this.dir*relativeStart; s.stop = s.start + this.dir*s.length; s.dir = this.dir; //console.log([this.start,this.stop,this.dir,this.length], [s.start,s.stop,s.dir,s.length]) s._caseTransform = this._caseTransform; return s; } x.substring = function() { // ... } x.substr = function() { // ... } //Miscellaneous functions: // Iterative search x.indexOf = function(value) { for(var i=0; i<this.length; i++) if (value==this.charAt(i)) return i; return -1; } x.lastIndexOf = function() { for(var i=this.length-1; i>=0; i--) if (value==this.charAt(i)) return i; return -1; } // The following functions are too complicated to reimplement easily. // Instead just realize the slice and do it the usual non-in-place way. x.match = function() { var s = this.toString(); return s.apply(s, arguments); } x.replace = function() { var s = this.toString(); return s.apply(s, arguments); } x.search = function() { var s = this.toString(); return s.apply(s, arguments); } x.split = function() { var s = this.toString(); return s.apply(s, arguments); } // Case transforms: x.toLowerCase = function() { var s = new LazyString(this.original); s._caseTransform = ''.toLowerCase; s.start=this.start; s.stop=this.stop; s.dir=this.dir; s.length=this.length; return s; } x.toUpperCase = function() { var s = new LazyString(this.original); s._caseTransform = ''.toUpperCase; s.start=this.start; s.stop=this.stop; s.dir=this.dir; s.length=this.length; return s; } })() // end anonymous scope 

演示:

 > r = S('abcABC') LazyString original: "abcABC" __proto__: LazyString > r.charAt(1); // doesn't reverse string!!! (good if very long) "B" > r.toLowerCase() // must reverse string, so does so "cbacba" > r.toUpperCase() // string already reversed: no extra work "CBACBA" > r + '-demo-' + r // natural coercion, string already reversed: no extra work "CBAcba-demo-CBAcba" 

踢球者 – 以下是纯粹的math就地完成的,每个angular色只能访问一次,

 > 'demo: ' + S('0123456789abcdef').slice(3).reversed().slice(1,-1).toUpperCase() "demo: EDCBA987654" > S('0123456789ABCDEF').slice(3).reversed().slice(1,-1).toLowerCase().charAt(3) "b" 

如果应用于一个非常大的string,如果您只是采取相对较小的切片,这将产生显着的节省。

无论这是否值得(像在大多数编程语言中那样作为副本进行反转)高度依赖于您的用例以及如何有效地重新实现stringAPI。 例如,如果你想要的只是做string索引操作,或者采取小的slicesubstr s,这将节省您的空间和时间。 如果你打算打印大的反转切片或子串,节省的可能确实很小,甚至比完成一个完整的副本还要差。 您的“反向”string也不会有typesstring ,尽pipe您可能能够通过原型来伪造。

上面的演示实现创build了一个ReversedStringtypes的新对象。 它是原型的,因此相当高效,几乎最less的工作和最小的空间开销(原型定义是共享的)。 这是一个涉及延迟切片的懒惰实现。 每当你执行一个函数如.slice.reversed ,它将执行索引math。 最后,当你提取数据时(通过隐式地调用.toString().charCodeAt(...)或者其他东西),它将以“聪明”的方式应用这些数据,触及尽可能less的数据。

注意:上面的stringAPI是一个例子,可能无法完美实现。 你也可以使用你需要的1-2个function。

在ECMAScript 6中,不用使用.split('')分割方法,可以更快地反转一个string, 扩展运算符就像这样:

 var str = [...'racecar'].reverse().join(''); 

首先,使用Array.from()将string转换为数组,然后使用Array.prototype.reverse()来反转数组,然后使用Array.prototype.join()使其返回string。

 const reverse = str => Array.from(str).reverse().join('') 

在采访中,我被要求翻转一个string,而不使用任何variables或本地方法。 这是我最喜欢的实现:

 function reverseString(str) { return str === '' ? '' : reverseString(str.slice(1)) + str[0]; } 

这是我想的最简单的方法

 var reverse = function(str) { var arr = []; for (var i = 0, len = str.length; i <= len; i++) { arr.push(str.charAt(len - i)) } return arr.join(''); } console.log(reverse('I want a 🍺')); 

我知道这是一个很好的答案,但为了我自己的喜悦,我写了下面的相反的function,并且认为我会分享它,以防其他人使用。 它处理代理对和组合标记:

 function StringReverse (str) { var charArray = []; for (var i = 0; i < str.length; i++) { if (i+1 < str.length) { var value = str.charCodeAt(i); var nextValue = str.charCodeAt(i+1); if ( ( value >= 0xD800 && value <= 0xDBFF && (nextValue & 0xFC00) == 0xDC00) // Surrogate pair) || (nextValue >= 0x0300 && nextValue <= 0x036F)) // Combining marks { charArray.unshift(str.substring(i, i+2)); i++; // Skip the other half continue; } } // Otherwise we just have a rogue surrogate marker or a plain old character. charArray.unshift(str[i]); } return charArray.join(''); } 

所有的道具Mathias,Punycode,和其他各种参考教育在JavaScript中字符编码的复杂性。

 var str = 'sample string'; [].map.call(str, function(x) { return x; }).reverse().join(''); 

要么

 var str = 'sample string'; console.log(str.split('').reverse().join('')); 

//输出:'gnirts elpmas'

真正的答案是:您不能将其逆转,但您可以创build一个相反的新string。

就像练习recursion一样:有时当你去面试的时候,面试官可能会问你如何使用recursion来做到这一点,我认为“首选答案”可能是“我宁愿不要在recursion中这样做,因为它很容易导致堆栈溢出“(因为它是O(n)而不是O(log n) ,如果是O(log n) ,堆栈溢出是相当困难的 – 40亿个项目可以被处理一个32的堆栈级别,如2 ** 32是4294967296.但是,如果是O(n) ,那么它很容易得到堆栈溢出。

有时面试官仍然会问你,“就像练习一样,你为什么不用recursion来写呢?” 这里是:

 String.prototype.reverse = function() { if (this.length <= 1) return this; else return this.slice(1).reverse() + this.slice(0,1); } 

testing运行:

 var s = ""; for(var i = 0; i < 1000; i++) { s += ("apple" + i); } console.log(s.reverse()); 

输出:

 999elppa899elppa...2elppa1elppa0elppa 

为了尝试获得堆栈溢出,我在Google Chrome中将1000更改为10000 ,并报告:

 RangeError: Maximum call stack size exceeded 

string本身是不可变的,但是你可以用下面的代码轻松地创build一个反转的拷贝:

 function reverseString(str) { var strArray = str.split(""); strArray.reverse(); var strReverse = strArray.join(""); return strReverse; } reverseString("hello"); 
 function reverseString(string) { var reversedString = ""; var stringLength = string.length - 1; for (var i = stringLength; i >= 0; i--) { reversedString += string[i]; } return reversedString; } 

无需将string转换为数组;

 String.prototype.reverse = function() { var ret = ""; var size = 0; for (var i = this.length - 1; -1 < i; i -= size) { if ( '\uD800' <= this[i - 1] && this[i - 1] <= '\uDBFF' && '\uDC00' <= this[i] && this[i] <= '\uDFFF' ) { size = 2; ret += this[i - 1] + this[i]; } else { size = 1; ret += this[i]; } } return ret; } console.log('anãnam anañam' === 'mañana mañana'.reverse()); 

使用Array.reverse而不将字符转换为代码点;

 String.prototype.reverse = function() { var array = this.split("").reverse(); for (var i = 0; i < this.length; ++i) { if ( '\uD800' <= this[i - 1] && this[i - 1] <= '\uDBFF' && '\uDC00' <= this[i] && this[i] <= '\uDFFF' ) { array[i - 1] = array[i - 1] + array[i]; array[i] = array[i - 1].substr(0, 1); array[i - 1] = array[i - 1].substr(1, 1); } } return array.join(""); } console.log('anãnam anañam' === 'mañana mañana'.reverse()); 

我认为String.prototype.reverse是解决这个问题的好方法; 代码如下;

 String.prototype.reverse = function() { return this.split('').reverse().join(''); } var str = 'this is a good example for string reverse'; str.reverse(); -> "esrever gnirts rof elpmaxe doog a si siht"; 

使用数组函数,

 String.prototype.reverse = function(){ return [].reduceRight.call(this, function(last, secLast){return last + secLast}); } 
 var str = "my name is saurabh "; var empStr='',finalString=''; var chunk=[]; function reverse(str){ var i,j=0,n=str.length; for(i=0;i<n;++i){ if(str[i]===' '){ chunk[j]=empStr; empStr = ''; j++; }else{ empStr=empStr+str[i]; } } for(var z=chunk.length-1;z>=0;z--){ finalString = finalString +' '+ chunk[z]; console.log(finalString); } return true; } reverse(str); 

我自己的原创尝试​​…

 var str = "The Car"; function reverseStr(str) { var reversed = ""; var len = str.length; for (var i = 1; i < (len + 1); i++) { reversed += str[len - i]; } return reversed; } var strReverse = reverseStr(str); console.log(strReverse); // "raC ehT" 

http://jsbin.com/bujiwo/19/edit?js,console,output

 //es6 //array.from const reverseString = (string) => Array.from(string).reduce((a, e) => e + a); //split const reverseString = (string) => string.split('').reduce((a, e) => e + a); //split problem "𠜎𠺢".split('')[0] === Array.from("𠜎𠺢")[0] // " " === "𠜎" => false "😂😹🤗".split('')[0] === Array.from("😂😹🤗")[0] // " " === "😂" => false 

保持干爽简单的傻!

 function reverse(s){ let str = s; var reverse = ''; for (var i=str.length;i>0;i--){ var newstr = str.substring(0,i) reverse += newstr.substr(-1,1) } return reverse; } 

好吧,非常简单,你可以用一个简单的循环创build一个函数来为你做string反转,而不使用reverse()charAt()等等。

例如,你有这个string:

 var name = "StackOverflow"; 

创build一个这样的function,我把它reverseString

 function reverseString(str) { if(!str.trim() || 'string' !== typeof str) { return; } let l=str.length, s=''; while(l > 0) { l--; s+= str[l]; } return s; } 

你可以这样称呼它:

 reverseString(name); 

结果将是:

 "wolfrevOkcatS" 

另一个变化(它与IE浏览器?):

 String.prototype.reverse = function() { for (i=1,s=""; i<=this.length; s+=this.substr(-i++,1)) {} return s; } 

编辑:

这是没有使用内置的function:

 String.prototype.reverse = function() { for (i=this[-1],s=""; i>=0; s+=this[i--]) {} return s; } 

注意:这个[-1]包含一个string的长度。

然而,不可能将string反转,因为对单个数组元素的赋值不适用于String对象(protected?)。 即你可以做指定,但结果string不会改变。

 var str = "IAMA JavaScript Developer"; var a=str.split(''), b = a.length; for (var i=0; i<b; i++) { a.unshift(a.splice(1+i,1).shift()) } a.shift(); alert(a.join('')); 
 function reverse_string(string) { var string; var len = string.length; var stringExp = string.split(''); var i; for (i = len-1; i >=0;i--) { var result = document.write(stringExp[i]); } return result; } reverse_string("This is a reversed string"); 

//输出:gnirts desrever a si sihT

 function reverse(str){ var s = ""; for (var i = str.length - 1; i >= 0; i--){ s += str[i]; } return s; }; reverse("your string comes here") 

下面的代码可以帮助任何想要recursion地反转string的人。 被要求在最近的一次采用函数式编程风格的求职面试中这样做:

 var reverseStr = function(str) { return (str.length > 0) ? str[str.length - 1] + reverseStr(str.substr(0, str.length - 1)) : ''; }; //tests console.log(reverseStr('setab retsam')); //master bates 

添加到string原型是理想的(以防万一它被添加到核心的JS语言),但是你首先需要检查它是否存在,并添加它,如果它不存在,如下所示:

 String.prototype.reverse = String.prototype.reverse || function () { return this.split('').reverse().join(''); }; 

我想,这将为你工作

 function reverse(str){ str = str.split("").reverse().join("").split(" ").reverse().join(" "); console.log(str) }