深入理解ES6 002【学习笔记】

字符串和正则表达式

字符串和正则表达式

Javascript字符串一直基于16位字符编码(UTF-16)进行构建。每16位的序列是一个编码单元(code unit),代表一个字符。length、charAt()等字符串属性和方法都基于这个编码单元构造的。
Unicode的目标是为世界上每一个字符提供全球唯一的标识符。如果我们把字符长度限制在16位,码位数量将不足以表示如些多的字符。“全球唯一的标识符”,又称作码位(code point),是从0开始的数值。而表示字符的这些数值或码位,我们称之为字符编码(character encode)。字符编码必须将码位编码为内部一致的编码单元。对于UTF-16来说,码位可以由多种编码单元表示。

更好的Unicode支持,在过去的16位足以包含任何字符(每16位的序列是一个编码单元,代表一个字符,在过去16足以包含任何字符),直到Unicode引入扩展字符集,编码规则才不得不进行变更。

UTF-16

前$2^{16}$个码位均以16位的编码单元表示,这个范围被称作基本多文种平面BMP(Basic Multilingual Plan)。超出这个范围的码位则要归属于某个辅助平面(supplementary plane) ,utf16引入了代理对,其规定用两个16位编码单元表示一个码位。这也就是说,字符串里的字符有两种,一种是由一个编码单元16表示的BMP字符,另一种是由两个编码单元32位表示的辅助平面字符 如 字符:‘𠮷’ (String.fromCodePoint(134071))

ECMAScript 5中,所有字符串的操作都基于16位编码单元。如果采用同样的的方式处理包含代理对的UTF-16编码字符,得到的结果可能与预期不符

let text = "𠮷";
console.log(text.length); //2
console.log(/^.$/.test(text)); //false
console.log(text.charAt(0)); // "" 
console.log(text.charAt(1)); // ""
console.log(text.charCodeAt(0)); // 55362
console.log(text.charCodeAt(1)); // 57271
  • 变量text的长度事实上是1,但它的length属性却是2
  • 变量text被判定为两个字符,因此匹配单一字符的正则表达式失效
  • 前后两个16位的编码单元都不表示任何可打印的字符,因此charAt()方法不会返回合法字符串
  • charCodeAt() 同样不能正确地识别字符。它会返回每个16位编码单元对应的数值

codePointAt()方法

对于BMP字符集中的字符,codePointAt()方法的返回值与chartCodeAt()方法相同,而对于非BMP字符集来说返回值则不同。字符串‘𠮷a’第一个字符是非bmp的,包含两个编码单元,所以它的length属性值为3. ES6完全支持UTF-16codePointAt()方法,这个方法接受编码单元的位置而非字符位置作为参数,返回与字符串中给定位置对应的码位,即一个整数值。

let text = '𠮷a'
console.log(text.length)

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

console.log(text.codePointAt(0)) // 134071
console.log(text.codePointAt(1)) // 57271
console.log(text.codePointAt(2)) // 97

在检测一个字符占用的编码单元,可以写如下的函数两检测

function is32Bite(c){
  return c.codePointAt(0)>0xFFFF;
}

console.log(is32Bite('𠮷')) // true
console.log(is32Bite('a')) // false

fromCodePoint()方法

通过一个字符的码位返回一个字符,可以看成是String.fromCharCode()的扩展版。对于BMP的所有字符两个方法的执行结果相同。只有传递非BMP的码位作为参数时,二者的执行结果才有可能不同。

console.log(String.fromCodePoint(134071)) // 𠮷

normalize()方法

Unicode的另一个有趣之处是,如果我们要对不同字符进行排序或比较操作,会存在一种可能,它们是等效的。代表相同文本的字符可能存在的码位不同。所以做比较时要使用normalize()方法来先标准化一下

只要牢记,在对比字符串之前,一定先把它们标签化为同一种形式。

let normalized  = values.map(funciton(text){
  return text.normalize();
});

