如何在JavaScript中反转字符串?

406 javascript string reverse

当传递给带有return语句的函数时,如何在JavaScript中反转字符串(或就地)?所有没有使用内置功能?.reverse(),.charAt()等等.

bel*_*qua 707

只要您处理简单的ASCII字符,并且您很乐意使用内置函数,这将有效:

function reverse(s){
    return s.split("").reverse().join("");
}
Run Code Online (Sandbox Code Playgroud)

如果您需要支持UTF-16或其他多字节字符的解决方案,请注意此函数将提供无效的unicode字符串或看起来很有趣的有效字符串.你可能想要考虑这个答案.

  • 对于包含代理项对的UTF-16字符串,即基本多语言平面之外的字符,这会被破坏.它还会为包含组合字符的字符串提供有趣的结果,例如,可能会在后面的字符上显示分音符.第一个问题将导致无效的unicode字符串,第二个问题将导致看起来很有趣的有效字符串. (44认同)
  • @MartinProbst我的答案为处理代理对并正确组合标记的问题提供了一个支持Unicode的解决方案:http://stackoverflow.com/a/16776380/96656 (14认同)
  • 对于 UTF-16 `return [...s].reverse().join("");` 可能有效。 (4认同)
  • @Richeve Bebedor"所有没有使用内置函数?.reverse()"这不是一个公认的解决方案,因为它不适合问题的范围,尽管它是在JS中反转字符串的可行解决方案. (2认同)
  • @DavidStarkey:是的,将近四年后回顾这一点,很难看出我是如何完全忽略了问题的重点。看起来我应该等两分钟,然后投票赞成 crescentfresh 对原始帖子的评论! (2认同)

Mat*_*ens 401

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

// Don’t use this!
var naiveReverse = function(string) {
    return string.split('').reverse().join('');
}
Run Code Online (Sandbox Code Playgroud)

事实上,到目前为止发布的所有答案都是这种模式的变体.但是,此解决方案存在一些问题.例如:

naiveReverse('foo  bar');
// ? 'rab ?? oof'
// Where did the `` symbol go? Whoops!
Run Code Online (Sandbox Code Playgroud)

如果您想知道为什么会这样,请阅读JavaScript的内部字符编码.(TL; DR: is an astral symbol, and JavaScript exposes it as two separate code units.)

But there’s more:

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

A good string to test string reverse implementations is the following:

'foo  bar mañana man?ana'
Run Code Online (Sandbox Code Playgroud)

Why? Because it contains an astral symbol ()(由JavaScript中的代理对表示)和一个组合标记(n?在最后一个man?ana实际上由两个符号组成:U + 006E LATIN SMALL LETTER N和U + 0303 COMBINING TILDE).

代理对出现的顺序不能反转,否则星形符号将不再出现在"反向"字符串中.这就是您??在前一个示例的输出中看到这些标记的原因.

组合标记始终应用于上一个符号,因此您必须将主符号(U + 006E LATIN SMALL LETTER N)作为组合标记(U + 0303 COMBINING TILDE)整体处理.反转它们的顺序将使组合标记与字符串中的另一个符号配对.这就是为什么示例输出a?而不是ñ.

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


为了回答你的初始问题 - 如何[正确]反转JavaScript中的字符串 - ,我编写了一个能够识别Unicode的字符串反转的小型JavaScript库.它没有我刚才提到的任何问题.该图书馆名为Esrever ; 它的代码在GitHub上,几乎适用于任何JavaScript环境.它带有一个shell实用程序/二进制文件,因此如果需要,您可以轻松地从终端中反转字符串.

var input = 'foo  bar mañana man?ana';
esrever.reverse(input);
// ? 'anan?am anañam rab  oof'
Run Code Online (Sandbox Code Playgroud)

