一、概述
在JavaScript中,所谓字符串就是包含在英文“双引号”或‘单引号’中的内容,可以是数字、运算符号、各国语言、特殊编码字符,甚至还能是HTML的标签。也就是说,只要符合在双引号或单引号中这个要求,计算机文本中的内容基本上都可以被称为一个字符串,ES6新引入了模板字符串,使用反引号(`)标识。
二、字符串引号使用规则 *
在使用字符串的引号时需要注意,如果一个字符串已经使用过引号,若再在字符串内使用引号需要使用另外一个引号。即双引号内使用单引号,单引号内使用双引号,或者使用转义符 \ 对同样的引号进行转换,多层引号嵌套,该规则同样适用,如:
var str1 = "Hello, I'm Petter!";
var str2 = 'How do you think about "JavaScript"?';
var str3 = "设置字体的'颜色'代码是:<p style=\"color:red;\">";
需要了解的一个字符串常识就是,字符串不能直接分成多行去写,否则浏览器会报错。如果字符串过长,需要进行换行使视觉层次上更加地清晰,那需要这样去写:
var str = "这是一个" +
"分行写的" +
"字符串";
console.log(str); // 输出:这是一个分行写的字符串
三、字符串与数组 *
通过上一章中学习的内容可以发现 parseInt() 和 parseFloat() 方法可以将字符串转换为数值,同时也能对数组元素中的第一个元素进行数值转化。这说明在某种程度上,字符串和数组是有一点联系的。实际上,数组拥有的很多属性,字符串也同样具备。来看这样一个输出例子:
var str = 'ABCDE';
var arr = ['A', 'B', 'C', 'D', 'E'];
console.log(str.length); // 5
console.log(arr.length); // 5
通过上例可以清晰地发现,字符串和数组是何等的相似。实际上,数组和字符串是可以互相转换的,这就需要用到两个方法:split() 和 join():
// 1、split 将字符串转换为数组
var result;
var str = "HTML,CSS,JavaScript,jQuery";
result = str.split();
console.log(result); // ["HTML,CSS,JavaScript,jQuery"]
result = str.split(',');
console.log(result); // ["HTML", "CSS", "JavaScript", "jQuery"]
// 2、join 将数组转换为字符串
var arr = ["152", "2888", "5771"];
result = arr.join();
console.log(result); // "152,2888,5771"
result = arr.join('-');
console.log(result); // "152-2888-5771"
从上例可以分析出 split() 方法可以将一个字符串转换为一个数组,若添加一个用引号引起来的参数,得出的数组就会以该参数进行分割(上例中使用的是“,”作为参数,用其它字符作为参数同样可行),若所给参数是一个空引号"",则形成的数组会合并为一个数组元素项。同样的,join() 方法是将一个数组元素转换为一个字符串,根据给出的参数对结果中的字符串进行分割,或者不进行分割。当然,这两个方法也可以不给出参数,用它们内置的默认值进行处理。
四、转义符
反斜杠 \ 在字符串中有特殊用途,用来表示一些特殊的字符,所以又称 转义操作符(简称:转义符),以下是一些常用转义符的表示法:(小括号中的值表示法为Unicode)
\n(或:\u000A)用于表示:换行符
\t(或:\u0009)用于表示:制表符
\'(或:\u0027)用于表示:单引号
\"(或:\u0022)用于表示:双引号
\\(或:\u005C)用于表示:反斜杠
当然,转义符远不止上面列出的这些。你需要注意的是,在非特殊字符前面加上转义符 \,那 \ 会被省略掉,如果需要输出 \,那就需要写成双斜杠 \ \ 的形式。
五、UNICODE
1、字符的 Unicode 表示法
JavaScript 允许采用 \uxxxx 形式表示一个字符,其中 xxxx 表示字符的 Unicode 码点,每个Unicode码都有各自对应的字符,如 \u00A9 就是用于输出版权符号“©”的。
在JavaScript引擎内部,所有字符都用Unicode表示,它不仅以Unicode储存字符,还允许直接在程序中使用Unicode编号表示字符。解析代码的时候,JavaScript会 自动识别 一个字符是字面形式表示,还是Unicode形式表示。输出给用户的时候,所有字符都会转成字面形式。其实也就是说,任何JavaScript中允许的字符都是可以通过Unicode来表示的。
"\u738b\u8005\u8363\u8000"
"王者荣耀"
我们还需要知道,每个字符在JavaScript内部都是以16位(即2个字节)的UTF-16格式储存。也就是说,JavaScript的单位字符长度固定为16位长度,即2个字节。因此,\uxxxx 这种表示法只限于码点在\u0000~\uFFFF之间的字符。超出这个范围的字符,必须用两个双字节的形式表示。
"\u20BB7" // "₻7"
"\uD842\uDFB7" // "𠮷"
上面代码表示,如果直接在\u后面跟上超过0xFFFF的数值(比如\u20BB7),JavaScript会理解成 \u20BB+7。由于 \u20BB 是一个不可打印字符,所以只会显示一个空格,后面跟着一个7。ES6 对这一点做出了改进,只要将码点放入大括号,就能正确解读该字符。
"\u{20BB7}" // "𠮷"
2、str.charCodeAt(idx)
在ES5中,如果想将一个现成的字符转换为十六进制的Unicode的话,需要通过 str.charCodeAt(idx)方法获取字符数字编码值,其中,str 为原字符串,idx为需要获取数字编码的字符对应的下标。然后再通过 toString(16) 将刚才得到的数字编码值转化为一个十六进制的字符,并在这个十六进制的字符的前面拼接上 \u,就可以得到一个十六进制表示法的Unicode了。
/**
* 函数封装:将字符串转为Unicode编码
*/
Object.prototype.toUnicodeString = function(str) {
var s = str || this.valueOf() ;
var res = "";
for (var i = 0; i < s.length; i++) {
res += "\\u" + s.charCodeAt(i).toString(16);
}
return res;
}
"王者荣耀".toUnicodeString(); // "\u738b\u8005\u8363\u8000"
但你需要注意,正如上一小节所说,JavaScript内部,字符以UTF-16的格式储存,每个字符固定为2个字节。对于那些需要4个字节储存的字符(Unicode码点大于0xFFFF的字符),JavaScript会认为它们是两个字符。
var s = "𠮷";
s.length // 2
s.charAt(0) // "�"
s.charAt(1) // "�"
s.charCodeAt(0) // 55362
s.charCodeAt(1) // 57271
上面代码中,汉字**“𠮷”**(注意,这个字不是”吉祥“的”吉“)的码点是0x20BB7,UTF-16编码为 0xD842 0xDFB7(十进制为55362 57271),需要4个字节储存。对于这种4个字节的字符,JavaScript不能正确处理,字符串长度会误判为2,而且 charAt 方法无法读取整个字符,charCodeAt 方法只能分别返回前两个字节和后两个字节的值。
3、String.fromCharCode()
在ES5中,我们可以通过String.fromCharCode(numCode)(这里的“numCode”为转换出的数字编码),又重新将数字编码转换为原来的字符串:
var str = "帅";
var numCode = str.charCodeAt(0); // 24069
var oriStr = String.fromCharCode(numCode); // "帅"
这个方法不能识别32位的UTF-16字符(即Unicode编号大于0xFFFF 的字符)。
String.fromCharCode(0x20BB7) // // "ஷ"
提示:这一小节作为了解
六、包装对象 *
JavaScript语言“一切皆对象”,数组和函数本质上都是对象,就连三种原始类型的值——数值、字符串、布尔值——在一定条件下,也会自动转为对象,也就是原始类型的“包装对象”。
JS中共有三种包装对象:即数值、字符串、布尔值相对应的Number、String、Boolean三个原生对象。这三个原生对象可以把原始类型的值变成(包装成)对象。
var v1 = new Number(10);
var v2 = new String("Hi");
var v3 = new Boolean("true");
上面代码根据原始类型的值,生成了三个对象,与原始值的类型不同。这用typeof运算符就可以看出来。
typeof v1 // "object"
typeof v2 // "object"
typeof v3 // "object"
可见:当上述三个对象充当构造函数时,可以将原始类型的值转换为对象;当充当普通函数时,可以将任意类型的值,转换为原始类型的值。
包装对象即对象,因此继承Object对象提供的原生方法。主要有一个方法叫valueOf(),这个方法的作用是返回包装对象实例对应的原始类型的值----即还原原始类型。
v1.valueOf() // 10
如何判断一个对象是内置对象还是包装对象?
在 JavaScript中对象主要分为两大类,一类称作“包装对象”,一类称作“内置对象”。从编码角度讲,它们的区别是如果使用typeof之后为“object”,那么它就是一个内置对象,否则就是包装对象。
七、字符串对象 *
1、定义
var str = "Hello, world!";
var str = 'Hello, world!';
var str = `Hello, world!`; // ES6
var str = new String("Hi!");
var str = new Object("Hi!");
2、长度
length 属性,用于返回字符串长度。
var str = "China!";
str.length; // 6
3、查询
3.1、charAt()
charAt 方法返回指定位置的字符,参数是从0开始编号的下标。其语法形式为:charAt(idx)
var s = new Object("ABCDE");
String {"ABCDE"}
0: "A"
1: "B"
2: "C"
3: "D"
4: "E"
s.charAt(2);
"C"
s.charAt(); // 如果没有传递任何参数,默认下标为0
"A"
s.charAt(s.length - 1); // 获取最后一个字符
"E"
// 如果传入的下标超出了有效下标的范围,则返回空字符串
s.charAt(5);
""
s.charAt(-1);
""
3.2、indexOf() 、 lastIndexOf()
这两个方法用于确定一个字符串在另一个字符串中的位置,返回一个整数,表示匹配开始的位置。如果返回-1,就表示不匹配。两者的区别在于,indexOf 从字符串头部开始匹配,lastIndexOf 从尾部开始匹配。
var s = "干包谷林头绑干包谷";
s.indexOf("包谷"); // 1
s.indexOf("玉米"); // -1
s.lastIndexOf("包谷"); //7
s.lastIndexOf("玉米"); // -1
它们还可以接受第二个参数,对于 indexOf 方法,第二个参数表示从该位置开始向后匹配;对于lastIndexOf,第二个参数表示从该位置起向前匹配。
var str = 'Hello, world!';
str.indexOf('l', 5); // 10
str.lastIndexOf('l', 5) // 3
3.3、match()
match 方法用于字符串查询,如果没有找到,则返回null,如果找到,返回一个数组。
var str = "abc";
str.match("ab"); // ["ab"]
str.match("ac"); // null
3.4、search()
search 方法的用法等同于 indexOf/lastIndexOf,但是其返回值为匹配到的第一个位置。如果没有找到匹配,则返回 -1。
var str = 'Hello, china!';
str.search('china'); // 7
str.search('world'); // -1
3.5、查询头部/尾部
字符串提供了三种方法便于我们查询是头部、尾部和是否包含,具体如下:
- startsWith:查询头部
- endsWith:查询尾部
- includes:查询是否包含
当然,你也可以使用正则表达式实现同样的效果:
var website = "http://www.baidu.com";
// 查询头部,正则表达式规则:/^condition/
/^http/.test(website);
// 查询尾部,正则表达式规则:/condition$/
/com$/.test(website);
// 查询是否包含,正则表达式规则:/condition/
/baidu/.test(website);
4、拼接
拼接字符串,使用+号:
var str1 = 'Hello, ';
var str2 = 'China!';
var str3 = str1 + str2; // "Hello, China!";
除了使用 + 拼接外,你还可以使用 concat 方法对字符串进行拼接,如:
var s1 = 'a';
var s2 = 'b';
s1.concat(s2);
'ab'
提示:从性能上考虑,强烈建议使用赋值操作符(
+,+=)代替concat方法。
5、截取
slice 方法用于字符串截取,其语法形式为:
slice(start, end)
start:开始位置end:结束位置(不含该位置)
'JavaScript'.slice(0, 4) // "Java"
如果省略第二个参数,则表示从指定位置开始截取到末尾。
'JavaScript'.slice(4) // "Script"
如果参数是负值,表示从结尾开始倒数计算的位置,即该负值加上字符串长度。
'JavaScript'.slice(-6) // "Script"
'JavaScript'.slice(0, -6) // "Java"
'JavaScript'.slice(-2, -1) // "p"
如果第一个参数大于第二个参数,slice 方法返回一个空字符串。
'JavaScript'.slice(2, 1) // ""
6、去除空格
trim 方法用于去除字符串两端的空格,返回一个新字符串,不改变原字符串。
' Hello world! '.trim() // "Hello world!"
7、大小写转换
toLowerCase:转小写toUpperCase:转大写
"ChInA".toLowerCase();
"china"
"ChInA".toUpperCase();
"CHINA"
这个方法也可以将布尔值或数组转为大写字符串,但是需要通过call方法使用。
String.prototype.toUpperCase.call(true)
// 'TRUE'
String.prototype.toUpperCase.call(['a', 'b', 'c'])
// 'A,B,C'
8、字符串比较
JavaScript允许使用 > 、< 或 == 比较字符串,返回一个布尔。其在比较时是通过字符的 Unicode 编码进行比较的。
var compare = (s1, s2) => {
if (s1 > s2) {
console.log(`${s1} > ${s2}`);
}else if(s1 == s2) {
console.log(`${s1} = ${s2}`);
}else {
console.log(`${s1} < ${s2}`);
}
}
compare('a', 'b'); // "a < b"
compare('a', 'A'); // "a > A"
compare('ab', 'ac'); // "ab < ac"
compare('ab', 'ab'); // "ab = ab"
有一种情况需要注意,就是计算机的区域设置。因为用"<"和">"来比较字符串时,并不会考虑当地的排序规则,比如在西班牙语中,按照传统的排序,"ch"将作为一个字符排在"c"和"d"之间。localeCompare() 提供了一种方式,可以帮助你使用默认区域设置下的字符排序规则。
localeCompare 用本地特定的顺序来比较两个字符串。其语法形式为: stringObject.localeCompare(target),如果 stringObject 小于 target,则 localeCompare() 返回小于 0 的数。如果 stringObject 大于 target,则该方法返回大于 0 的数。如果两个字符串相等,或根据本地排序规则没有区别,该方法返回 0。
'apple'.localeCompare('banana') // -1
'apple'.localeCompare('apple') // 0
提示:ECMAscript 标准并没有规定如何进行本地特定的比较操作,它只规定该函数采用底层操作系统提供的排序规则。
9、字符串替换
replace 方法用于字符串替换,其语法形式为:
replace(search, replacement)
search:替换源replacement:要替换成什么数据
var str = "Hello, world!";
str.replace("world", "china"); // "Hello, china!"
replace只会替换第一次匹配到的字段,不能完全替换:
var str = "#fffff#";
str.replace("f", "o"); // "#offff#"
上述例子中只会替换第一个f,如果想要全局匹配替换,我们可以使用正则表达式,如下所示:
// g -> global:全局模式
str.replace(/f/g, "o"); // "#ooooo#"
replace 方法的第2个参数也可以是一个函数,如下所示:
"Tel:17398888669;".replace(/(\d{3})(\d{4})(\d{4})/, (match,$1, $2, $3, offset, string) => {
console.log(match, $1, $2, $3, offset, string);
return `${$1} ${$2} ${$3}`;
})
// 17398888669 173 9888 8669 4 Tel:17398888669;
// Tel:173 9888 8669;
可以看到,当参数为函数时,其中各参数表示:
match:匹配的字符串(17398888669)。$1:如果replace放法的第1个参数是正则表达式,则$1表示第1个括号(组匹配)匹配的字符串,以此类推,$2/$3表示第2 / 3 个括号匹配字符串。offset:偏移量,表示被匹配到的字符串在原始字符串中的位置。string:被匹配的原始字符串。
替换字符串可以插入下面的特殊变量名:
| 变量名 | 代表的值 |
|---|---|
| $$ | 插入一个 "$"。 |
| $& | 插入匹配的子串。 |
| $` | 插入当前匹配的子串左边的内容。 |
| $' | 插入当前匹配的子串右边的内容。 |
| $n | 假如第一个参数是 RegExp对象,并且 n 是个小于100的非负整数,那么插入第 n 个括号匹配的字符串。提示:索引是从1开始。如果不存在第 n个分组,那么将会把匹配到到内容替换为字面量。比如不存在第3个分组,就会用“$3”替换匹配到的内容。 |
| $<Name> | 这里*Name* 是一个分组名称。如果在正则表达式中并不存在分组(或者没有匹配),这个变量将被处理为空字符串。只有在支持命名分组捕获的浏览器中才能使用。 |
我们简单应用下:
// 1. 加密手机
"17398888669".replace(/(\d{3})(\d{4})(\d{4})/, "$1 *** $3")
// -> '173 *** 8669'
// 2. 交换两个单词
var re = /(\w+)\s(\w+)/;
var str = "John Smith";
var newstr = str.replace(re, "$2, $1");
// Smith, John
console.log(newstr);
八、编码
1. Base64转码
Base64是一种编码方法,可以将任意字符转成可打印字符。这种编码方法,一开始的作用是为了不显示特殊字符,简化程序的处理。但因为这种编码方式比较复杂,现在使用JavaScript的开发者都少有涉及这种编码方式,所以更多时候都将其作为一种加密手段在使用。该编码提供两个方法来转换字符:
-
btoa()
将字符串或二进制值转为Base64编码
-
atob()
将Base64编码转为原来的编码
我们先来看一个将普通字符串转换为Base64格式编码,再将该格式转换为普通字符串在控制台中输出的例子:
可以从示例中的前两个结果很直观的发现,我们首先是用btoa()将一串普通的字符串“Hello, world!”转换成为了Base64码“YXVsZW5jZQ==”,然后我们将得出的Base64码通过atob()方法将得到的Base64码再次成功地转换成为了普通的字符串“Hello, world!”。示例中的后两个结果我们是对二进制数“0b1001”进行了相同方式的转换,最后得到字符串“9”(若用parseInt(0b1001),得出的结果为数字9,而不是字符串“9”)。可见这两种方法是可以将字符串和Base64码在两种格式之间互相转换的,但是要注意的是,通过atob()这种方法是不能还原出数值型的值的,这为我们拼接密码字符串提供了便利(因为“+”运算符只能做拼接运算了)。但在模型情况下需要得出的结果是一个数值型的值,那就得用“数值的转换”提供的三种方法来进行转换了。
但是在使用这两个方法的时候需要注意的是,这两个方法不支持对非ASC II字符转换为Base64码,否则在浏览器中会报错。如果要对非ASC II的字符进行Base64转码,需要用到两个对URI组件编码的函数:“encodeURIComponent()”函数和“decodeURIComponent()”函数,前者能将字符串作为 URI 组件进行编码,后者能将encodeURIComponent() 函数编码的 URI 进行解码。如下所示:
2. encodeURI && decodeURI
- encodeURI:编码,不可以编码特殊字符(
#、/、&等) - decodeURI:解码,不可以解码特殊字符(
#、/、&等)
3. encodeURIComponent && decodeURIComponent
-
encodeURIComponent:编码,可以编码特殊字符(
#、/、&等) -
decodeURIComponent:解码,可以解码特殊字符(
#、/、&等)
九、课后练习
1. 将字符串 “hello” 逆序输出为 “olleh”
2. 删除字符串 “01i13P47h2o39n32e09” 中的所有数字,输出结果
3. 定义一个字符串 ”CHINESE“,将其输出为 “Chinese“
4. 统计 ”warriors” 单词中,“r“字母出现的次数
5. 将字符串 ”-_-” 中的 “_“ 替换成 ”$”
6. 将字符串”border-bottom-color” 转换成驼峰命名”borderBottomColor”
7. 输入身份证号,点击按钮,显示出生年月,输出格式为:"出生年月:xxxx年xx月xx日"
> 提示:
- 获取元素:document.querySelector(CSS选择器);
- 点击事件:el.onclick = function(){};
- 获取输入框的值:el.value;
- 设置元素显示内容:el.textContent = "";