基本概念
JavaScript 内部,字符以 UTF-16 的格式储存,每个字符固定为2个字节。对于那些需要4个字节储存的字符(Unicode 码点大于0xFFFF的字符),JavaScript 会认为它们是两个字符
字符串的扩展
js可以使用 \uxxxx 的形式定义字符,这种表示法只限于码点在 \u0000~\uFFFF 之间的字符。超出这个范围的字符,必须用两个双字节的形式表示,在ES6可以使用大括号进行包裹就能正常显示
"\uD842\uDFB7" // "𠮷"
"\u{20BB7}" // "𠮷"
...
// 大于0xFFFF的字符会被当作两个字符
let s = '𠮷'; // s.length为2
相关方法
字符转成相关unicode码
- charAt 返回在指定位置的字符,不能正确处理 4 个字节存储的字符
- charCodeAt 返回一个字符的码点,不能正确处理 4 个字节存储的字符
- codePointAt 返回一个字符的码点,能正确处理 4 个字节储存的字符
s.charCodeAt(0).toString("16"); // d842
s.charCodeAt(1).toString("16"); // dfb7, 也就是两个码点分别对应数值
// 使用codePointAt能正确返回对应码点
s.codePointAt(0).toString("16"); // 20bb7
charAt 只能用在 2 个字节存储的字符,针对 4 个字节的字符,可以借助 for...of 方法
// charAt只能用在utf-32的16位指定位置字符
'abc'.charAt(0); // a
'𠮷'. charAt(0); // 32位的形式下,展示异常,标准方法at尚未获取支持
...
let s = '𠮷a';
for (let ch of s) {
console.log(ch.codePointAt(0).toString(16));
}
// 20bb7
// 61
unicode码转为字符
- fromCharCode 从字符编码创建一个字符串,不能识别大于0xFFFF的码点
- fromCodePoint 从字符编码创建一个字符串,能识别大于0xFFFF的码点
// String.fromCharCode只能识别uft-32的16位形式
String.fromCharCode(97); // a
// String.fromCodePoint可以识别utf-32的32位形式
String.fromCodePoint(0x20BB7); // "𠮷"
判断字符中是否包含指定字符
- indexOf 返回某个指定的字符串值在字符串中首次出现的位置
- lastIndexOf 返回某个指定的字符串值在字符串中最后出现的位置
- includes 返回布尔值,表示是否找到了参数字符串。
- startsWith 返回布尔值,表示参数字符串是否在原字符串的头部。
- endsWith 返回布尔值,表示参数字符串是否在原字符串的尾部。
'hello world'.indexOf('o'); // 4
'hello world'.lastIndexOf('o'); // 7
'hello world'.includes('wo'); // true
'hello world'.startsWith('wo'); // false
'hello world'.endsWith('wo'); // false
// includes, startsWith, endsWith还可以接收第二个参数,表示搜索开始位置
'hello world'.startsWith('wo', 6); // true
大小写转换
- toLocaleLowerCase 根据特定语言环境,把字符串转换为小写
- toLocaleUpperCase 根据特定语言环境,把字符串转换为大写
- toLowerCase 把字符串转换为小写
- toUpperCase 把字符串转换为大写
toLocaleLowerCase 与 toLowerCase 不同的是,toLocaleLowerCase 方法按照本地方式把字符串转换为小写。只有几种语言(如土耳其语)具有地方特有的大小写映射,一般情况下,两者转换后的结果一致
将原字符重复
- repeat 方法返回一个新字符串,表示将原字符串重复n次
接收正整数,小数会进行取整运算(可以认为是使用parseInt的方式进行转换),如果为0则转为空,负数则报错,NaN是特殊值等同0,Infinity特殊值报错
'x'.repeat(3) // "xxx"
'x'.repeat(2.1); // 'xx',按2处理
'x'.repeat(0); // ''
'x'.repeat(-0.9); // '',-0.9取值后等同0
'x'.repeat(NaN); // '',和0一致,特殊值
'x'.repeat(-1); // Uncaught RangeError: Invalid count value(无效数值)
'x'.repeat(Infinity); // Uncaught RangeError: Invalid count value(无效数值)
其他数据类型会先转为数字,在按照数字的转换规则进行转换
'x'.repeat(true); // 等同1
'x'.repeat(false); // 等同0
'x'.repeat('na'); // 等同NaN
'x'.repeat('3'); // 'xxx'等同3
字符串补全
- padStart 用于头部补全
- padEnd 用于尾部补全
'x'.padStart(5, 'ab') // 'ababx'
'x'.padEnd(5, 'ab') // 'xabab'
padStart、padEnd接收两个参数,第一参数是补全后字符的长度,第二个参数是用来补全的字符,规则是长度不足,补全字符按顺序补全,按情况截取
'x'.padStart(5, 'abc'); // "abcax", 第二轮只补全了第一个字符a
'x'.padStart(5, 'abcde'); // "abcdx", 补全后的字符为5,超出的进行截取
如果补全后的字符长度不大于原字符长度,则不进行补全
'xxxxx'.padStart(5, 'abc'); // 'xxxxx'
// 如果省略补全字符,则用空格进行补全
'x'.padStart(4); // ' x'
字符串操作
- match 在字符串内检索指定的值,或找到一个或多个正则表达式的匹配
// 如果检索的是字符,返回的是一个匹配对象
'hello world'.match('world');
如果检索的是正则,并且不添加g 标志,其结果和RegExp.exec() 一致
groups: 一个捕获组数组 或 undefined(如果没有定义命名捕获组)。
index: 匹配的结果的开始位置
input: 搜索的字符串.
'Hello world'.match(/world/);
//
["world", index: 6, input: "Hello world", groups: undefined]
0: "world"
groups: undefined
index: 6
input: "hello world"
length: 1
如果使用了g 标志,则返回匹配的数组
"1 plus 2 equal 3".match(/\d+/g);
// ["1", "2", "3"]
- matchAll 返回匹配正则表达式的所有结果的迭代器,另外还能获取分组捕获
返回的迭代器可以使用...(扩展语法),for...of 或 Array.from进行遍历
let regexp = /t(e)(st(\d?))/g;
let str = 'test1test2';
str.match(regexp); // ["test1", "test2"]
let arr = [...str.matchAll(regexp)];
Array.from(arr[0]); // ["test1", "e", "st1", "1"]
Array.from(arr[1]); // ["test2", "e", "st2", "2"]
- replace 用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串
'hello'.replace('e', 'd');
// hdllo
...
'hello'.replace(/l/g, 'd');
// heddo
- search 用于检索字符串中指定的子字符串,或检索与正则表达式相匹配的子字符串,返回第一个与 regexp 相匹配的子串的起始位置,如果没有匹配则返回 -1
'hello'.search('l');
// 2
'hello'.search(/l/);
// 2
- slice 提取字符串的片断,并在新的字符串中返回被提取的部分
'hello'.slice(0,3);
// hel
- split 把字符串分割为字符串数组
'hello hello hello'.split('ll');
// ["he", "o he", "o he", "o"]
...
// 这个方法可以接收第二个参数,用来限定输出数组的最大限度
'hello hello hello'.split('ll', 2);
// ["he", "o he"]
- substring 提取字符串中两个指定的索引号之间的字符
'hello'.substring(0,3);
// hel
如果传递的值都是正整数,slice 可以认为和 substring 没有区别,但是substring 不接受负数参数,如果是负数,其实是把负数当作0
'hello'.slice(-2, -1);
// l
...
'hello'.substring(-2, -1);
// ''
...
'hello'.substring(-2, 1);
// h
- toString 返回指定对象的字符串形式
let x = new String("Hello world");
console.log(x.toString());
// Hello world
- localeCompare 用本地特定顺序来比较两个字符串,返回值是数值,-1表示小于目标字符,1表示大于目标字符,0表示相等,使用这个方法可以对汉语进行排序
'中'.localeCompare('国');
// 1 因为中的开头音节是z,国的开头音节是g(汉语拼音)
- trim 方法会从一个字符串的两端删除空白字符
- trimRight 方法从一个字符串的右端移除空白字符
- trimStart 方法从字符串的开头删除空格(trimLeft是此方法的别名)
' foo '.trim();
// "foo"
' foo '.trimRight();
// " foo"
' foo '.trimLeft()
// "foo "
' foo '.trimStart()
// "foo "
模板字符串
模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量
- 用反引号(`)标识来设置字符串和变量连接
const name = 'rede';
`hello ${name}`; // hello rede
- 大括号内可以是任意js表达式
const x = 1;
const y = 2;
`x + y = ${x+y}`; // "x + y = 3"
- 如果大括号内是字符则原样输出
`Hello ${'world'}`;
// Hello world
- 如果模板字符串跟在函数后面,就会变成标签模板
function printMsg (msg) {
console.log(msg);
}
const name = 'rede';
printMsg`hello ${name}`;
// 此时控制台会输出["hello ", "", raw: Array(2)],对printMsg做改造
function printMsg (msg, name) {
console.log(msg);
console.log(name);
}
// 此时控制台输出["hello ", "", raw: Array(2)]和rede
原因是这种标签模板其实是一种特殊的函数调用,进行参数传参时,分两部分进行传参,第一个部分是一个数组,数组以变量为界限,比如例子中的hello ${name},所以相应的数组为["hello ", ""],第二个空字符表示变量后面数据,下面可以验证这个说法
printMsg`hello ${name} `; // 结尾比上面多个空格
// 此时控制台输出["hello ", " ", raw: Array(2)]
标签模板 的一个重要应用,就是过滤 HTML 字符串,防止用户输入恶意内容。
let message =
SaferHTML`<p>${sender} has sent you a message.</p>`;
function SaferHTML(templateData) {
let s = templateData[0];
for (let i = 1; i < arguments.length; i++) {
let arg = String(arguments[i]);
// Escape special characters in the substitution.
s += arg.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">");
// Don't escape special characters in the template.
s += templateData[i];
}
return s;
}
标签模板 的另一个应用,就是多语言转换(国际化处理)。
i18n`Welcome to ${siteName}, you are visitor number ${visitorNumber}!`
// "欢迎访问xxx,您是第xxxx位访问者!"
String.raw提供了反向返回模板字符串的原始字符串形式,会将所有变量进行替换
第一个参数应该是一个具有raw 属性的对象,并且raw 属性应该是一个数组,如果要使用这个方法,建议按数组形式,写出原始字符串模版,这样方便查看
String.raw({raw: ['hello ', ' ']}, 'rede'); // "hello rede "