影子的知识库

影子的知识库

  • 知识库
  • GitHub

›ES6

JVM系列

  • JVM内存区域
  • 对象创建-布局-访问
  • 内存溢出实战
  • 内存区域回收
  • 四大引用
  • 垃圾回收算法
  • HotSpot回收算法细节

Java系列

  • java注解
  • springboot请求参数绑定
  • springboot请求参数校验框架
  • YAML语法
  • 动态代理
  • classpath和java命令
  • springboot-aop编程
  • springboot统一异常处理
  • springboot数据库和事务
  • springboot拦截器
  • springboot中的web配置
  • docker的简单开发
  • springboot自动配置
  • 数据库的隔离级别
  • springboot监控
  • java类加载
  • java-agent的相关内容
  • 类加载器详解
  • java的SecurityManager
  • maven学习

Node

    JS 基础

    • 语法基础和数据类型
    • 数据类型转换
    • 语句 表达式 运算符
    • 变量与对象
    • 函数
    • 数据处理
    • 常用 API
    • 重点知识

    ES6

    • 块级作用域
    • 字符串和正则表达式
    • 函数
    • 对象
    • Symbol
    • Set和Map
    • 迭代器和生成器
    • 类
    • 数组
    • Promise

    Node 基础

    • 模块系统
    • package.json
    • 内置对象
    • npm脚本的使用
    • Buffer
    • Stream
    • 事件循环机制
    • 示例代码

    stream系列

    • 流的缓冲
    • 可读流
    • 可写流
    • 双工流和转换流
    • 自定义流

后期计划

  • 学习计划
  • 专题研究计划
Edit

本文内容

UTF-16 和 Unicode

Unicode 为全世界所有的字符提供了一个对应的值,例如 a 对应 1,b 对应 2..."人" 对应 2333(以上的值都是我瞎编的),它仅仅是规定了字符和它对应的值。

而 UTF-8、UTF-16、UTF-32 这些,都是属于字符编码,它们的作用是用 8 位、16位、32位 等来表示一个 Unicode 字符(也就是用 1、2、4 个字节来表示 Unicode 里规定的码值)

显然,字符那么多,8 位不可能表示完世界上所有的字符,所以 UTF-8 提供了一种扩展机制,8 位最多表示 256 种状态,UTF-8 会留下一部分的值,标识为一个扩展。例如当我们读取一个字节,发现读取到的是 255 时,代表这一个 UTF-8 编码还没有将一个字符编码完全,需要再读一个 UTF-8 来确定剩下的编码。(255 是我编的,但是肯定是读取到某些值就当做一个扩展码位)

也就是说,会使用多个 UTF-8 码位来表示一个字符(称之为代理对)。同样的 UTF-16 也一样面临这个问题,因为字符总量超过了 65536 个,16 位也无法表示所有字符,UTF-16 依然需要扩展码位(代理对)。现代来说,UTF-32 是不需要扩展码位的,但是所有的字符都用 32 位表示,存储会有所浪费。

关于码位的 API

在 JavaScript 中,字符串都使用 UTF-16 编码,str.length 返回的是字符串的码位数量,因此,对于某些生僻的字符,str.length 的数量可能与字符串的字符数量不一致,例如:

let txt = "😁";
console.log(txt.length);  // 2
console.log(txt.charAt(0));  // �
console.log(txt.charAt(1)); // �
console.log(txt.charCodeAt(0)); // 55357
console.log(txt.charCodeAt(1)); // 56833
console.log(txt.codePointAt(0)); // 128513
console.log(txt.codePointAt(1)); // 56833

这个 😁 字符是通过 2 个 UTF-16 代理对表示的,因此:

  • length 将会返回 2
  • charAt 将返回不可打印的字符,因为这两个码位都是代理位,无法被打印出来
  • charCodeAt 返回的是字符串中的码位的信息
  • codePointAt 遇到代理对的时候,会将后续的代理对全部打印出来,因此可以完整的打印出一个字符代表的值
  • 当 codePointAt 遇到的不是代理对,打印的值与 charCodeAt 是一样的

检测一个字符占用的编码单元数量

function isBit32(str) {
    return str.codePointAt(0) > 0xFFFF;
}
console.log(isBit32('😁')); // true
console.log(isBit32('a')); // false

逻辑很直接,通过这种专门获取代理对的 API 很容易知道字符是否是代理对来表示的

根据码位生成字符

我们上面的字符 😁 的码位是 128513,我们可以根据这个码位,再反向生成这个字符

console.log(String.fromCodePoint(128513)); // 😁

总之就是,我们使用 CodePoint 而不是 charCode 就对了

其它字符串变更 API

子串识别

我们经常想要在一个字符串里查询另一个字符串是否存在,ES 6 中有 3 个 API

  • includes()
  • startsWith()
  • endsWith()

这三个方法的作用看名字就知道是啥意思,就不再细说了。

以上三个方法都接收 2 个参数:

  • 第一个参数指定要搜索的字符串文本
  • 第二个参数是可选的,指定一个搜索的位置的索引值。如果指定了这个位置,includes 和 startsWith 都会从这个位置开始搜索。而 endsWith 则从字符串长度减去这个索引值的位置开始搜索(也就是匹配倒数的位置是不是这个字符串)。

