es6-string

Posted by XuBaoshi on June 20, 2017

ECMAScript.6 String相关

Unicode

{}

JavaScript字符以UTF-16的格式储存,允许使用\uxxxx表示一个字符,其中xxxx表示字符的Unicode码点。这个表示法只限于 “\u0000~\uFFFF”之间的字符。

console.log('\u0061'); // "a"
// "𠮷"
console.log('\u20bb7'); // "₻7"
// 上述代码JavaScript会将其理解成 \u20bb+7
// 替代方案
console.log('\ud842\udfb7'); // "𠮷" 

ES6中 将码点放入大括号内,加强了对 Unicode 的支持。

console.log('\u{20bb7}'); //"𠮷"
console.log('\u{d842}\u{dfb7}'); //"𠮷"
console.log('\u{20bb7}' === '\u{d842}\u{dfb7}');// true
console.log('\u{7a}' === '\u007a'); //true

codePointAt

JavaScript内部,每个字符固定为2个字节,但是这个表示法只限于 “\u0000~\uFFFF”之间的字符,Unicode码点大于0xFFFF的字符就需要4个字节储存的字符,JavaScript会认为它们是两个字符.。

let text1 = '\u0061';
let text2 = '\ud842\udfb7';

console.log(text1); // "a"
console.log(text1.length); // 1
console.log(text2); //"𠮷"
console.log(text2.length); // 2

es6之前可以使用charAt()和charCodeAt()访问字符串,其中charAt()方法返回给定位置的那个字符。charCodeAt()则返回给定位置的那个字符的字符编码。

let text1 = "a";
console.log(text1.charAt(0)); // "a"
console.log(text1.charCodeAt(0)); // 97
console.log(text1.charCodeAt(0).toString(16)); // "61"

let text2 = "𠮷";
console.log(text2.length); // 2
console.log(text2.charAt(0)); // ""
console.log(text2.charAt(1)); // ""
console.log(text2.charCodeAt(0)); // 55362
console.log(text2.charCodeAt(1)); // 57271
console.log(text2.charCodeAt(0).toString(16)); // "d842"
console.log(text2.charCodeAt(1).toString(16)); // "dfb7"

上述代码中”𠮷”的码点在”20bb7”,超出了”FFFF”,故需要4个字节存储,对于对于这种情况JavaScript会误判字符串长度为2。charAt方法失效。charCodeAt也只能返回前两个及后两个字节的值。

var s = '𠮷a';

console.log(s.length);
console.log(s.codePointAt(0)); // 134071
console.log(s.codePointAt(1)); // 57271
console.log(s.codePointAt(2)); // 97

console.log(s.charCodeAt(0)); // 55362
console.log(s.charCodeAt(1)); // 57271
console.log(s.charCodeAt(2)); // 97

上述代码中可以看出JavaScript依旧视s为三个字符,但使用codePointAt后第一个字符可以识别“𠮷”,第二个及第三个字符返回值与charCodeAt相同。

// 转换为16进制
var s = '𠮷a';
console.log(s.codePointAt(0).toString(16)); // "20bb7"
console.log(s.codePointAt(1).toString(16)); // "dfb7"
console.log(s.codePointAt(2).toString(16)); // "61"

console.log(s.charCodeAt(0).toString(16)); // "d842"
console.log(s.charCodeAt(1).toString(16)); // "dfb7"
console.log(s.charCodeAt(2).toString(16)); // "61"

console.log('\u{20bb7}' === '\ud842\udfb7'); //true

String.fromCodePoint()

ES6提供了String.fromCodePoint方法,可以识别大于0xFFFF的字符

var txt = "𠮷";
console.log(String.fromCodePoint(txt.codePointAt(0))); // "𠮷"

normalize()

许多欧洲语言有语调符号和重音符号。Unicode提供两种方式表达,一种是直接提供带重音符号的字符,比如Ǒ(\u01D1)。另一种是两个字符合成一个字符,比如Ǒ(\u004F\u030C)。他们在语义和视觉上是等价的,但JavaScript不能识别。

console.log('\u01D1'==='\u004F\u030C') //false
console.log('\u01D1'.length);
// 1
console.log('\u004F\u030C'.length);// 2

