JavaScript简明教程-字符串

154 阅读7分钟

基本概念

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...ofArray.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, "&amp;")
            .replace(/</g, "&lt;")
            .replace(/>/g, "&gt;");

    // 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 "