repeat() 方法

将一个字符串重复多次。

console.log('xa'.repeat(5));

输出:

(py3.5) czp@:~/workspace/knowledge-base/demos/node_start$ node hello.js
xaxaxaxaxa

模板字面量

模板字面量是允许嵌入表达式的字符串字面量。你可以使用多行字符串和字符串插值功能。

简而言之,模板字面量最后也会生成一个字符串。

基础语法

最简单是就是把模板字面量当成一个字符串使用

console.log(`aaaaaaa`);

注意我们这里的字符串,不是使用单引号,而是使用的反引号给包起来的,这就代表了一个模板字面量

模板字面量中不需要转义单引号和双引号,但是如果想输出反引号的话,需要转义,也就是

console.log(`\`aaaaaa\``);

多行字符串

let line = `line1
line2`
console.log(line);
console.log(line.length);

将会输出

(py3.5) czp@:~/workspace/knowledge-base/demos/node_start$ node hello.js
line1
line2
11

原理:被反引号包围的空白字符(空格、换行)都输于字符串的一部分

我们使用这个特性来创建多行字符串的时候,要小心缩进,例如:

let line = `line1
            line2`
console.log(line);
console.log(line.length);

将会输出

(py3.5) czp@:~/workspace/knowledge-base/demos/node_start$ node hello.js
line1
            line2
23

最佳实践

let line = `
<div>
    xxx
</div>`
console.log(line.trim());
console.log(line.length);

输出

(py3.5) czp@:~/workspace/knowledge-base/demos/node_start$ node hello.js
<div>
    xxx
</div>
21

我们通过第一行留白,然后 trim,来保持缩进

字符串占位符

模板字面量支持占位符的特性,也就是说,在模板字面量中,留下一些可供替换的地方,然后使用变量或者表达式对字面量进行替换

let name = "张三";
let message = `你好,${name}`;
console.log(message);

将会输出:你好,张三

这个功能其实也是很多语言都有的一种特性了。scala 里也有这种模板替换的功能。

注:模板字面量可以访问作用域中的所有可访问的变量,嵌入未定义的变量将会抛出错误

嵌入表达式

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

占位符里可以嵌入任何合法的 JavaScript 表达式,所以我们可以在里面进行函数调用和其它的计算

嵌入模板字面量

由于占位符可以嵌入合法的表达式,因此模板字面量本身也是可以被嵌入进去的

let name = "张三";
let message = `你好,${
    `我的名字是${name}`
}`;
console.log(message); // 你好,我的名字是张三

很明显:先计算里面的表达式,再计算外面的表达式

标签模板

标签模板就是加强版本的模板占位符

或者可以这么说,模板字面量其实就是通过标签模板来完成的,下面看例子

let age = 22;
let name = 'czp'
var tag = function (arr, age, name) {
    console.log(arr);
    console.log(age);
    console.log(name);
    return 'hehehe';
}
var message = tag`my age is ${age}, my name is ${name}`;
console.log(message);

将会输出

(py3.5) czp@:~/workspace/knowledge-base/demos/node_start$ node hello.js
[ 'my age is ', ', my name is ', '' ]
22
czp
hehehe

分析:

var message = tag`my age is ${age}, my name is ${name}`;

这一行跟之前的模板字面量的区别就是前面加了一个标签:tag

实际上这个标签就是我们自己定义的一个函数的名称(名称可以自己随便定义)

这一行的实际意义是:

  • 将模板字面量进行分割,例如 my age is ${age}, my name is ${name} 将会分割成:

    • "my age is "
    • ", my name is "
    • ""

    这个分割是按照里面的嵌入模板进行分割,任何一个类似于 ${name} 这样的模板都会将字符串分割成左边和右边两部分(右边没有字符的时候就会是一个 "" 空字符串)

  • 将分割后的字符串数组 ["my age is " , ", my name is " , ""] 作为 tag 函数的第一个参数

  • 第二个参数是 ${age} 的值,第三个参数是 ${name} 的值,总而言之后续参数是模板按照顺序解析的值

  • tag 函数的返回值将会是这个模板的值,也就是说

    tag`my age is ${age}, my name is ${name}`;
    等价于 tag(["my age is " , ", my name is " , ""], age, name);
    

显然,我们可以利用标签模板实现占位符的功能,也可以利用它实现一些更高级的功能,总之算是 ES 6 提供的一种模板 API 吧

Last updated on 11/8/2020
← 块级作用域函数 →
  • UTF-16 和 Unicode
  • 关于码位的 API
    • 检测一个字符占用的编码单元数量
    • 根据码位生成字符
  • 其它字符串变更 API
    • 子串识别
    • repeat() 方法
  • 模板字面量
    • 基础语法
    • 多行字符串
    • 字符串占位符
    • 标签模板
影子的知识库
Docs
Getting Started (or other categories)Guides (or other categories)API Reference (or other categories)
Community
User ShowcaseStack OverflowProject ChatTwitter
More
BlogGitHub
Copyright © 2020 Cen ZhiPeng