ES6提供字符串实例的normalize()方法 normalize函数的参数有四个可选值:’NFC(默认参数)’(标准等价合成)、’NFD’(标准等价分解)、’NFKC’(兼容等价合成)、’NFKD’(兼容等价分解)

console.log('\u01D1'.normalize()==='\u004F\u030C'.normalize())

u修饰符

ES6 对正则表达式添加了u修饰符,可以正确处理四个字节的 UTF-16 编码。

let text = '\ud842\udfb7';  //"𠮷"	
let reg1 = /^\ud842/;
let reg1u = /^\ud842/u;
// wrong
console.log(reg1.test(text)); // true
// right
console.log(reg1u.test(text)); // false


// 同理希望匹配 text为换行符以外的任意单个字符 
let text = '\ud842\udfb7';  //"𠮷"
let reg2 = /^.$/;
let reg2u = /^.$/u;
// wrong
console.log(reg2.test(text));
// right
console.log(reg2u.test(text));


// 在正则表达式中使用大括号表示Unicode 字符时,这种表示法必须加上u修饰符,否则会当做量词处理。

/\u{20bb7}/u.test('𠮷') // true
/\u{20bb7}/.test('𠮷') // false

// 匹配码点大于0xFFFF的 Unicode 字符还需注意
// 使用量词匹配
console.log(/a{2}/.test('aa') ); // true
console.log(/a{2}/u.test('aa'));  // true
console.log(/𠮷{2}/.test('𠮷𠮷') ); // false
console.log(/𠮷{2}/u.test('𠮷𠮷'));  // true

// 使用预定义字符匹配
// 匹配非空字符串
console.log(/^\S$/.test('𠮷')); //false
console.log(/^\S$/u.test('𠮷')); //true

计算字符长度

function codePointLength(text){
	let result = text.match(/[\s\S]/gu);
	return result ? result.length : 0;
}
console.log(codePointLength("abc")); // 3
console.log("𠮷bc".length); // 4
console.log(codePointLength("𠮷bc")); // 3

y

y叫做‘粘连’(sticky)修饰符,y修饰符与g类似都是全局匹配,不同之处在于g修饰符只要剩余位置中存在匹配就可以,但y则必须确保匹配从剩余位置的第一个位置开始。

var s = 'aaa_aa_a';
var r1  = /a+/g;
var r2 = /a+/y;

console.log(r1.exec(s));  //["aaa"]
console.log(r2.exec(s));  //["aaa"]

console.log(r1.exec(s));  //["aa"]
console.log(r2.exec(s));  //null

var r = /a+_/y;
console.log(r.exec(s));   //["aaa_"]
console.log(r.exec(s));   //["aa_"]
console.log(r.exec(s));   //null

下面的代码lastIndex指的是每次搜索的开始位置,g修饰符从这个位置开始向后搜索直至发现匹配为止,y同时遵循lastIndex属性但必须要求在lastIndex指定的位置发现匹配。

// g lastIndex 
const REGEX = /a/g;
console.log(REGEX.lastIndex);  //0
REGEX.lastIndex = 2;
const match = REGEX.exec('xaya');  
console.log(match);  //["a"]
console.log(match.index);//3
console.log(REGEX.lastIndex);//4
console.log(REGEX.exec('xaya'));//null


// y lastIndex
const REGEX = /a/y;

// wrong
REGEX.lastIndex = 2;
const match = REGEX.exec('xaya');  
console.log(match); // null 从y开始

// right
REGEX.lastIndex = 3;
const match = REGEX.exec('xaya'); //["a"]
console.log(match.index);//3
console.log(REGEX.lastIndex);//4
console.log(REGEX.exec('xaya'));//null


// sticky属性
var r = /hello\d/y;
console.log(r.sticky); //true
let re = /ab/g
console.log(re.flags)  // 'g'
console.log(re.source) // 'ab'

template literals(模板字符串)

反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。

基础语法

let message = `Hello world!`;
console.log(message);  //"Hello world!"
console.log(typeof message);//"string"
console.log(message.length);//12

let number = `1`;
console.log(typeof number); //"string"

// 如果想添加“`”,添加转译字符
let message = `\`Hello\` world!`;
console.log(message);  //"`Hello` world!"
console.log(typeof message);//"string"
console.log(message.length);//14

多行文本

// 使用 "\" 将js 代码折行
var message = "Multiline \
string";
console.log(message); // "Multiline string"