至于"就地"部分,请参阅其他答案.

  • 您应该在答案中包含Esrever代码的主要部分. (61认同)
  • 虽然这对于解释问题非常重要,但实际的*答案*是[在另一座城堡](http://meta.stackexchange.com/questions/225370/your-answer-is-in -another-城堡时,是-的回答 - 不的回答).正如@ r0estir0bbe在一年多前所说的那样,相关代码应该在*答案中,而不仅仅是链接. (19认同)
  • 当然,问题在于"反转字符串"听起来毫不含糊,但它并不是面对这里提到的问题.是否正在返回一个返回字符串的字符串,打印时它会以相反的顺序显示字符串中的字形集群?一方面,这听起来很可能.另一方面,你为什么要这样做呢?这个定义取决于它的打印,打印反向字符串很少有用.作为算法的一部分,您的要求可能完全不同. (7认同)
  • “希望这解释了为什么到目前为止发布的所有答案都是错误的” - 这种断言过于强烈。许多用例不需要 UTF-16 支持(简单示例;使用 URL 和 URL 组件/参数)。解决方案不会仅仅因为它不处理非必需的场景而“错误”。值得注意的是,最高投票的答案明确声明它仅适用于 ASCII 字符,因此绝对没有一点错误。 (4认同)
  • @Meglio 通过这种具体方法,是的。 (2认同)

小智 88

String.prototype.reverse_string=function() {return this.split("").reverse().join("");}
Run Code Online (Sandbox Code Playgroud)

要么

String.prototype.reverse_string = function() {
    var s = "";
    var i = this.length;
    while (i>0) {
        s += this.substring(i-1,i);
        i--;
    }
    return s;
}
Run Code Online (Sandbox Code Playgroud)

  • 但是,当存在Unicode复合字符时,两种解决方案都不起作用 (9认同)
  • 字符串连接很昂贵.最好建立一个数组并加入它或使用concat(). (3认同)
  • #1最好,#2可能非常慢 (2认同)
  • @JuanMendes我在2009年留下了这个评论,过去4年里情况发生了变化.:P (2认同)

Ani*_*ole 56

详细分析和十种不同的方法来反转字符串及其性能细节.

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

这些实现的性能:

每个浏览器的最佳性能实现

  • Chrome 15 - 实施1和6
  • Firefox 7 - 实施6
  • IE 9 - 实施4
  • Opera 12 - 实施9

以下是这些实现:

实施1:

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

实施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('');
}
Run Code Online (Sandbox Code Playgroud)

实施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('');
}
Run Code Online (Sandbox Code Playgroud)

实施4:

function reverse(s) {
  return s.split('').reverse().join('');
}
Run Code Online (Sandbox Code Playgroud)

实施5:

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

实施6:

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

实施7:

function reverse(s) {
  return (s === '') ? '' : reverse(s.substr(1)) + s.charAt(0);
}
Run Code Online (Sandbox Code Playgroud)

实施8:

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

实施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('');
    }
Run Code Online (Sandbox Code Playgroud)

实施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));
}
Run Code Online (Sandbox Code Playgroud)


小智 51

整个"逆转一个字符串到位"是一个过时的面试问题C程序员,他们采访过的人(为了复仇,也许?)会问.不幸的是,它是"In Place"部分不再有效,因为几乎所有托管语言(JS,C#等)中的字符串都使用不可变字符串,从而无法在不分配任何新内存的情况下移动字符串.

虽然上面的解决方案确实会反转字符串,但如果不分配更多内存,它们就不会这样做,因此不满足条件.您需要直接访问已分配的字符串,并能够操纵其原始内存位置以便能够将其反转到位.

就个人而言,我真的很讨厌这些面试问题,但遗憾的是,我相信我们会在未来几年继续看到它们.

  • 我至少可以说,当他问我如何在JS中"替换"字符串时,我有一位面试官,但是我解释了为什么它不可能,因为JS中的字符串是不可变的.我不知道这是他预期的答案,还是我教育了他一点.无论哪种方式,它都没问题;) (7认同)
  • 在什么意义上JS是一种"托管语言"? (3认同)

Mic*_*ski 38

首先,用于Array.from()将字符串转换为数组,然后Array.prototype.reverse()反转数组,然后Array.prototype.join()将其转换为字符串.