normalized.sort(funciton(first,second){
  if(first < second){
    return -1;
  } else if (first === second) {
    return 0;
  } else {
    return 1;
  }
})

正则表达式u修饰符

一个支持Unicode的修饰符u 使它从编码单元操作模式切换成为字符模式,如此一来正则表达式就不会视代理对为两个字符,从而完全按照预期正常运行。如:

let text = '𠮷a'

console.log(text.length)
console.log(/^.$/.test(text)) //false
console.log(/^.$/u.test(text)) //true 使用了u修饰符后,正则表达式会匹配字符,从而就可以匹配日文文字字符

计算码位数量

es6仍然不支持字符串码位数量检测(length仍然返回字符串编码单元的数量),但有了u修饰符后,你就可以通过正则来解决这个问题。

// 长字符串可能会有效率问题,可以使用字符串迭代器来处理
function codePointLength(text){
  let rs = text.match(/[\s\S]/gu);
  return rs?rs.length:0
}
// 判断浏览器是否支持u
function hasRegExU(){
  try{
    var partten = new RegExp(".","u");
     return true
  }catch(ex){
    return false
  }
}

字符串的子串识别

  • trim()
  • includes() 如果在字符串的起始部分检测到指定文本则返回true,否则返回false
  • startWith()如果字符的起始部分检测到指文本则返回true,否则返回false
  • endsWith() 如果在字符串的结束部分检测到指定文本则返回true,否则返回false
  • repeat() 返回当前字符串重复一定次数的新字符串

两个参数 第一个指定要搜索的文本 第二个参数是可选的,指定搜索位置的索引位置,如果你需要在一个字符串中寻找一个子字符串的实际位置,还是需要使用indexOf()lastIndexOf()方法

repeat()方法

ES6还增加了一个repeat(),它接受一个number类型的参数,表示,该字符串的重复次数,返回值是当前字符串重复一定次数后的新字符串。比如在代码格式化工具中创建缩进级别

let indent = " ".repeat(4),
indentLevel = 0;
// 当需要增加缩进时
let newIndent = indent.repeat(++indentLevel)

正则表达式中的y修饰符

它会影响正则表达式搜索过程中sticky属性,当在字符串中开始字符匹配时,它会通知搜索从正则表达的lastIndex属性开始进行。如果在指定位置没有成功匹配,则停止继续匹配。只有调用exec()和test()这些正则表达式表达式对象的方法时才会涉及lastIndex属性

正则表达式的复制

var reg1 = /ab/i,
// es5中抛出异常,es6中正常运行
reg2 = new RegExp(reg1,"g")

let re = /ab/g
console.log(re.source); // "ab"
console.log(re.flags); // "g"

模板字面量

  • 多行字符串 一个正式的多行字符串的概念
  • 基本的字符串格式化 将变量的值嵌入字符串的能力
  • HTML转义 向html插入经过安全置换后的字符串的能力

模板字面量里不需要转义单、双引号,如果要使用反撇号则需要通过\来转义。有变量可以使用${变量名}占位(如果使用一个未定义的变量,总会抛出错误),模板字面量本身也是javascript表达式,所以你可以在一个模板字面量里嵌入另一个,如下

let name = "Nicholas",
message = `Hello ${
  `my name is ${name}`
}`;
console.log(message);

标签函数的使用

function tag(literals,...substitutions){
  // 返回一个字符串
}
// 举个栗子
let count = 10,
price = 0.25
message = passthru`${count}items cost $${count*price.toFixed(2)}.`

如果你有一个名为 passthru() 函数,那么作为一个模板字面量标签,它会接受3个参数:
首先是一个literals数组:相当于两个变量位符把字符串切成了三段

  • 第一占位符前面:空字符''
  • 第一个和第二个占位符中间的items cost $
  • 第二个后面'.'

第二个参数就是count解释的值,传参为10,它也成为了substitutions数组的第一个元素,最后一个参数是count*price.toFixed(2)解释的值2.5作为substitutions数组的第二个元素。substitutions的元素个数总是比literals的长度少1。

