3.2 文本
字符串(string)是一组由 16 位值组成的不可变的有序序列,每个字符通常来自于 Unicode 字符集。JavaScript 通过字符串类型来表示文本。字符串的长度(length)是其所含 16 位值的个数。JavaScript 字符串(和其数组)的索引从零开始:第一个字符的位置是 0,第二个字符的位置是 1,以此类推。空字符串(empty string)长度为 0,JavaScript 中并没有表示单个字符的“字符型”。要表示一个 16 位值,只需将其赋值给字符串变量即可,这个字符串长度为 1 。
var hzh = "";
console.log("空字符串的长度:");
console.log(hzh.length);
[Running] node "e:\HMV\JavaScript\JavaScript.js"
空字符串的长度:
0
[Done] exited with code=0 in 0.3 seconds
字符集,内码和JavaScript字符串
JavaScript 采用 UTF-16 编码的 Unicode 字符集,JavaScript 字符串是由一组无符号的16位值组成的序列。最常用的 Unicode 字符(这些字符属于“基本多语种平面”)都是通过 16 位的内码表示,并代表字符串中的单个字符,那些不能表示为 16 位的 Unicode 字符则遵循 UTF-16 编码规则————用两个 16位值组成的一个序列(亦称做“代理项对”)表示。这意味着一个长度为 2 的 JavaScript 字符串(两个 16 位值)有可能表示一个 Unicode 字符:
var p = "π"; // π由16位内码表示0x03c0
var e = "e"; // e由17位内码表示0x1d452
p.length // => 1: p包含一个16位值
e.length // => 2: e通过UTF-16编码后包含两个16位值:"\ud835\udc52"
var hzh1 = "π";
var hzh2 = "e";
console.log("hzh1的长度:");
console.log(hzh1.length);
console.log("hzh2的长度:");
console.log(hzh2.length);
[Running] node "e:\HMV\JavaScript\JavaScript.js"
hzh1的长度:
1
hzh2的长度:
1
[Done] exited with code=0 in 0.175 seconds
JavaScript 定义的各式字符串操作方法均作用于 16 位值,而非字符,且不会对代理项对做单独处理,同样 JavaScript 不会对字符串做标准化的加工,甚至不能保证字符串是合法的 UTF-16 格式。
注
“基本多语种平面”(Basic Multilingual Plane,BMP),也称“零断面”(Plan 0),是 Unicode 中的一个编码区段。编码介于 U+0000~U+FFFF 之间。
3.2.1 字符串直接量
在 JavaScript 程序中的字符串直接量,是由单引号或双引号括起来的字符序列。由单引号定界的字符串中可以包含双引号,由双引号定界的字符串中也可以包含单引号。这里有几个字符串直接量的例子:
"" //空字符串:它包含0个字符
'testing'
"3.14"
'name="myform"'
"Wouldn't you prefer O'Reilly's book?"
"This string\nhas two lines"
"π is the ratio of a circle's circumference to its diameter"
在 ECMAScript 3 中,字符串直接量必须写在一行中,而在 ECMAScript 5 中,字符串直接量可以拆分成数行,每行必须以反斜线(\)结束,反斜线和行结束符都不算是字符串直接量的内容。如果希望在字符串直接量中另起一行,可以使用转义字符\n :
"two\nlines" //这里定义了一个显示为两行的字符串
"one\ //用三行代码定义了显示为单行的字符串,只在ECMAScript 5中可用
long\
line"
需要注意的是,当使用单引号来定界字符串时,需要格外小心英文中的缩写和所有格写法,比如 can't 和 O'Reilly's。因为撇号和单引号是同一个字符,所以必须使用反斜线()来转义所有的撇号。
在客户端 JavaScript 程序设计中,JavaScript 代码会夹杂 HTML 代码的字符串,HTML 代码也会夹杂 JavaScript 代码。和 JavaScript 一样,HTML 也使用单引号或者双引号来定界字符串,因此,当JavaScript 代码和 HTML 代码混杂在一起的时候,最好在 JavaScript 和 HTML 代码中各自使用独立的引号风格。例如,在 JavaScript 表达式中使用单引号表示字符串 "Thank you”,而在 HTML 事件处理程序属性中则使用双引号表示字符串:
<button onclick="alert('Thank you')">Click Me</button>
3.2.2 转义字符
在 JavaScript 字符串中,反斜线()有着特殊的用途,反斜线符号后加一个字符,就不再表示它们的字面含义了,比如,\n 就是一个转义字符(escape sequence),它表示的是一个换行符。
注
escape sequence 译为“转义序列”,有时也译成“转义字符”和“逃逸符”。
另一个例子是上节中提到的转义字符',表示单引号(或撇号)。当需要在一个单引号定界的字符串内使用撇号的时候,它就显得非常有用。现在你就会明白我们为什么把它们叫做转义字符了,因为反斜线可以使我们避免使用常规方式解释单引号,当单引号不是用来标记字符串结尾时,它只是一个撇号:
'You\'re right, it can\'t be a quote'
下表列出了 JavaScript 中的转义字符以及它们所代表的含义。其中有两个是通用的,通过十六进制数表示 Latin-1 或 Unicode 中的任意字码。例如,\xA9 表示版权符号,版权符号的 Latin-1 编码是十六进制数 A9 。同样,\u 表示由 4 个十六进制数指定的任意 Unicode 字符,比如,\u03c0 表示字符π。
表3-1 JavaScript转义字符
转义字符 | 含义 |
---|---|
\o | NUL字符(\u0000) |
\b | 退格符(\u0008) |
\t | 水平制表符(\u0009) |
\n | 换行符(\u000A) |
\v | 垂直制表符(\u000B) |
\f | 换页符(\u000C) |
\r | 回车符(\u000D) |
\" |
双引号(\u0022) |
\' |
撇号或单引号(\uOO27) |
\\ |
反斜线(\UOO5C) |
\xXX | 由两位十六进制数XX指定的Latin-1字符 |
\uXXXX | 由4位十六进制数XXXX指定的Unicode字符 |
如果“\”字符位于没有在上表中列出的字符前,则忽略“\”(当然,JavaScript 语言将来的版本可能定义新的转义符)。比如,“#”和“#”等价。最后,上文提到过,在 ECMAScript 5 中,允许在一个多行字符串直接量里的每行结束处使用反斜线。
3.2.3 字符串的使用
JavaScript 的内置功能之一就是字符串连接。如果将加号(+)运算符用于数字,表示两数相加。但将它作用于字符串,则表示字符串连接,将第二个字符串拼接在第一个之后, 例如:
msg = "Hello, " + "world"; // 生成字符串"Hello, world"
greeting = "Welcome to my blog," + " " + name;
var hzh1 = "黄子涵";
var hzh2 = "是帅哥!"
console.log("拼接字符串:");
console.log(hzh1 + hzh2);
[Running] node "e:\HMV\JavaScript\JavaScript.js"
拼接字符串:
黄子涵是帅哥!
[Done] exited with code=0 in 0.174 seconds
要确定一个字符串的长度————其所包含的16位值的个数————可以使用字符串的length属性。比如,要得到字符串s的长度:
s.length
除了length属性,字符串还提供许多可以调用的方法:
var s = "hello, world" //定义一个字符串
s.charAt(0) // => "h": 第一个字符
s.charAt(s.length-1) // => "d": 最后一个字符
s.substring(1,4) // => "ell": 第2〜4个字符
s.slice(1,4) // => "ell": 同上
s.slice(-3) // => "rid": 最后三个字符
s.indexOf("l") // => 2: 字符1首次出现的位置
s.lastIndexOf("l") // => 10: 字符1最后一次出现的位置
s.IndexOf("1", 3) // => 3: 在位置3及之后首次出现字符1的位置
s.split(", ") // => ["hello, "world"]分割成子串
s.replace('h', 'H') // => "Hello, world": 全文字符替换
s.toUpperCase() // => "HELLO, WORLD"
var hzh = "黄子涵是帅哥!";
var HZH = "hzh";
console.log("输出第一个字符:");
console.log(hzh.charAt(0));
console.log("");
console.log("输出最后一个字符:");
console.log(hzh.charAt(hzh.length-1));
console.log("");
console.log("用substring方法输出字符串某部分:");
console.log(hzh.substring(1,4));
console.log("");
console.log("用slice方法输出字符串某部分:");
console.log(hzh.slice(1,4));
console.log("");
console.log("用slice方法输出字符串最后三个字符:");
console.log(hzh.slice(-3));
console.log("");
console.log("字符“涵”首次出现的位置:");
console.log(hzh.indexOf("涵"));
console.log("");
console.log("字符“涵”最后一次出现的位置:");
console.log(hzh.lastIndexOf("涵"));
console.log("");
console.log("在位置3及之后首次出现字符“涵”的位置");
console.log(hzh.indexOf("涵", 3));
console.log("");
console.log("将数组分割成子串:");
console.log(hzh.split("是"));
console.log("");
console.log("全文字符替换:");
console.log(hzh.replace("子涵", "春钦"));
console.log("");
console.log("转换为大写:");
console.log(HZH.toUpperCase());
[Running] node "e:\HMV\JavaScript\JavaScript.js"
输出第一个字符:
黄
输出最后一个字符:
!
用substring方法输出字符串某部分:
子涵是
用slice方法输出字符串某部分:
子涵是
用slice方法输出字符串最后三个字符:
帅哥!
字符“涵”首次出现的位置:
2
字符“涵”最后一次出现的位置:
2
在位置3及之后首次出现字符“涵”的位置
-1
将数组分割成子串:
[ '黄子涵', '帅哥!' ]
全文字符替换:
黄春钦是帅哥!
转换为大写:
HZH
[Done] exited with code=0 in 0.247 seconds
记住,在 JavaScript 中字符串是固定不变的,类似replace()toUpperCase() 的方法都返回新字符串,原字符串本身并没有发生改变。
在 ECMAScript 5中,字符串可以当做只读数组,除了使用 charAt() 方法,也可以使用方括号来访问字符串中的单个字符(16位值):
s = "hello, world";
s[0] // => "h"
s[s.length-1] // => "d"
var hzh = "黄子涵是帅哥!";
console.log("字符串的第一个元素:");
console.log(hzh[0]);
console.log("");
console.log("字符串的最后一个元素:");
console.log(hzh[hzh.length-1]);
[Running] node "e:\HMV\JavaScript\JavaScript.js"
字符串的第一个元素:
黄
字符串的最后一个元素:
!
[Done] exited with code=0 in 0.176 seconds
基于 Mozilla 的 Web 浏览器(比如 Firefox )很久之前就支持这种方式的字符串索引,多数现代浏览器( IE 除外)也紧跟 Mozilla 的脚步,在 ECMAScript 5 成型之前就支持了这一特性。
3.2.4 模式匹配
JavaScript 定义了 RegExp() 构造函数,用来创建表示文本匹配模式的对象。这些模式称为“正则表达式”(regular expression),JavaScript 采用 Perl 中的正则表达式语法。String 和 RegExp 对象均定义了利用正则表达式进行模式匹配和査找与替换的函数。
RegExp 并不是 JavaScript 的基本类型。和 Date 一样,它只是一种具有实用 API 的特殊对象。正则表达式的语法很复杂,API 也很丰富。RgeExp 是一种强大和常用的文本处理工具。
尽管 RegExp 并不是语言中的基本数据类型,但是它们依然具有直接量写法,可以直接在 JavaScript 程序中使用。在两条斜线之间的文本构成了一个正则表达式直接量。第二条斜线之后也可以跟随一个或多个字母,用来修饰匹配模式的含义,例如:
/^HTML/ //匹配以HTML开始的字符串
/[1-9][0-9]*/ //匹配一个非零数字,后面是任意个数字
/\bjavascript\b/i // 匹配单词"javascript",忽略大小写
RegExp 对象定义了很多有用的方法,字符串同样具有可以接收 RegExp 参数的方法,例如:
var text = "testing: 1, 2, 3"; // 文本示例
var pattern = /\d+/g // 匹配所有包含一个或多个数字的实例
pattern.test(text) // => true: 匹配成功
text.search(pattern) // => 9: 首次匹配成功的位置
text.match(pattern) // => ["1", "2", "3"]: 所有匹配组成的数组
text.replace(pattern, "#") // => "testing: #, #, #"
text.split(/\D+/); // => ["","l","2","3"]: 用非数字字符截取字符串
var hzh = "黄子涵的手机号码:1, 9, 2, 1, 3, 2, 3, 4, 9, 3";
var pattern = /\d+/g;
console.log("看能不能匹配成功?");
console.log(pattern.test(hzh));
console.log("");
console.log("首次匹配成功的位置:");
console.log(hzh.search(pattern));
console.log("");
console.log("所有匹配组成的数组:");
console.log(hzh.match(pattern));
console.log("");
console.log("把数字替换为“?”");
console.log(hzh.replace(pattern,"?"));
console.log("");
console.log("用非数字字符截取字符串:");
console.log(hzh.split(/\D+/));
[Running] node "e:\HMV\JavaScript\JavaScript.js"
看能不能匹配成功?
true
首次匹配成功的位置:
9
所有匹配组成的数组:
[
'1', '9', '2', '1',
'3', '2', '3', '4',
'9', '3'
]
把数字替换为“?”
黄子涵的手机号码:?, ?, ?, ?, ?, ?, ?, ?, ?, ?
用非数字字符截取字符串:
[
'', '1', '9', '2',
'1', '3', '2', '3',
'4', '9', '3'
]
[Done] exited with code=0 in 0.176 seconds