String

一个很常见的想法是,string 实质上只是字符的 array。虽然内部的实现可能是也可能不是 array,但重要的是要理解 JavaScript 的 string 与字符的 array 确实不一样。它们的相似性几乎只是表面上的。

举个例子,让我们考虑这两个值:

  1. var a = "foo";
  2. var b = ["f","o","o"];

String 确实与 array 有很肤浅的相似性 — 也就是上面说的,类 array — 举例来说,它们都有一个 length 属性,一个 indexOf(..) 方法(在 ES5 中仅有 array 版本),和一个 concat(..) 方法:

  1. a.length; // 3
  2. b.length; // 3
  3. a.indexOf( "o" ); // 1
  4. b.indexOf( "o" ); // 1
  5. var c = a.concat( "bar" ); // "foobar"
  6. var d = b.concat( ["b","a","r"] ); // ["f","o","o","b","a","r"]
  7. a === c; // false
  8. b === d; // false
  9. a; // "foo"
  10. b; // ["f","o","o"]

那么,它们基本上都仅仅是“字符的数组”,对吧? 不确切

  1. a[1] = "O";
  2. b[1] = "O";
  3. a; // "foo"
  4. b; // ["f","O","o"]

JavaScript 的 string 是不可变的,而 array 是相当可变的。另外,在 JavaScript 中用位置访问字符的 a[1] 形式不总是广泛合法的。老版本的 IE 就不允许这种语法(但是它们现在允许了)。相反,正确的 方式是 a.charAt(1)

string 不可变性的进一步的后果是,string 上没有一个方法是可以原地修改它的内容的,而是创建并返回一个新的 string。与之相对的是,许多改变 array 内容的方法实际上 原地修改的。

  1. c = a.toUpperCase();
  2. a === c; // false
  3. a; // "foo"
  4. c; // "FOO"
  5. b.push( "!" );
  6. b; // ["f","O","o","!"]

另外,许多 array 方法在处理 string 时非常有用,虽然这些方法不属于 string,但我们可以对我们的 string “借用”非变化的 array 方法:

  1. a.join; // undefined
  2. a.map; // undefined
  3. var c = Array.prototype.join.call( a, "-" );
  4. var d = Array.prototype.map.call( a, function(v){
  5. return v.toUpperCase() + ".";
  6. } ).join( "" );
  7. c; // "f-o-o"
  8. d; // "F.O.O."

让我们来看另一个例子:翻转一个 string(顺带一提,这是一个 JavaScript 面试中常见的细节问题!)。array 拥有一个原地的 reverse() 修改器方法,但是 string 没有:

  1. a.reverse; // undefined
  2. b.reverse(); // ["!","o","O","f"]
  3. b; // ["!","o","O","f"]

不幸的是,这种“借用” array 修改器不起作用,因为 string 是不可变的,因此它不能被原地修改:

  1. Array.prototype.reverse.call( a );
  2. // 仍然返回一个“foo”的 String 对象包装器(见第三章) :(

另一种迂回的做法(也是黑科技)是,将 string 转换为一个 array,实施我们想做的操作,然后将它转回 string

  1. var c = a
  2. // 将 `a` 切分成一个字符的数组
  3. .split( "" )
  4. // 翻转字符的数组
  5. .reverse()
  6. // 将字符的数组连接回一个字符串
  7. .join( "" );
  8. c; // "oof"

如果你觉得这很难看,没错。不管怎样,对于简单的 string好用,所以如果你需要某些快速但是“脏”的东西,像这样的方式经常能满足你。

警告: 小心!这种方法对含有复杂(unicode)字符(星型字符、多字节字符等)的 string 不起作用。你需要支持 unicode 的更精巧的工具库来准确地处理这种操作。在这个问题上可以咨询 Mathias Bynens 的作品:Esreverhttps://github.com/mathiasbynens/esrever)。

另外一种考虑这个问题的方式是:如果你更经常地将你的“string”基本上作为 字符的数组 来执行一些任务的话,也许就将它们作为 array 而不是作为 string 存储更好。你可能会因此省去很多每次都将 string 转换为 array 的麻烦。无论何时你确实需要 string 的表现形式的话,你总是可以调用 字符的 arrayjoin("") 方法。