es6学习笔记-字符串的扩展_v1.0

字符的Unicode表示法

JavaScript 允许使用uxxxx的形式表示一个字符,但在 ES6 之前,单个码点仅支持u0000到uFFFF,超出该范围的必须用双字节形式表示,否则会解析错误。ES6对这一点做出了改进,只要将码点放入大括号,就能正确解读该字符,不受限于4位。例如下面的写法就是合法的,能够被正确解析:

//在es6后才可以,在es5的时候不行
"\u{20BB7}"  // "?"

codePointAt()

  • JavaScript内部,字符以UTF-16的格式储存,每个字符固定为2个字节。

  • 对于那些需要4个字节储存的字符(Unicode码点大于0xFFFF的字符),JavaScript会认为它们是两个字符。

  • codePointAt方法,能够正确处理4个字节储存的字符,返回一个字符的码点。

  • codePointAt方法会正确返回32位的UTF-16字符的码点。

注意例子是?,汉字“?”(注意,这个字不是”吉祥“的”吉“)的码点是0x20BB7,UTF-16编码为0xD842 0xDFB7(十进制为55362 57271),需要4个字节储存。

var s = '?a';

console.log(s.length); //3,?使用了2个字节来存储,a使用了一个字节存储
console.log(s.codePointAt(0)); // 134071
console.log(s.codePointAt(1)); // 57271
console.log(s.codePointAt(2)); // 97

codePointAt方法是测试一个字符由两个字节还是由四个字节组成的最简单方法。


function is32Bit(c) {
    //超过2个字节组成的字符串的第一位码点都是大于0xFFFF
    return c.codePointAt(0) > 0xFFFF;
}

console.log(is32Bit("?")) // true
console.log(is32Bit("a")) // false

ES6 提供了 codePointAt(),能够正确处理4个字节存储的字符,返回一个字符的码点。

但需要注意的是,这并没有改变 JavaScript 将2个字节视为1个字符的事实,只是自动识别出了这是个4字节的字符并返回了正确的码点而已

对于单个4字节的字符(例如'?')来说,charPointAt(0)返回完整字符的十进制码点,charPointAt(1)返回这个字符的后2个字节的十进制码点,效果等同于charCodeAt(1)。
参考

var s = '?a';
for (let ch of s) { //遍历s字符串,会有2个字符串被遍历,因为他的长度是2
  console.log(ch.codePointAt(0));
}
// 134071,能知道这是一个超过四位(2个字节)的字符串(?)
// 97,这是一个只需要2位(1个字节)的字符串(a)
  1. 编码格式的检测:

    专家给每种格式和字节序规定了一些特殊的编码,这些编码在unicode 中是没有使用的,所以不用担心会冲突。这个叫做BOM(Byte Order Mark)头。意思是字节序标志头。通过它基本能确定编码格式和字节序。
    
    UTF编码 ║ Byte Order Mark   
    UTF-8   ║ EF BB BF   
    UTF-16LE ║ FF FE   
    UTF-16BE ║ FE FF   
    UTF-32LE ║ FF FE 00 00   
    UTF-32BE ║ 00 00 FE FF
    
    所以通过检测文件前面的BOM头,基本能确定编码格式和字节序。但是这个BOM头只是建议添加,不是强制的,所以不少软件和系统没有添加这个BOM头(所以有些软件格式中有带BOM头和NoBOM头的选择)
    
  2. UTF-16长度相对固定,只要不处理大于U200000范围的字符,每个Unicode代码点使用16位即2字节表示,超出部分使用两个UTF-16即4字节表示。按照高低位字节顺序,又分为UTF-16BE/UTF-16LE。参考

String.fromCodePoint()

String.fromCodePoint()就是和codePointAt()做相反的操作了。例如:

console.log(String.fromCodePoint(134071));  // "?"

字符串的遍历器接口

这个遍历器最大的优点是可以识别大于0xFFFF的码点

for (let codePoint of 'foo') {
  console.log(codePoint)
}
// "f"
// "o"
// "o"

//能识别大于0xFFFF的码点
var text = String.fromCodePoint(0x20BB7);

//用for不行
for (let i = 0; i < text.length; i++) {
  console.log(text[i]);
}
// " "
// " "

//用遍历器可以
for (let i of text) {
  console.log(i);
}
// "?"

includes(), startsWith(), endsWith()

传统上,JavaScript只有indexOf方法,可以用来确定一个字符串是否包含在另一个字符串中。ES6又提供了三种新方法。

  • includes():返回布尔值,表示是否找到了参数字符串。

  • startsWith():返回布尔值,表示参数字符串是否在源字符串的头部。

  • endsWith():返回布尔值,表示参数字符串是否在源字符串的尾部。