function passthru(literals,...substitutions){
  let result = '';
  // 根据substition的数量来确定循环的次数
  for(let i=0;i<substitutions.length;i++){
    result+=literals[i];
    result+=substitutions[i];
  }
  // 合并最后一个literal
  result+=literals[literals.length-1];
  return result;
}

String.raw()

模板标签同样可以访问原生字符串信息,也就是说通过模板标签可以访问到字符转义被转换成等价字符前的原生字符串,最简单的例子是使用内建的String.raw()标签函数

let message1 = `Multiline\nstring`,
message2 = String.raw`Multiline\nstring`;
console.log(message1); // "Multiline
                       // string"
console.log(message2); // "Multiline\\nstring"

原生字符串信息同样被传入模板标签,标签函数的第一个参数是一个数组,它有一个额外的属性raw,是一个包含每一个字面值的原生等价信息的数组。如literals[0]总有一个等价的literals.raw[0],它包含着它的原生字符串信息。

主题测试文章,只做测试使用。发布者:Walker,转转请注明出处:https://joyjs.cn/archives/4309

(0)
Walker的头像Walker
上一篇 2025年3月8日 10:59
下一篇 2025年3月8日 02:11

相关推荐

  • 深入理解ES6 008【学习笔记】

    迭代器(Iterator)和生成器(Generator) 这个新特性对于高效的数据处理而言是不可或缺的,你也会发现在语言的其他特性中也都有迭代器的身影:新的for-of循环、展开运算符(...)、甚至连异步编程都可以使用迭代器。 迭代器是一种特殊的对象,它具有一些专门为迭代过程设计的专有接口,所有的迭代器对象都有一个next()方法,每次调用都返回一个结果对…

    个人 2025年3月8日
    35100
  • Nuxt3_扫盲 入门与原理介绍【学习笔记】

    Nuxt 3 入门与原理介绍 💡 什么是 Nuxt 3? Nuxt 3 是基于 Vue 3 和 Vite 打造的全栈前端框架,支持: 服务端渲染(SSR) 静态站点生成(SSG) 单页应用(SPA) 构建全栈应用(支持 API) Nuxt 3 是 Vue 的“加强版”,帮你简化项目结构和开发流程。 🔧 核心原理 功能 Nuxt 如何处理 ✅ 页面路由 自动根…

    个人 2025年4月6日
    42000
  • 深入理解ES6 005【学习笔记】

    解构:使用数据访问更便捷 如果使用var、let或const解构声明变量,则必须要提供初始化程序(也就是等号右侧的值)如下会导致错误 // 语法错误 var {tyep,name} // 语法错误 let {type,name} // 语法错误 const {type,name} 使用解构给已经声明的变量赋值,哪下 let node = { type:&qu…

    个人 2025年3月8日
    45800
  • TS珠峰 001【学习笔记】

    课程大纲 搭建 TypeScript 开发环境。 掌握 TypeScript 的基础类型,联合类型和交叉类型。 详细类型断言的作用和用法。 掌握 TypeScript 中函数、类的类型声明方式。 掌握类型别名、接口的作用和定义。 掌握泛型的应用场景,熟练应用泛型。 灵活运用条件类型、映射类型与内置类型。 创建和使用自定义类型。 理解命名空间、模块的概念已经使…

    个人 2025年3月27日
    38100
  • 深入理解ES6 010【学习笔记】

    改进的数组功能 new Array()的怪异行为,当构造函数传入一个数值型的值,那么数组的length属性会被设为该值;如果传入多个值,此时无论这些值是不是数值型的,都会变为数组的元素。这个特性另人困惑,你不可能总是注意传入数据的类型,所以存在一定的风险。 Array.of() 无论传多少个参数,不存在单一数值的特例(一个参数且数值型),总是返回包含所有参数…

    个人 2025年3月8日
    37000

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信
欢迎🌹 Coding never stops, keep learning! 💡💻 光临🌹