字符串经常被当成字符数组。 字符串的内部实现究竟有没有使用数组并不好说,但JavaScript中的字符串和字符数组并不是一回事,最多只是看上去相似而已。
例如:
var a = 'foo'
var b = ['f', 'o', 'o']
字符串和数组的确很相似,它们都是类数组都有length属性以及indexOf(…)(从ES5开始数组支持此方法)和concat(…)方法。
a.length // 3
b.length // 3
a.indexOf('o') // 1
b.indexOf('o') // 1
var c = a.concat('bar) // 'foobar'
var d = b.concat(['b', 'a', 'r']) // ['f', 'o', 'o', 'b', 'a', 'r']
a === c // false
b === d // false
a // 'foo'
b // ['f', 'o', 'o']
但是并不意味着它们都是“字符数组”,比如:
a[1] = '1'
b[1] = '1'
a // 'foo'
b // ['f', '1', 'o']
JavaScript中字符串是不可变的,而数组是可变的。并且a[1]在JavaScript中并非总是合法语法,在老版本的IE中就不被允许(现在可以了),正确的方法是a.charAt(1)。
字符串不可变是指字符串的成员函数不会改变其原始值,而是创建并返回一个新的字符串。而数组的成员函数都是在其原始值上进行操作。
c = a.toUpperCase();
a === c; // false
a; // "foo"
c; // "FOO"
b.push( "!" );
b; // ["f","O","o","!"]
许多数组函数用来处理字符串很方便,虽然字符串没有这些函数,但可以通过“借用”数组的非变更方法来处理字符串。
a.join; // undefined
a.map; // undefined
var c = Array.prototype.join.call( a, "-" );
var d = Array.prototype.map.call( a, function(v){
return v.toUpperCase() + ".";
} ).join( "" );
c; // "f-o-o"
d; // "F.O.O."
另一个不同点在于字符串反转(面试常见问题),数组有一个字符串没有的可变更成员函数reverse()。
a.reverse; // undefined
b.reverse(); // ["!","o","O","f"]
b; // ["!","o","O","f"]
可惜我们无法”借用”字符串的可变更成员函数,因为字符串是不可变的。
Array.prototype.reverse.call( a );
// 返回值仍然是字符串"foo"的一个封装对象(参考第三章)
一个变通方法是先将字符串转为数组,待处理完成后,再将结果转回字符串。
var c = a
// 将a的值转换为字符数组
.split( "" )
// 将数组中的字符进行倒转
.reverse()
// 将数组中的字符拼接回字符串
.join( "" );
c; // "oof"
注意:上述方法对于包含复杂字符(Unicode,如星号、多字节字符等)的字符串并不适用。这时则需要功能更加完备,能够处理Unicode的工具库。
如果需要经常以字符数组的方式来处理字符串的话,倒不如直接使用数组,然后在需要时使用 join('')
将字符数组转换为字符串。