// \n
var message = "Multiline \n\
message";
console.log(message); //"Multiline 
                      // message"

// join
var message = ['Multiline','string'].join('\n');
console.log(message)   //"Multiline 
                       // message"
// +
var message = 'Multiline \n' + 'string';
console.log(message);  //"Multiline 
                       // message"

// `
let message = `Multiline
string`;
console.log(message);

// 使用trim方法使首行末行缩进
let html = `
<div>
  <h1>title<h1>
</div>

`;
console.log(html);
// "
// <div>
//   <h1>title<h1>
// </div>

// "
console.log(html.trim());
"<div>
  <h1>title<h1>
</div>"

嵌入变量

// 使用 ${}嵌入变量
let name = "Nicholas";
let message = `Hello,${name}`;
console.log(message);  //"Hello,Nicholas"

// 计算
let count = 10,
price = 0.25,
message = `${count} items cost ${(count*price).toFixed(2)}`;
console.log(message); // "10 items cost 2.50"

// dom
 var data = [{
	id: '201706',
	name: '二零一七年六月'
}, {
	id: '201707',
	name: '二零一七年七月'
}];
var $body = $('body');
var template = '',result = '';
data.forEach(function(obj){
	template +=  `<li>${obj.name}</li>`
})
result = `<ul>${template}</ul>`;
$body.append($(result));

标签模板

Template Strings(模板字符串)是以整体为单位进行即时计算,对模板字符串的操控能力有限,但Tagged Template Strings则可以增强操控能力。

var x = 1, y = 2;
var passthru = function (literals,...substitutions){
  let result = '';
  console.log(literals);   //["hello", ":", ""]
  console.log(substitutions);//[1, 3]
  substitutions.forEach(function(val,i){
      result+=literals[i] + 'tagged';
      result+=substitutions[i];
  })
  result+=literals[literals.length -1];
  return result
};

console.log(`hello${x}:${y+1}`);// "hello1:3"
console.log(passthru`hello${x}:${y+1}`);//"hellotagged1:tagged3"

includes(),startsWith(),endsWith()

在es6之前通常使用indexOf确定一个字符串是否在另一个字符串中。es6新增includes(),startsWith(),endsWith()三个方法,这三个方法有两个参数,第一个为参数字符串,第二个为从哪个位置开始搜索(除了endsWith)。

  1. includes:表示在源字符串中是否找到了参数字符串
  2. startsWith:表示在源字符串头部是否找到了参数字符串
  3. endsWith:表示在源字符串尾部是否找到了参数字符串,但第二个参数指的是针对前n个字符开始查找。
var s = 'Hello World!';
console.log(s.startsWith('Hello')); //true
console.log(s.endsWith('!')); //true
console.log(s.includes('W')); // true

console.log(s.startsWith('World',6)); // true
console.log(s.startsWith('Hello',5)); // true
console.log(s.includes('H',1)); // false

repeat()

该方法接受一个参数n重复源字符串n次。

// 单个字符
console.log('x'.repeat(2));  // "xx"
// 字符串
console.log('world'.repeat(3)); // "worldworldworld"
// 参数为0 结果为空字符串
console.log('hello'.repeat(0)); // ""

// 负数 -1 > n
console.log('hello'.repeat(-2)); // throw error RangeError
// 如果 -1 < n < 0  结果为空字符串
console.log('hello'.repeat(-0.2)); // ""
// Infinity
console.log('hello'.repeat(Infinity));// throw error RangeError

//  NaN
console.log('hello'.repeat(NaN)); // ""

// 如果是字符串,会将其转换成数字然后执行
console.log('hello'.repeat('qq')); // ""
console.log('hello'.repeat('3')); // "hellohellohello"

padStart()、padEnd()

如果某个字符串不够指定长度,会在头部或尾部补全,padStart()用于头部补全,padEnd()用于尾部补全。第一个参数为补全后字符串的长度,但二个参数为使用什么字符串补全,如果第二个参数没有默认为空格。

console.log('x'.padStart(5,'ab'));// 'ababx'
console.log('x'.padStart(4,'ab'));// 'abax'
console.log('x'.padStart(4)); // '   x'
console.log('x'.padEnd(5,'ab'));// 'xabab'
console.log('x'.padEnd(4,'ab'));// 'xaba'
console.log('x'.padEnd(4)); //'x   '