var s = 'Hello world!';
//第二个参数代表开始位置
console.log(s.startsWith('world', 6))// true
console.log(s.endsWith('Hello', 5)) // true
console.log(s.includes('Hello', 6)) // false

repeat()

repeat方法返回一个新字符串,表示将原字符串重复n次。

console.log('x'.repeat(3)) // "xxx"
console.log('na'.repeat(0)) // ""
console.log('na'.repeat(2.9)) // "nana"
'na'.repeat(Infinity)// RangeError
'na'.repeat(-1)// RangeError

padStart(),padEnd()

ES2017(es6是es2016) 引入了字符串补全长度的功能。如果某个字符串不够指定长度,会在头部或尾部补全。padStart()用于头部补全,padEnd()用于尾部补全。

padStart和padEnd一共接受两个参数,第一个参数用来指定字符串的最小长度,第二个参数是用来补全的字符串。

//主要用于补全
console.log('1'.padStart(10, '0')); // "0000000001"
console.log('12'.padStart(10, '0'))// "0000000012"
console.log('123456'.padStart(10, '0')) // "0000123456"

node 6.95还不支持,现在是用babel-node来测试的

模板字符串

  • 使用反引号,反引号代替以前的单引号或者双引号

  • 使用大括号支持任意的JavaScript表达式,可以进行运算,以及引用对象属性和函数

// 普通字符串
`In JavaScript '\n' is a line-feed.`

// 多行字符串
`In JavaScript this is
 not legal.`

console.log(`string text line 1
string text line 2`);

// 字符串中嵌入变量
var name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`   

//如果在模板字符串中需要使用反引号,则前面要用反斜杠转义。
var greeting = `\`Yo\` World!`;


//使用大括号的情况
var x = 1;
var y = 2;

`${x} + ${y} = ${x + y}`
// "1 + 2 = 3"

`${x} + ${y * 2} = ${x + y * 2}`
// "1 + 4 = 5"

var obj = {x: 1, y: 2};
`${obj.x + obj.y}`
// 3

//模板字符串之中还能调用函数,要用大括号
function fn() {
  return "Hello World";
}

`foo ${fn()} bar`
// foo Hello World bar

传统写法vs新写法

//传统写法
$('#result').append(
  'There are <b>' + basket.count + '</b> ' + //不断用+号连接
  'items in your basket, ' +
  '<em>' + basket.onSale +
  '</em> are on sale!'
);
//新写法
$('#result').append(` //一个反引号括起来,然后直接写,用大括号包括变量
  There are <b>${basket.count}</b> items
   in your basket, <em>${basket.onSale}</em>
  are on sale!
`);

//模板字符串中嵌入变量,需要将变量名写在${}之中。
function authorize(user, action) {
  if (!user.hasPrivilege(action)) {
    throw new Error(
      // 传统写法为
      // 'User '
      // + user.name
      // + ' is not authorized to do '
      // + action
      // + '.'
      
      //新写法
      `User ${user.name} is not authorized to do ${action}.`);
  }
}

模板嵌套

  • map() 方法创建一个新数组,其结果是该数组中的每个元素调用一个提供的函数。

const tmpl = addrs => ` //箭头后面跟的是反引号
  <table>
  ${addrs.map(addr => ` //不断解析,直到获取到data数组的元素里面的对象属性
    <tr><td>${addr.first}</td></tr>
    <tr><td>${addr.last}</td></tr>
  `).join('')}//将他们直接连接起来
  </table>
`;

//初始化需要处理的模板
const data = [
    { first: '<Jane>', last: 'Bond' },
    { first: 'Lars', last: '<Croft>' },
];

console.log(tmpl(data));
//返回结果
// <table>
//
//   <tr><td><Jane></td></tr>
//   <tr><td>Bond</td></tr>
//
//   <tr><td>Lars</td></tr>
//   <tr><td><Croft></td></tr>
//
// </table>

如果需要引用模板字符串本身,在需要时执行,可以像下面这样写。

// 写法一
let str = 'return ' + '`Hello ${name}!`';//将模板本身保存起来为变量
let func = new Function('name', str); //作为functionBody使用
func('Jack') // "Hello Jack!"

// 写法二
let str = '(name) => `Hello ${name}!`'; 
let func = eval.call(null, str); //不建议使用eval
func('Jack') // "Hello Jack!"

new Function ([arg1[, arg2[, ...argN]],] functionBody)

  1. 参数arg1, arg2, ... argN:被函数使用的参数的名称必须是合法命名的。

  2. functionBody:一个含有包括函数定义的JavaScript语句的字符串。

参考引用:

  1. es6字符串扩展

  2. 每天一点ES6(5):字符串的扩展


线上猛如虎
2.2k 声望178 粉丝

你们都有梦想的,是吧.怀抱着梦想并且正朝着梦想努力的人,寻找着梦想的人,我想为这些人加油呐喊!