ES6字符串操作讲解(详细),字符串编码表,代码单元,码点的详细介绍。

Wesley13
• 阅读 498

以前用到字符串的方法时候,并不会深刻的去思考其中的原理,所以在es6新增的这些方法里就有点蒙圈了,于是想要搞清楚为什么会新增这些方法,以及如何使用这些方法。 

在博客园上看见一篇大神SamWeb的总结,很是详细,讲解透彻,故,引用于此,望莫失莫忘。

地址: https://www.cnblogs.com/SamWeb/p/7091469.html

ES6字符串操作

  讨论字符串操作之前,我们先来了解一下Unicode 编码的由来,因为Js中的字符串就是一系列Unicode码的集合。

  我们都知道,世界上存在着各种各样的语言,汉语,英语,日语等,相对应的,也就存在各种各样的字符,如汉语就是对应我们博大精深的汉字,英语则对应着英文字符,各种不同的字符存在,也导致各国制定各自不同的标准,来使用字符存储在计算机上,如Ascii 码表,GBK表,这就导致了一个问题,各国之间的文件不能够交换使用,容易出现乱码问题。这时国际标准化组织就想把各国的字符都统一起来,把它们放到一张码表中,然后在这张表中,给每一个字符都分配一个数字,成为这个字符的唯一的标识符,由于每个字符都对应唯一的一个数字,所以不会出现冲突。这张表就是Unicode码表或Unicode编码字符集,这个数字又叫做码点(Code Point). 码表的样子如下

ES6字符串操作讲解(详细),字符串编码表,代码单元,码点的详细介绍。

  通过这张表,我们可以看到字符和数字的一一对应关系。 丽 ---> 数字4E3D, 充 -----> 数字5145,  注意这里的数字都是用的16进制。

  想法是美好的,但是怎么落实到计算机中呢?怎么在计算中表示这些码点呢? 因为计算机中全是二进制,我们要用多少个字节来表示一个数字,这时出现了不同的实现方案,就是我们经常听说的UTF-8, UTF-16, UTF-32等, UTF-16 就是使用16个bit 位, 也就是两个字节来表示一个码点,这16个字节叫做代码单元(code unit),很明显这里有一个问题,两个字节的能够表示的最大数是65535, 如果码点超出了65535怎么处理? 比如码点134071, 那就用两个代码单元进行表示。

  Js 中对字符串的操作就是基于以上理论进行实现的,你可能还记得charCodeAt方法,它就是获取的代码单元

<script>
        let text = "𠮷";  
        console.log(text.charCodeAt(0));  // 55362
        console.log(text.charCodeAt(1))   // 57271
 </script>

  可以看到 𠮷 是有两个代码单元组成。Js中所有字符串操作都是基于代码单元的,如:length方法,它也是指多少个代码单元,  我们可以测试一下 𠮷 的长度,如果按代码单元的话,它的长度就是2.

let text = "𠮷";  
 console.log(text.length);  // 2

  ES6 为了增强对unicode 编码的支持,增加了一个方法codePointAt(),它直接返回的是字符的码点。这个方法接受一个参数(代码单元的位置),返回一个整数。

let text = "𠮷a";
console.log(text.codePointAt(0)); // 134071console.log(text.codePointAt(1)); // 57271console.log(text.codePointAt(2)); // 97 

  在原来代码单元0的位置上,我们获取到的是整个字符的码点值,同时我们如果要获取到a的码点值,只能是通过2来获取,因为𠮷 是有两个代码单元组成,a 的代码单元位置只能是第3位。在一个字符代码单元0的位置,我们始终会获取的它的码点值。

  通过码点值,我们可以很容易的判断一个字符是2个字节表示,然后多余两个字符,只要它的码点值大于65535(16进制0xFFFF)就是多余2个字节

function more16bit (char) {
    return char.codePointAt(0) > 0xFFFF;
}
console.log(more16bit('a')) // false
console.log(more16bit('𠮷')) // true

  有了codePointAt() 方法,肯定还要有一个相反的方法,通过码点来返回字符,那就是fromCodePoint

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

   为了配合code point 码点,正则表达式增加了一个u标志符,当有u标志后,正则表达式会按码点进行匹配,而不再使用代码单元。当有一个双字节的字符时,如果用单字符进行匹配,按照代码单元它就会出错,按照码点则不会(一个码点就是对应一个字符)。看一下下面这个例子:

let text = '𠮷';
// 正则表达式/^.$/ 表示单个字符
console.log(/^.$/.test(text)); //false
console.log(/^.$/u.test(text)); // true 加了u 标志
console.log(/^..$/.test(text));  // true 当我们用两个字符进行匹配成功

  字符串操作,其他方面的增强

   三个判断包含关系的API: includes(), startsWith() , endsWith() 通过字面意思可以知道 includes判断一个字符串是否包含另一个字符串,startsWith 一个字符串是 否以一个字符串开始,endsWith 一个字符串是否以一个字行串结束,所以它们都接受一个必须参数,要搜索的字符串,和一个可选参数,索引值,表示从字符串的第几个位置开始查找。和indexof ,lastIndexof 用法一样。写几个例子就非常熟悉了

ES6字符串操作讲解(详细),字符串编码表,代码单元,码点的详细介绍。

let msg = "Hello world!";

console.log(msg.startsWith("Hello")); // true 以Hello开头,正确
console.log(msg.endsWith("!")); // true  以!结尾,正确
console.log(msg.includes("o")); // true  包含o ,正确

console.log(msg.startsWith("o", 4)); // true  从开始第四个位置开始计算,第四个位置正好是o
console.log(msg.endsWith("o", 8)); // true  从倒数第8个位置开始,o正好是倒数第8个,注意,当倒数时候是从1开始记数
console.log(msg.includes("o", 8)); // false 正数第8个是r,r后面没有o

