String字符串
- 字符串作为JS的原始值,对应也有包装类型String,这里重点介绍String对象的属性、方法。
- JS中的字符用16位码元存储,即1个字符占2个字节,可见,字符串的length属性表示所占码元的个数。
字符编码问题:
- JS实际采用 UTF-16 UCS-2 混合编码,范围在U+0000 ~ U+FFFF的字符,两种编码一样,lenth charAt() charCodeAt() fromCharCode()都是基于UTF-16实现的。
- UCS-2,固定宽度编码,2字节通用字符集
- UTF-16,可变宽度编码,即每个字符最少用2字节、最多用4字节 空间存储。
诞生背景:UCS-2中2字节仅可表示 65536 个字符,不能满足需求。
推荐外文:difference-between-ucs-2-and-utf-16
1. 创建字符串
两种方式创建:字符串字面量、String构造函数
// 字面量
let str1 = "JS";
// 构造函数
let str2 = new String("JS");
// 作为对象,还可添加属性
str1.name = "ES";
// 字符串对象的内部结构
> console.dir(str2);
0: "J" // 可见,内部结构和Array类似
1: "S"
length: 2 // 内部自动维护length属性
__proto__:
String[[PrimitiveValue]]: "JS"
2. 实例属性
- String对象内部会自动维护的lenth属性表示字符串长度
- 对字符串对象内元素的增删操作都会更新length属性值
> console.dir(str2);
0: "J" // 可见,内部结构和Array类似
1: "S"
length: 2 // 内部自动维护length属性,基于 UTF-16
__proto__:
String[[PrimitiveValue]]: "JS"
3. 原型方法
字符串对象通过在原型上定义方法来实现方法的共享,字符串原始值在调用时会自动转为包装对象再调用,下面是所有的方法:
- 字符串位置方法:
indexOf: ƒ indexOf()
lastIndexOf: ƒ lastIndexOf()
// 返回第一个目标字符的索引
> let a = "hello";
> a.indexOf('l'); // 2
> a.indexOf("a"); // -1 未找到则返回-1
> a.lastIndexOf("l"); // 3 不同的只是搜索方向为从后往前
- 获取字符方法:
charAt: ƒ charAt()
> let A1 = "hello";
> A1.charAt(4); // 'o' 等效于 A[4],基于UTF-16
- 字符串连接方法:
concat: ƒ concat()
> "ab".concat("cd"); // 'abcd' str.concat(str2, [, ...strN])
> "ab".concat(['c','d']); // 'abc,d' 非字符串则先转为字符串
> "ab".concat(['c','d'].join("")); // 和上面等效
- 空格去除方法:
trim: ƒ trim()
trimRight: ƒ trimEnd()
trimStart: ƒ trimStart()
trimEnd: ƒ trimLeft() // 这两个是上面方法的别名
trimLeft: ƒ trimRight()
> let b = " hello ";
> b.trim(); // 'hello' 去除首尾空格
> b.trimStart(); // 'hello ' 去除开始空格
> b.trimEnd(); // ' hello' 去除末尾空格
- 字符串包含方法:
startsWith: ƒ startsWith()
endsWith: ƒ endsWith()
includes: ƒ includes()
> let c = 'hello world';
> c.startsWith("hello"); // true 是否指定字串开始
> c.endsWith("world"); // true 是否指定字串结尾
> c.includes("lo w"); // true 是否存在指定字串
> c.inculdes("low"); // false
- 字符串重复方法:
repeat: ƒ repeat()
> let d = "+*";
> d.repeat(3); // '+*+*+*' 返回重复拼接的新字符串
- 填补方法:
padEnd: ƒ padEnd()
padStart: ƒ padStart()
> let e = '...';
> e.padStart(5,"h"); // 'hh...' 首部填补
> e.padEnd(15,"bala"); // '...balabalabala' 尾部填补
- 大小写转换方法:
toLowerCase: ƒ toLowerCase()
toUpperCase: ƒ toUpperCase()
toLocaleLowerCase: ƒ toLocaleLowerCase()
toLocaleUpperCase: ƒ toLocaleUpperCase()
> let f = "Hello";
> f.toLowerCase(); // 'hello'
> f.toUpperCase(); // 'HELLO'
> f.toLocaleLowerCase(); // 'hello' 部分地区的语言大小写比较特殊
> f.toLocaleUpperCase(); // 'HELLO'
- 获取码元编码方法:
charCodeAt: ƒ charCodeAt()
codePointAt: ƒ codePointAt()- 拓展:
fromCharCode()方法可由给定的UTF-16码元创建字符串
> let g='hello';
> let h = String.fromCharCode(55357,56842); // "😊"
> g.charAt(2); // 'l'
> g.charCodeAt(2); // 108 自动转为十进制输出 基于UTF-16
> h.codePointAt(0); // 128522 码点,charCodeAt方法升级版,可识别码点
> 108 === 0x6c; // true
// 拓展:
> String.fromCharCode(0x68,0x69); // 'hi' 可接受任意个参数 基于UTF-16
> String.fromCodePoint(128522); // "😊" 可识别码点(字符的完整标识)
- 模式匹配方法:
match: ƒ match()
matchAll: ƒ matchAll()
search: ƒ search()
replace: ƒ replace()
replaceAll: ƒ replaceAll()
> let pattern = /0[xo]\d*/gi;
> let i = "0x11 0o11";
// match(regexp)方法
> i.match(pattern); // ["0x11", "0o11"] 等效于 pattern.match(i);
> i.match(new RegExp("0[xo]\\d*","gi")); // 也可以接收正则对象
// matchAll(regexp)方法
i.match(/o[xo]\d*/i); // 报错,必须全局匹配,返回一个迭代器
// search()方法
> i.search(pattern); // 0 返回第一次匹配成功的位置索引,匹配失败则返回-1
> i.search(new RegExp("0[xo]\\d*","gi")); // 0 也可以接收正则对象
// replace(regexp|substr, newSubStr|function):子串替换
> let str = "foot tooth";
> str.replace("oo","ee"); // 'feet tooth' 只替换一次
> str.replace(/o{2}/g,"ee"); // "feet teeth"
> str.replace(new RegExp("o{2}","g"),"ee"); // 和上面等效
// replaceAll(regexp|substr, newSubstr|function)
> str.replaceAll(/o{2}/,"ee"); // 必须设置全局 replaceAll called with a non-global RegExp argument
- 本地化字符串比较
localeCompare: ƒ localeCompare()
// 按字母表顺序排序
> "abc".localeCompare("abC"); // -1 字符串排在参数字串前
> "abc".localeCompare("abc"); // 0 字符串相等
> "aB".localeCompare("ab"); // 1 字符串排在参数字串后
- 获取子串方法:
substring: ƒ substring()
substr: ƒ substr() 废弃
slice: ƒ slice()
// substring(indexStart[, indexEnd])
> "hello".substring(1); // 'ello' 省略则直到末尾
> "hello".substring(1,2); // 'e' [1,2)
> "hello".substring(-1,2); // 'he' 任一参数<0则当作0
> "hello".substring(100,100); // '' 任一参数>length则当作length
> "hello".substring(2,0); // 'he' indexStart>indexEnd则自动调换
// substr(start[, length]) 废弃了
> "hello".substr(2); // 'llo' 默认直到末尾
> "hello".substr(-4,2); // 'el' start为负时,start+=length
> "hello".substr(-8,2); // 'he' start<-length时,start置为0
> "hello".substr(2,0); // '' length<=0时,返回空串
> "hello".substr(2,-1); // ''
// str.slice(beginIndex[, endIndex])
> "hello".slice(2,-1); // 'll' 和substring()类似,但是可解析负值索引
> "hello".slice(2,0); // '' 但beginIndex>endIndex时,不会自动调换
- 字符串分割方法:
split: ƒ split()
// split([separator[, limit]])
> str = "I; am ; you ;!" // 'I; am ; you ;!'
> str.split(";"); // [ 'I', ' am ', ' you ', '!' ] 默认无限分割
> str.split(";",2); // [ 'I', ' am ' ]
> let regexp = /\s*(?:;|$)\s*/; // 可解析正则
> str.split(regexp); // [ 'I', 'am', 'you', '!' ]
迭代器方法:
Symbol(Symbol.iterator): ƒ [Symbol.iterator]()
> let iter = "hello"[Symbol.iterator](); // 返回一个迭代器对象
> for(v of iter) { console.log(v); }
h
e
l
l
o
- 编码规范化方法:
normalize: ƒ normalize()
由于同一字符可有多种表示方法,如BMP、代理对…
Unicode提供4种规范化形式:NFD、NFC、NFKD、NFKC
// normalize() 规范化为指定形式
> let char = String.fromCharCode(0x00C5); // 'Å'
> char == char.normalize("NFD"); // false
> char == char.normalize("NFC"); // true 同种规范化形式才能正确比较
> char == char.normalize("NFKC"); // true
> char == char.normalize("NFKD"); // false
- 继承的方法:返回原始字符串值
toString: ƒ toString()
valueOf: ƒ valueOf()
> "hello".toString(); // 'hello'
> "hello".valueOf(); // 'hello'
- HTML相关方法:辅助生成HTML标签,了解即可,几乎不使用
anchor: ƒ anchor()
big: ƒ big()
blink: ƒ blink()
bold: ƒ bold()
fixed: ƒ fixed()
fontcolor: ƒ fontcolor()
fontsize: ƒ fontsize()
italics: ƒ italics()
link: ƒ link()
small: ƒ small()
strike: ƒ strike()
sub: ƒ sub()
sup: ƒ sup()