tring类型
String 类型是字符串的对象包装类型,可以像下面这样使用 String 构造函数来创建。
String 对象的方法也可以在所有基本的字符串值中访问到。其中,继承的 valueOf()、toLocale- String()和 toString()方法,都返回对象所表示的基本字符串值。
String 类型的每个实例都有一个 length 属性,表示字符串中包含多个字符。
var stringValue="hello world"; alert(stringValue.length); //"11"
这个例子输出了字符串"hello world"中的字符数量,即"11"。应该注意的是,即使字符串中包 含双字节字符(不是占一个字节的 ASCII 字符),每个字符也仍然算一个字符。
String 类型提供了很多方法,用于辅助完成对 ECMAScript 中字符串的解析和操作。
1.字符方法
两个用于访问字符串中特定字符的方法是:charAt()和 charCodeAt()。这两个方法都接收一个 参数,即基于 0 的字符位置。其中,charAt()方法以单字符字符串的形式返回给定位置的那个字符 (ECMAScript 中没有字符类型)。
var stringValue="hello world"; alert(stringValue.charAt(1)); //"e"
字符串"hello world"位置 1 处的字符是"e",因此调用 charAt(1)就返回了"e"。如果你想得到 的不是字符而是字符编码,那么就要像下面这样使用 charCodeAt()了。
var stringValue="hello world"; alert(stringValue.charCodeAt(1)); //输出"101"
这个例子输出的是"101",也就是小写字母"e"的字符编码。
ECMAScript 5 还定义了另一个访问个别字符的方法。在支持此方法的浏览器中,可以使用方括号加数 字索引来访问字符串中的特定字符,
var stringValue="hello world"; alert(stringValue[1]); //"e"
使用方括号表示法访问个别字符的语法得到了 IE8 及 Firefox、Safari、Chrome 和 Opera 所有版本的 支持。如果是在 IE7 及更早版本中使用这种语法,会返回 undefined 值(尽管根本不是特殊的 6 undefined 值)。
2.字符串操作方法
第一个就是 concat(),用于将一或多个字符串拼接起来,返回拼接得到的新字符串。
var stringValue="hello "; var result=stringValue.concat("world"); alert(result); //"hello world" alert(stringValue); //"hello"
在这个例子中,通过 stringValue 调用 concat()方法返回的结果是"hello world"——但 stringValue 的值则保持不变。实际上,concat()方法可以接受任意多个参数,也就是说可以通过它 9 拼接任意多个字符串。
var stringValue="hello "; var result=stringValue.concat("world", "!"); alert(result); //"hello world!" alert(stringValue); //"hello"
这个例子将"world"和"!"拼接到了"hello"的末尾。虽然 concat()是专门用来拼接字符串的方 11 法,但实践中使用更多的还是加号操作符(+)。而且,使用加号操作符在大多数情况下都比使用 concat() 方法要简便易行(特别是在拼接多个字符串的情况下)。
ECMAScript 还提供了三个基于子字符串创建新字符串的方法:slice()、substr()和 substring()。 12 这三个方法都会返回被操作字符串的一个子字符串,而且也都接受一或两个参数。第一个参数指定子字 符串的开始位置,第二个参数(在指定的情况下)表示子字符串到哪里结束。具体来说,slice()和 substring()的第二个参数指定的是子字符串最后一个字符后面的位置。而 substr()的第二个参数指定的则是返回的字符个数。如果没有给这些方法传递第二个参数,则将字符串的长度作为结束位置。与 concat()方法一样,slice()、substr()和 substring()也不会修改字符串本身的值——它们只是 返回一个基本类型的字符串值,对原始字符串没有任何影响。
var stringValue="hello world"; alert(stringValue.slice(3)); alert(stringValue.substring(3)); alert(stringValue.substr(3)); alert(stringValue.slice(3, 7)); alert(stringValue.substring(3,7)); alert(stringValue.substr(3, 7)); //"lo world" //"lo world" //"lo world" //"lo w" //"lo w" //"lo worl"
这个例子比较了以相同方式调用 slice()、substr()和 substring()得到的结果,而且多数情 况下的结果是相同的。在只指定一个参数 3 的情况下,这三个方法都返回"lo world",因为"hello" 中的第二个"l"处于位置 3。而在指定两个参数 3 和 7 的情况下,slice()和 substring()返回"lo w" ("world"中的"o"处于位置 7,因此结果中不包含"o"),但 substr()返回"lo worl",因为它的第二 个参数指定的是要返回的字符个数。
在传递给这些方法的参数是负值的情况下,它们的行为就不尽相同了。其中,slice()方法会将传 入的负值与字符串的长度相加,substr()方法将负的第一个参数加上字符串的长度,而将负的第二个 参数转换为 0。最后,substring()方法会把所有负值参数都转换为 0。
var stringValue="hello world"; alert(stringValue.slice(-3)); alert(stringValue.substring(-3)); alert(stringValue.substr(-3)); alert(stringValue.slice(3, -4)); alert(stringValue.substring(3, -4)); alert(stringValue.substr(3, -4)); //"rld" //"hello world" //"rld" //"lo w" //"hel" //""(空字符串)
这个例子清晰地展示了上述三个方法之间的不同行为。在给 slice()和 substr()传递一个负值 参数时,它们的行为相同。这是因为-3 会被转换为 8(字符串长度加参数 11+(?3)=8),实际上相当 于调用了 slice(8)和 substr(8)。但 substring()方法则返回了全部字符串,因为它将-3 转换 成了 0。
当第二个参数是负值时,这三个方法的行为各不相同。slice()方法会把第二个参数转换为 7,这 就相当于调用了 slice(3,7),因此返回"lo w"。substring()方法会把第二个参数转换为 0,使调 用变成了 substring(3,0),而由于这个方法会将较小的数作为开始位置,将较大的数作为结束位置, 因此最终相当于调用了 substring(0,3)。substr()也会将第二个参数转换为 0,这也就意味着返回 包含零个字符的字符串,也就是一个空字符串。
3.字符串位置方法
有两个可以从字符串中查找子字符串的方法:indexOf()和 lastIndexOf()。这两个方法都是从 一个字符串中搜索给定的子字符串,然后返子字符串的位置(如果没有找到该子字符串,则返回-1)。 这两个方法的区别在于:indexOf()方法从字符串的开头向后搜索子字符串,而 lastIndexOf()方法 是从字符串的末尾向前搜索子字符串。
var stringValue="hello world"; alert(stringValue.indexOf("o")); //4 alert(stringValue.lastIndexOf("o")); //7
子字符串"o"第一次出现的位置是 4,即"hello"中的"o";最后一次出现的位置是 7,即"world"中的 "o"。如果"o"在这个字符串中仅出现了一次,那么 indexOf()和 lastIndexOf()会返回相同的位置值。
这两个方法都可以接收可选的第二个参数,表示从字符串中的哪个位置开始搜索。换句话说, indexOf()会从该参数指定的位置向后搜索,忽略该位置之前的所有字符;而 lastIndexOf()则会从 指定的位置向前搜索,忽略该位置之后的所有字符。
var stringValue="hello world"; alert(stringValue.indexOf("o", 6)); //7 alert(stringValue.lastIndexOf("o", 6)); //4
在将第二个参数 6 传递给这两个方法之后,得到了与前面例子相反的结果。这一次,由于 indexOf()是从位置 6(字母"w")开始向后搜索,结果在位置 7 找到了"o",因此它返回 7。而 last- IndexOf()是从位置 6 开始向前搜索。结果找到了"hello"中的"o",因此它返回 4。在使用第二个 参数的情况下,可以通过循环调用 indexOf()或 lastIndexOf()来找到所有匹配的子字符串,如下 面的例子所示:
var stringValue="Lorem ipsum dolor sit amet, consectetur adipisicing elit"; var positions=new Array(); var pos=stringValue.indexOf("e"); while(pos > -1){ positions.push(pos); pos=stringValue.indexOf("e", pos + 1); } alert(positions); //"3,24,32,35,52"
这个例子通过不断增加 indexOf()方法开始查找的位置,遍历了一个长字符串。在循环之外,首 先找到了"e"在字符串中的初始位置;而进入循环后,则每次都给 indexOf()传递上一次的位置加 1。 这样,就确保了每次新搜索都从上一次找到的子字符串的后面开始。每次搜索返回的位置依次被保存在 数组 positions 中,以便将来使用。
、string类型
string类型用于表示由零或多个16位Unicode字符组成的字符序列,即字符串。字符串可以由双引号或单引号表示,下面两种字符串的写法都是有效的:
var firstName="Nicholas"; var lastName='zakas';
与PHP中的双引号和单引号会影响对字符串的解释方式不同,ECMAScript中的这两种语法形式没有什么区别。用双引号表示的字符串和单引号表示的字符串完全相同。不过,以双引号开头的字符串也必须以双引号结尾,而以单引号开头的字符串必须以单引号结尾。
二、字符字面量
string数据类型包含一些特殊的字符字面量,也叫转义字符,用于表示非打印字符,或者具有其他用途的字符。这些字符字面量如下表所示:
这些字符字面量可以出现在字符串的任意位置,而且会被当作一个字符来解析,来看下面的例子:
var text="This is the letter sigma: \u03a3"; alert(text.length); //输出28
这个例子中的变量text有28个字符,其中6个字符长的转义序列表示1个字符。任何字符串的长度都可以通过访问其length属性取得。
三、转换为字符串
要不一个值转换为一个字符串有三种方式。
1、toString()方法
第一种是使用几乎每个值都有的toString()方法,这个方法唯一要做的就是返回相应值的字符串表现。数值、布尔值、对象和字符串值都有toString()方法,但null和undefined值没有这个方法。
var age=11; var ageAsString=age.toString(); //字符串"11" var found=true; var foundAsString=found.toString(); //字符串"true"
多数情况下,调用toString()方法不必传递参数。但是,在调用数值的toString()方法时,可以传递一个参数:输出数值的基数。默认情况下,toString()方法以十进制格式返回数值的字符串表示。而通过传递基数,toString()可以输出以二进制、八进制、十六进制。如下所示:
var num=10; alert(num.toString()); //"10" alert(num.toString(2)); //"1010" alert(num.toString(8)); //"12" alert(num.toString(10)); //"10" alert(num.toString(16)); //"a"
2、String()方法
在不知道要转换的值是不是null或undefined的情况下,还可以使用转型函数String(),这个函数能够将任何类型的值转换为字符串。String()函数遵循下列转换规则:
var value1=10; var value2=true; var value3=null; var value4; alert(String(value1)); //"10" alert(String(value2)); //"true" alert(String(value3)); //"null" alert(String(value4)); //"undefined"
3、利用加号操作符
要把某个值转换为字符串,可以使用加号操作符把它与一个字符串(“”)加在一起。
JavaScript中,所有的string类型(或者被称为DOMString)都是使用UTF-16编码的。
MDN
DOMString 是一个UTF-16字符串。由于JavaScript已经使用了这样的字符串,所以DOMString 直接映射到 一个String。
将 null传递给接受DOMString的方法或参数时通常会把其stringifies为“null”。
引出的Unicode中UTF-8与UTF-16编码的详细了解
Unicode(统一码、万国码、单一码)是计算机科学领域里的一项业界标准,包括字符集、编码方案等。Unicode 是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。1990年开始研发,1994年正式公布。
通常Unicode编码是通过2 Byte来表示一个字符的,如U+A12B,2 Byte的二进制表示方法结果就是1010(A)0001(1) 0010(2)1011(B)。
需要注意的是:UTF是Unicode TransferFormat的缩写,UTF-8和UTF-16都是把Unicode码转换成程序数据的一种编码方式。
UTF-8(8-bit Unicode Transformation Format)是一种针对Unicode的可变长度字符编码,又称万国码。由Ken Thompson于1992年创建。现在已经标准化为RFC 3629。UTF-8用1到6个字节编码Unicode字符(尽管如此,2003年11月UTF-8被RFC 3629重新规范,只能使用原来Unicode定义的区域,U+0000到U+10FFFF,也就是说最多4个字节)。用在网页上可以统一页面显示中文简体繁体及其它语言(如英文,日文,韩文)。
UTF-8的来历
UTF-8的规范里充斥着这样神秘的句子:“第一个位元组由110开始,接着的位元组由10开始”,“第一个位元组由1110开始,接着的位元组由10开始”。
那么这到底是什么意思呢?为什么要这么做呢?
我们先从二进制说起。我们都知道,一个字节是由8个二进制位构成的,最小就是0000 0000,最大就是1111 1111。那么一个字节所能表示的最多字符数就是2的8次方,也就是256。对于26个英文字母来说,大小写全算上就是52个,再加上10个阿拉伯数字,62个字符,用可以表达256个不同字符的一个字节来存储是足够了。
但是,我们中国的常用汉字就有3000多个,用一个只能表达256个字符的字节显然是不够存储的。至少也需要有2个字节,1个字节是8个二进制位,2个字节就是16个二进制位,最多可以表达2的16次方,也就是256*256=65536。65536个字符足够容纳所有中国的汉字,外带日语、韩语、阿拉伯语、稀其古怪语等等各种各样的字符。所以这样就产生了Unicode,因为它用2字节表示字符,所以更严格来讲应该叫UCS-2,后来因为怪字符太多,2字节都不够用了,所以又搞出来了一个4字节表示的方法,称作UCS-4。不过现在对绝大多数人来讲UCS-2已经是足够了。
Unicode本来是一个好东西,用2字节表示65536种字符,全人类皆大欢喜的事情。但是偏偏有一帮子西洋人,非要认为这个东西是一种浪费,说我们英文就最多只需要26个字母就够了,1个字节就够了,为什么要浪费2字节呢?比如说字母A就是0100 0001,这一个字节就够了的东西,你弄2字节,非要在前面加8个0,0000 0000 0100 0001,这不是浪费吗?我们就偏要用1字节表示英文。
好吧,我们全人类只好做妥协,规定每个字节,只要看见0打头的,就知道这是英文字母,这肯定不是汉字,只有看见1开头的,才认为这是汉字。
但是我们汉字用1个字节表示不下,那好办,用2个1开头的字符表示1个汉字。这样本来16个二进制位,减去2个开头的1,只剩下14个二进制位了,2的14次方就是16384个字符,对于中文来讲,也是足够用了。但是无奈他们还是想表达65536种字符,那怎么办呢?就需要3个字节才能容纳得下了,于是UTF-8粉墨登场。
首先,首位为0的字符被占了,只要遇到0开头的字符,就知道这是一个1字节的字符,不必再往后数了,直接拿来用就可以,最多表示128种字符,从0000 0000到0111 1111,也就是从0到127。
接下来的事情就比较蹊跷了。我们怎么用1开头的字符既表示2字节,又表示3字节呢?假设我们只判断首位的1,这显然是不行的,没有办法区分,所以我们可以用10或者11开头的字符来表示2字节,但是3字节又该以什么开头?或者可以用10开头表示2字节,用11开头表示3字节?那么4字节的字符将来又该怎么办?也许我们可以用110开头表示3字节,用111开头表示4字节?那么5字节6字节呢?似乎我们看到了一个规律:前面的1越多,代表字节数越多。
这时候,看一下我们的第一种方案:用10开头表示2字节,那么我们的一个字符将是
10xx xxxx 10xx xxxx
用110表示3字节,那么一个3字节的字符将是:
110x xxxx 110x xxxx 110x xxxx
这样无疑是能区分得开的。但是4字节怎么办?
1110 xxxx 1110 xxxx 1110 xxxx 1110 xxxx
吗?这样也能区分开,但似乎有点浪费。因为每个字节的前半扇都被无用的位占满了,真正有意义的只有后面一半。
或者我们干脆这样做得了,我们来设计方案二:为了节省起见,所有后面的字符,我们统统都以10开头,只要遇见10我们就知道它只是整个字符流的一部分,它肯定不是开头,但是10这个开头已经被我们刚刚方案一的2字节字符占用了,怎么办?好办,把2字节字符的开头从10改成110,这样它就肯定不会和10冲突了。于是2字节字符变成
110x xxxx 10xx xxxx
再往后顺推,3字节字符变成
1110 xxxx 10xx xxxx 10xx xxxx
4字节字符变成
1111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx
好像比刚才的方案一有所节省呢!并且还带来了额外的好处:如果我没有见到前面的110或者1110开头的字节,而直接见到了10开头的字节,毫无疑问地可以肯定我遇到的不是一个完整字符的开头,我可以直接忽略这个错误的字节,而直接找下一个正确字符的开头。
这个改良之后的方案二就是UTF-8!
UTF-8表示的字符数
现在,我们来算一下在UTF-8方案里,每一种字节可以表示多少种字符。
1字节的字符,以0开头的,0xxx xxxx,后面7个有效位,2的7次方,最多可以表示128种字符。
2字节的字符,110x xxxx 10xx xxxx,数一数,11个x,所以是2的11次方,2的10次方是1024,11次方就是2048,很不幸,只能表示2048种字符,而我们的常用汉字就有3000多个,看来在这一区是放不下了,只好挪到3字节。
3字节的字符,1110 xxxx 10xx xxxx 10xx xxxx,数一数,16个x,2的16次方,最多可以表示65536个字符,所以我们的汉字就放在这一区,所以在UTF-8方案里我们的汉字都是以3个字节表示的。
所以这也就是这一张表的来历:
UTF-16是Unicode字符编码五层次模型的第三层:字符编码表(Character Encoding Form,也称为 "storage format")的一种实现方式。即把Unicode字符集的抽象码位映射为16位长的整数(即码元, 长度为2 Byte)的序列,用于数据存储或传递。Unicode字符的码位,需要1个或者2个16位长的码元来表示,因此这是一个变长表示。
起初,UTF-16任何字符对应的数字都用两个字节来保。但是,65536显然是不算太多的数字,用它来表示常用的字符是没一点问题,足够了。但如果加上很多特殊的就也不够了。于是,从1996年开始又来了第二个版本,用四个字节表示所有字符。这样就出现了UTF-8,UTF16,UTF-32。UTF-32就是把所有的字符都用32bit也就是4个字节来表示。然后UTF-8,UTF-16就视情况而定了,UTF-16可以选择两字节或四字节。
UTF-16 并不是一个完美的选择,它存在几个方面的问题:
大小端转换?
1、因为utf8是变长编码,而且是单字节为编码单元,不存在谁在高位、谁在低位的问题,所以不存在顺序问题!顺便说一下解码,由于utf8的首字节记 录了总字节数(比如3个),所以读取首字节后,再读取后续字节(2个),然后进行解码,得到完整的字节数,从而保证解码也是正确的。
2、utf16是变长编码,使用1个16-bit编码单元或者2个16-bit编码单元,utf32是定长编码,这里拿utf16举例,在基本平面总是以2个字节为编码单元, 鉴于“第一条”编码单元与编码单元之间的顺序是正确的,问题只能在编码单元内部中字节与字节的顺序,由于硬件cpu的不同,编码单元内部字节 与字节的顺序不确定。假如cpu是大端序那么高位在前,如果cpu是小端序那么低位在前,为了区分,所以有了BOM(byte order mark),然后计 算机才能知道谁是高位,谁是低位,知道了高低位,从而能正确组装,然后才能解码正确。
例如,一个“奎”的Unicode编码是594E,“乙”的Unicode编码是4E59。如果我们收到UTF-16字节流“594E”,那么这是“奎”还是“乙”?如果BOM 是大端序,那么代码点就应该是594E,那么就是“奎”,如果BOM是小端序,那么代码点就应该是4E59,就是“乙”了。
综上所述,因为utf8是单字节为编码单元,在网络传输时,不存在字节序列问题。在解码时,由于首字节记录了总字节数,所以能正确解码。
因为utf16是定长编码,总是以2个字节为编码单元,在网络传输时,不存在字节序列问题。在解码时,由于cpu硬件差异,存在字节序问题,所以通 过BOM来标记字节顺序;
另外,容错性低有时候也是一大问题——局部的字节错误,特别是丢失或增加可能导致所有后续字符全部错乱,错乱后要想恢复,可能很简单,也可能会 非常困难。(这一点在日常生活里大家感觉似乎无关紧要,但是在很多特殊环境下却是巨大的缺陷)
目前支撑我们继续使用 UTF-16 的理由主要是考虑到它是双字节的,在计算字符串长度、执行索引操作时速度很快。当然这些优点 UTF-32 都具有,但很多人毕竟还是觉得 UTF-32 太占空间了。
UTF-8 也不完美,也存在一些问题:
UTF-8 的优点:
大神如何选择的呢?
因为无论是 UTF-8 和 UTF-16/32 都各有优缺点,因此选择的时候应当立足于实际的应用场景。例如在大神的习惯中,存储在磁盘上或进行网络交换时都会采用 UTF-8,而在程序内部进行处理时则转换为 UTF-16/32。对于大多数简单的程序来说,这样做既可以保证信息交换时容易实现相互兼容,同时在内部处理时会比较简单,性能也还算不错。(基本上只要你的程序不是 I/O 密集型的都可以这么干,当然这只是大神粗浅的认识范围内的经验,很可能会被无情的反驳)
*请认真填写需求信息,我们会在24小时内与您取得联系。