ES6字符串操作讲解(详细),字符串编码表,代码单元,码点的详细介绍。

  repeat 方法,就是对一个字符串重复多次,它接受一个参数,重复的次数,返回一个新字符串。

let msg = "Hello";
console.log(msg.repeat(4))  //HelloHelloHelloHello

  模版字符串:字符串中可以很简单地插入变量,像我们用handlebars写html模版一样,所以叫模版字符串,基本语法,就是用反引号``把字符串括起来。

  首先,模版字符串本质上也是字符串,我们可以像平时一样正常使用它。

let message = `Hello world!`;

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

  这样正常方式使用模版字符串的好处是我们可以非常轻松地写出多行字行串。

let message = `Hello 

world!`;

console.log(message);

  所有在反引号中的空白都会被保留, 浏览器控制台做如下输出:

ES6字符串操作讲解(详细),字符串编码表,代码单元,码点的详细介绍。

  这就非常有利于我们写html模版了

let html = `
<div>
    <h1>Title</h1>
</div>`;

   刚才我们说过,可以在html模版中插入js表达式, 插入的方式是:用${}把js表达式包起来

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

  然而模版字符串真正强大的地方在于标签模板,它的语法非常简单,就是在模板字符串开始的反撇号前附加一个额外的标签即可

let newMessage = tag`${count} items cost $${(count * price).toFixed(2)}.`;

  有了tag标签,我们要做就是定义这个标签,从而使我们可以对模版字符串进行各种转换操作,返回一个我们真正想要的字符串。其实tag标签就是一个函数,我们把它放到模版字符串中前面,就是对模版字符串调用这个函数.在这里要注意,调用tag函数的时候,并不是把整个模版字符串当作参数传递给这个函数,而是js引擎会把模版字符串进行分解,然后把分解后的结果传递给tag 函数。

  分割的方式,则是以${}作为分界进行分割。上面的模版字符串就会分割成 “ ” ,count, "item cost $", (count*price).toFixed(2), "."

前面的空字符串一定要注意,在第一个${}前面没有内容,所以是一个空字符串,这样可以保证,分割的字符串永远比 ${} 包 含的变量多1. 分割完成后,得到的字符串会再合并成一个数组[“”, “item cost $”, "."] ,然后作为第一个参数传递给tag 函数,剩下的变量count,(count*price).toFixed(2) 则会作为单独的参数,按照它们在模版字符串中出现的顺序依次传递tag函数, 

所以我们的tag函数接受的参数应该是([“”, “item cost $”, "."] , count, (count*price).toFixed(2))

  tag 函数的定义应该是 function tag(lliterals, value1, value2), ES6 增加了一个...rest 操作符,它会把所有的剩余函数参数都收集到一个数组中,所以tag 函数的定义还可以是这样 function tag(lliterals, ...substitutions)

ES6字符串操作讲解(详细),字符串编码表,代码单元,码点的详细介绍。

function tag(literals, ...substitutions) {
    let result = '';
    // 数组substitutions的长度永运比literals数组的长度少1个
    substitutions.forEach((item,i)=> (result += literals[i],result += item))
    // 把literals数组最后一个再加到新生成的字符串中.
    result += literals[literals.length -1];
    return result;
}

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

ES6字符串操作讲解(详细),字符串编码表,代码单元,码点的详细介绍。

  利用解构赋值和...rest操作符也可以对字符串进行解析,这时,js会把字符串作为数组看待,从而可以得到任意的字符,从而代替了charAt, substr 等方法。

// es6 解析字符串的方式
var str = 'helo';
var [first, ...tail] = str;
console.log(first) // 'h'
console.log(tail)  // ['e','l','o']
点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
2年前
java将前端的json数组字符串转换为列表
记录下在前端通过ajax提交了一个json数组的字符串,在后端如何转换为列表。前端数据转化与请求varcontracts{id:'1',name:'yanggb合同1'},{id:'2',name:'yanggb合同2'},{id:'3',name:'yang
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
2年前
Java日期时间API系列31
  时间戳是指格林威治时间1970年01月01日00时00分00秒起至现在的总毫秒数,是所有时间的基础,其他时间可以通过时间戳转换得到。Java中本来已经有相关获取时间戳的方法,Java8后增加新的类Instant等专用于处理时间戳问题。 1获取时间戳的方法和性能对比1.1获取时间戳方法Java8以前
Stella981 Stella981
2年前
JS 苹果手机日期显示NaN问题
问题描述newDate("2019122910:30:00")在IOS下显示为NaN原因分析带的日期IOS下存在兼容问题解决方法字符串替换letdateStr"2019122910:30:00";datedateStr.repl
Wesley13 Wesley13
2年前
ES6学习笔记(3)
参考书《ECMAScript6入门》http://es6.ruanyifeng.com/字符串的扩展ES6之前只能识别\\u0000\\uFFFF之间的字符,超过此范围,识别会出错;ES6弥补了这个错误ES6扩展的新方法codePointAt"𠮷".CodePointAt(0)//返回超过\\u00
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Stella981 Stella981
2年前
JavaScript常用函数
1\.字符串长度截取functioncutstr(str,len){vartemp,icount0,patrn/^\x00\xff/,strre"";for(vari
京东云开发者 京东云开发者
7个月前
分布式系统的主键生成方案对比 | 京东云技术团队
UUID​UUID(通用唯一识别码)是由32个十六进制数组成的无序字符串,通过一定的算法计算出来。为了保证其唯一性,UUID规范定义了包括网卡MAC地址、时间戳、名字空间(Namespace)、随机或伪随机数、时序等元素,以及从这些元素生成UUID的算法。