const reverse = str => Array.from(str).reverse().join('');
Run Code Online (Sandbox Code Playgroud)

  • 这应该是可接受的答案,因为它也适用于unicode.例如,从上面的例子:`Array.from('foobarmañanamañana').reverse().join('')=='anãnamonañamraboof'` (5认同)
  • @JulianTF不完全是,一个代字号仍应用于“ a”而不是“ n”。 (3认同)
  • @RomanBoiko 是的,但你可以先规范化字符串。`Array.from('foo bar mañana mañana'.normalize('NFC')).reverse().join('')` 将变为 `"anañam anañam rab oof"` (3认同)
  • @felixfbecker不,`string.split('')`不起作用。有关更多说明,请参见[此答案](http://stackoverflow.com/a/16776621/3853934)。 (2认同)

Luk*_*uke 25

在ECMAScript 6中,您可以更快地反转字符串而不使用.split('')split方法,扩展运算符如下:

var str = [...'racecar'].reverse().join('');
Run Code Online (Sandbox Code Playgroud)

  • 除非你正在打代码高尔夫,否则你应该避免这种情况。对于大多数人来说,编写“string.split('')”比“[...string]”更清晰。 (3认同)
  • @AnnanFay `.split('')` 对于补充平面(UTF-16 中的代理对)中的字符存在问题,因为它按 UTF-16 代码 *unit* 而不是代码 *point* 进行分割。扩展运算符和“Array.from()”(我的偏好)则不然。 (3认同)

nin*_*cko 19

好像我迟到了3年...

不幸的是你不能像我们指出的那样.请参阅JavaScript字符串是否不可变?我需要JavaScript中的"字符串构建器"吗?

您可以做的另一件好事是创建一个"视图"或"包装器",它接受一个字符串并重新实现您正在使用的字符串API的任何部分,但假装字符串是相反的.例如:

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 e.g. "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
Run Code Online (Sandbox Code Playgroud)

演示:

> 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"
Run Code Online (Sandbox Code Playgroud)

踢球者 - 以下是纯数学就地完成,只访问每个角色一次,并且只在必要时:

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

> S('0123456789ABCDEF').slice(3).reversed().slice(1,-1).toLowerCase().charAt(3)
"b"
Run Code Online (Sandbox Code Playgroud)

如果应用于非常大的字符串,如果您只采用相对较小的片段,则会产生显着的节省.

这是否值得(在大多数编程语言中像反转一样)在很大程度上取决于您的用例以及重新实现字符串API的效率.例如,如果您只想进行字符串索引操作,或者使用小slices或substrs,这将节省您的空间和时间.但是,如果您计划打印大型反转切片或子串,那么节省的确可能很小,甚至比完整副本更糟糕.您的"反向"字符串也不具有该类型string,但您可以通过原型设计伪造它.

上面的演示实现创建了一个ReversedString类型的新对象.它是原型的,因此效率很高,几乎只需要很少的工作量和最小的空间开销(共享原型定义).这是一个涉及延迟切片的惰性实现.一旦执行像一个函数.slice或者.reversed,它会执行索引数学.最后,当您提取数据时(通过隐式调用.toString().charCodeAt(...)其他内容),它将以"智能"方式应用这些数据,触及可能的最少数据.

注意:上面的字符串API是一个示例,可能无法完美实现.您还可以使用您需要的1-2个功能.

  • +1对于"不要反转它 - 只是假装你做了"答案. (7认同)

Hus*_*een 13

您可以通过多种方式来反转JavaScript中的字符串。我记下了我喜欢的三种方式。

方法1:使用反向功能:

function reverse(str) {
  return str.split('').reverse().join('');
}
Run Code Online (Sandbox Code Playgroud)

方法2:遍历字符:

function reverse(str) {
  let reversed = '';

  for (let character of str) {
    reversed = character + reversed;
  }

  return reversed;
}
Run Code Online (Sandbox Code Playgroud)

方法3:使用reduce函数:

function reverse(str) {
  return str.split('').reduce((rev, char) => char + rev, '');
}
Run Code Online (Sandbox Code Playgroud)

我希望这有帮助 :)


小智 10

在一次采访中,我被要求在不使用任何变量或本机方法的情况下反转字符串.这是我最喜欢的实现:

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

  • 零原生方法?怎么样'切片'?: - / (12认同)

Mah*_*ain 10

有多种方法,你可以检查以下,

1.传统的for循环(递增):

function reverseString(str){
        let stringRev ="";
        for(let i= 0; i<str.length; i++){
            stringRev = str[i]+stringRev;
        }
        return stringRev;
}
alert(reverseString("Hello World!"));
Run Code Online (Sandbox Code Playgroud)

2.传统的for循环(递减):

function reverseString(str){
    let revstr = "";
    for(let i = str.length-1; i>=0; i--){
        revstr = revstr+ str[i];
    }
    return revstr;
}
alert(reverseString("Hello World!"));
Run Code Online (Sandbox Code Playgroud)

3.使用for-of循环

function reverseString(str){
    let strn ="";
    for(let char of str){
        strn = char + strn;
    }
    return strn;
}
alert(reverseString("Get well soon"));
Run Code Online (Sandbox Code Playgroud)

4.使用forEach/high order数组方法:

function reverseString(str){

  let revSrring = "";
  str.split("").forEach(function(char){
    
    revSrring = char + revSrring;
  
  });
  return revSrring;
}
alert(reverseString("Learning JavaScript"));
Run Code Online (Sandbox Code Playgroud)

5. ES6标准:

function reverseString(str){

  let revSrring = "";
  str.split("").forEach(char => revSrring = char + revSrring);
  return revSrring;
}
alert(reverseString("Learning JavaScript"));
Run Code Online (Sandbox Code Playgroud)

6.最新方式:

function reverseString(str){

  return str.split("").reduce(function(revString, char){
       return char + revString;
  }, "");
 
}

alert(reverseString("Learning JavaScript"));
Run Code Online (Sandbox Code Playgroud)

7.您也可以使用以下方式获得结果,

function reverseString(str){

  return str.split("").reduce((revString, char)=> char + revString, "");
 
}
alert(reverseString("Learning JavaScript"));
Run Code Online (Sandbox Code Playgroud)


Dr.*_*r.G 10

使用扩展运算符的清晰方法:

const reverseString = str => [...str].reverse().join('');

console.log(reverseString('ABC'));
Run Code Online (Sandbox Code Playgroud)


Fat*_*cet 6

这是我认为最简单的方法

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 '));
Run Code Online (Sandbox Code Playgroud)

  • 很高兴您在示例中包含了一个表情符号.因此,我们很快就会发现这显然不适用于表情符号和许多其他unicode字符. (3认同)

Yog*_*esh 6

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

要么

var str = 'sample string';
console.log(str.split('').reverse().join(''));
Run Code Online (Sandbox Code Playgroud)

//输出:'gnirts elpmas'


jit*_*put 6

如果您不想使用任何内置函数。尝试这个

var string = 'abcdefg';
var newstring = '';

for(let i = 0; i < string.length; i++){
    newstring = string[i] += newstring;
}

console.log(newstring);
Run Code Online (Sandbox Code Playgroud)


Nic*_*ons 6

正如其他人指出的那样,字符串是不可变的,因此您无法就地反转它。您需要生成一个新字符串。执行此操作的一个新选项是使用Intl.Segmenter,它允许您分割视觉字素(即:用户感知的字符单元,例如表情符号、字符等)。Intl.Segmenter目前是第 4 阶段提案,如果您想使用它,可以使用它的polyfill 。目前它的浏览器支持有限,您可以在此处找到更多信息。

\n

reverse()如果您使用以下方法,该方法可能如下所示Intl.Segmenter

\n

\r\n
\r\n
const reverse = str => {\n  const segmenter = new Intl.Segmenter("en", {granularity: \'grapheme\'});\n  const segitr = segmenter.segment(str);\n  const segarr = Array.from(segitr, ({segment}) => segment).reverse();\n  return segarr.join(\'\');\n}\n\nconsole.log(reverse(\'foo  bar ma\xc3\xb1ana man\xcc\x83ana\')); // anan\xcc\x83am ana\xc3\xb1am rab  oof\nconsole.log(reverse(\'This  emoji is happy\')); // yppah si ijome  sihT\nconsole.log(reverse(\'Text surrogate pair  composite pair mo\xcc\x88o varient selector \xe2\x9d\xa4\xef\xb8\x8f & ZWJ \xe2\x80\x8d\xe2\x80\x8d\')); // \xe2\x80\x8d\xe2\x80\x8d JWZ & \xe2\x9d\xa4\xef\xb8\x8f rotceles tneirav oo\xcc\x88m riap etisopmoc  riap etagorrus txeT
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n

上面创建了一个segmenter通过视觉字素来分段/分割字符串的方法。.segment()使用字符串输入调用segmenter,然后返回一个迭代器,该迭代器生成 形式的对象{segment, index, input, isWordLike}。该对象的键segment包含字符串段(即:单个字素)。要将迭代器转换为数组,我们使用Array.from()迭代器并提取分段的字素,这可以用 反转.reverse()。最后,我们使用以下方法将数组连接回字符串:.join()

\n
\n

您还可以尝试另一个选项,它比 Intl.Segmenter 具有更好的浏览器支持,但并不那么安全:

\n
const reverse = str => Array.from(str.normalize(\'NFC\')).reverse().join(\'\');\n
Run Code Online (Sandbox Code Playgroud)\n

这有助于处理由多个代码点和代码单元组成的字符。正如其他答案中所指出的,在诸如 之类的字符串中维护复合和代理对排序存在问题\'foo bar ma\xc3\xb1ana man\xcc\x83ana\'。这是由两个代码单元组成的代理对,最后一个n\xcc\x83是由两个 Unicode 字符组成的复合对,组成一个字素 ( n+ \xcc\x83= n\xcc\x83)。

\n

为了反转每个字符,您可以使用.reverse()数组原型中的方法。正如.reverse()在数组上使用的那样,首先要做的是将字符串转换为字符数组。通常,.split(\'\')用于此任务,但是,这会分割由多个代码单元组成的代理对(如前面的答案所示):

\n
>> \'\'.split(\'\')\n>> `["\xef\xbf\xbd", "\xef\xbf\xbd"]`\n
Run Code Online (Sandbox Code Playgroud)\n

相反,如果您调用String.prototypeSymbol.iterator方法,那么您将能够在数组中保留代理对,因为它会迭代代码点而不是字符串的代码单元:

\n
>> [...\'\']\n>> [""]\n
Run Code Online (Sandbox Code Playgroud)\n

接下来要处理的是字符串中的任何复合字符。迭代时,由两个或多个代码点组成的字符仍将被分割:

\n
>> [...\'o\xcc\x88\']   \n>> ["o", "\xcc\x88"]\n
Run Code Online (Sandbox Code Playgroud)\n

上面将基本字符 (o) 与分音符分开,这不是所需的行为。这是因为它o\xcc\x88是字符的分解版本,由多个代码点组成。为了解决这个问题,您可以使用 ES6 中引入的字符串方法,称为String.prototype.normalize(). 该方法可以通过使用“NFC”作为参数将多个代码点组合成其组合的规范形式。这允许我们将分解的字符o\xcc\x88(o +结合分音符)转换为其仅由一个代码点组成的预组合形式\xc3\xb6拉丁小写字母 o 与分音符)。因此,在可能的情况下,调用.normalize()with"NFC"会尝试用单个代码点替换多个代码点。这使得由两个代码点组成的字素可以用一个代码点来表示。

\n
>> [...\'o\xcc\x88\'.normalize(\'NFC\')]   \n>> ["\xc3\xb6"]\n
Run Code Online (Sandbox Code Playgroud)\n

normalize(\'NFC\')生成一个字符时,它可以在其他字符中安全地反转。将扩展语法和规范化结合在一起,您可以成功反转字符串,例如:

\n

\r\n
\r\n
const reverse = str => Array.from(str.normalize(\'NFC\')).reverse().join(\'\');\n
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n

在某些情况下,上述归一化+迭代会失败。例如,字符 \xe2\x9d\xa4\xef\xb8\x8f (heavy black heart \xe2\x9d\xa4\xef\xb8\x8f) 由两个代码点组成。第一个是心形,后者是变体选择器 16 (U+FE0F),用于定义前一个字符的字形变体。其他角色也会产生类似的问题。

\n

另一件需要注意的事情是ZWJ(零宽度连接符)字符,您可以在某些脚本中找到这些字符,包括表情符号。例如,表情符号 \xe2\x80\x8d\xe2\x80\x8d 由男人、女人和男孩表情符号组成,每个表情符号由 ZWJ 分隔。上面的归一化+迭代方法也无法解释这一点。

\n

因此,使用Intl.Segmenter是比这两种方法更好的选择。目前,Chrome 还拥有自己的特定分段 API,称为Intl.v8BreakIterator。这个分段 API 是非标准的,Chrome 只是简单实现的。因此,它可能会发生变化,并且不适用于大多数浏览器,因此不建议使用。但是,如果您好奇,可以这样做:

\n

\r\n
\r\n
>> \'\'.split(\'\')\n>> `["\xef\xbf\xbd", "\xef\xbf\xbd"]`\n
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n


Sco*_*ner 5

我知道这是一个老问题已经得到很好的回答,但为了我自己的乐趣,我编写了以下反向功能,并认为我会分享它,以防它对其他人有用.它处理代理对和组合标记:

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('');
}
Run Code Online (Sandbox Code Playgroud)

所有关于Mathias,Punycode和其他各种参考资料的道具都是为了让我了解JavaScript中字符编码的复杂性.


小智 5

在ES6中,您还有一个选择

function reverseString (str) {
  return [...str].reverse().join('')
}

reverseString('Hello');
Run Code Online (Sandbox Code Playgroud)


Kam*_*ski 5

你不能,因为 JS 字符串是不可变的。简短的非就地解决方案

[...str].reverse().join``
Run Code Online (Sandbox Code Playgroud)

[...str].reverse().join``
Run Code Online (Sandbox Code Playgroud)


Gia*_*dei 5

你不能string就地反转 a 但你可以使用这个:

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

var s = "ABCD";
s = s.reverse();
console.log(s);
Run Code Online (Sandbox Code Playgroud)