# 一、环境

对于不支持ES6的老版本浏览器,需要转译为ES5,可用webpack、babel等 此处使用babel转译ES6为ES5

# 1. 建立项目并配置环境

mkdir myProject // 新建项目
cd myProject // 进入项目
cnpm init -y // 快速初始化
// cnpm install -g babel-cli // 全局安装babel转译
cnpm i -D @babel/cli @babel/core @babel/preset-env // 使用最新的 babel 7 以及最新 preset-env 环境
touch .babelrc // 新建 .babelrc 文件,或 touch babel.config.js
vim .babelrc // 编辑新建的 .babelrc 文件
1
2
3
4
5
6
7
// .babelrc

{
 "presets":[
  "@babel/preset-env",
  "@babel/preset-flow",
  "@babel/preset-react",
  ["@babel/preset-typescript", {
      isTSX: true,
      allExtensions: true,
      // jsxPragma:
  }]
 ],
 "plugins":[]
}

// babel.config.js

const presets = [
  [
    "@babel/preset-env"
    // {
    //   targets: {
    //     edge: "17",
    //     firefox: "60",
    //     chrome: "67",
    //     safari: "11.1",
    //   }
    //   // useBuiltIns: "usage",
    // },
  ],
];

module.exports = { presets };

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

# 2. 开始编写

mkdir src // 创建 src 目录,作为 开发目录
mkdir dist // 创建 dist 目录,作为编译目录

cd src // 进入 src 目录
touch index.js // 创建 index.js 文件
vim index.js // 开始编写含有 es6 的 js 代码
1
2
3
4
5
6

# 3. 编译

  • 命令行方式
// -o => --out-file
// -d => --out-dir
// babel src/index.js -o dist/index.js  // 将src目录下的 index.js 转译到dist目录下
// babel src/index.js -o dist/index.js -w // 开启 babel 监听,可实时转译编写的文件
npx babel src/index.js -o dist/index.js  // npx babel 相当于执行 ./node_modules/.bin/babel
npx babel src/index.js -o dist/index.js -w // 开启监听编译
npx babel src -d dist -w // 开启监听编译,将src目录整个编译到dist目录

1
2
3
4
5
6
7
8
  • 脚本方式(推荐):
// package.json

  "scripts": {
    "dev": "babel src/index.js -o dist/index.js -w",
    "build": "babel src/index.js -o dist/index.js"
  },
1
2
3
4
5
6

之后执行 npm run dev/build 即可

# 二、变量声明: letconst

# 1. var(原有)

将会声明一个全局变量,全局可用,使用后依然存在,不会被销毁

# 2. let(新增)

# 建议不再使用var,全部用let代替var
  • let 不允许在相同作用域内,重复声明同一个变量。

  • 不存在变量提升,var声明的变量会被提前,值为undefined,let不会,在let前使用直接报错

    // 编译前直接运行
    
    console.log(a) // ReferenceError: a is not defined
    let a = 1
    
    1
    2
    3
    4
    // 编译后运行
    
    "use strict";
    
    console.log(a); // undefined
    var a = 1;
    
    1
    2
    3
    4
    5
    6
  • 原有的var只在函数中区分作用域,在大括号{}中无法建立块级作用域,let 声明的变量,只在let命令所在的代码块{}内有效。
    ES6 的块级作用域必须有大括号,如果没有大括号,JavaScript 引擎就认为不存在块级作用域。

    // 编译前直接运行
    
    {
      var a = 1
      let b = 1
    }
    console.log(a)
    console.log(b) // ReferenceError: b is not defined
    
    1
    2
    3
    4
    5
    6
    7
    8
    // 编译后运行
    
    "use strict";
    
    {
      var a = 1;
      var _b = 1; // 会自动将 let 声明的变量和代码块外使用的变量命名区分
    }
    console.log(a);
    console.log(b);  // ReferenceError: b is not defined
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
# 块级作用域的出现,实际上使得获得广泛应用的立即执行函数表达式(IIFE)不再必要了
```js
// IIFE 写法
(function () {
  var tmp = ...;
  ...
}());

// 块级作用域写法
{
  let tmp = ...;
  ...
}
```
# 暂时性死区(temporal dead zone,简称 TDZ)

只要块级作用域内存在 letconst 命令, 它所声明的变量就“绑定”(binding)这个区域,不再受外部同名全局变量的影响。不管在 let 前面还是后面。且在let前使用这个变量会直接报错

var a = 123

if (true) {
  a = 'abc' // ReferenceError: a is not defined,无法访问同名全局变量且报错
  let a
}
1
2
3
4
5
6
// 较为隐蔽的死区
function bar(x = y, y = 2) { // (x = 2, y = x) 则不会报错
  return [x, y]
}

bar() // ReferenceError: y is not defined, x = y 时 变量 y 还未被声明
1
2
3
4
5
6
// 不报错
var x = x // undefined,声明提前

// 报错
let y = y // ReferenceError: y is not defined
1
2
3
4
5

# 3. const (新增)

  • 声明一个只读的常量。声明之后不可被修改,否则报错。意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值。
// const a; // SyntaxError: Missing initializer in const declaration

const PI = 3.14;
PI = 3.1415; //报错
1
2
3
4

const 实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const 只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。

# ES6 声明变量的六种方法

ES5 只有两种声明变量的方法:var 命令和 function 命令。

ES6 除了添加 letconst 命令,后面章节还会提到,另外两种声明变量的方法: import 命令和 class 命令。所以,ES6 一共有 6 种声明变量的方法。

# 顶层对象的属性

ES6 为了改变这一点,一方面规定,为了保持兼容性,var命令和function命令声明的全局变量,依旧是顶层对象的属性;另一方面规定,let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性。也就是说,从 ES6 开始,全局变量将逐步与顶层对象的属性脱钩。

var a = 1;
// 如果在 Node 的 REPL 环境,可以写成 global.a
// 或者采用通用方法,写成 this.a
window.a // 1

let b = 1;
window.b // undefined
1
2
3
4
5
6
7

# globalThis 对象

JavaScript 语言存在一个顶层对象,它提供全局环境(即全局作用域),所有代码都是在这个环境中运行。但是,顶层对象在各种实现里面是不统一的。

浏览器里面,顶层对象是window,但 Node 和 Web Worker 没有window。 浏览器和 Web Worker 里面,self也指向顶层对象,但是 Node 没有self。 Node 里面,顶层对象是global,但其他环境都不支持。 同一段代码为了能够在各种环境,都能取到顶层对象,现在一般是使用this变量,但是有局限性。

全局环境中,this会返回顶层对象。但是,Node 模块和 ES6 模块中,this返回的是当前模块。 函数里面的this,如果函数不是作为对象的方法运行,而是单纯作为函数运行,this会指向顶层对象。但是,严格模式下,这时this会返回undefined。 不管是严格模式,还是普通模式,new Function('return this')(),总是会返回全局对象。但是,如果浏览器用了 CSP(Content Security Policy,内容安全策略),那么eval、new Function这些方法都可能无法使用。 综上所述,很难找到一种方法,可以在所有情况下,都取到顶层对象。下面是两种勉强可以使用的方法。

// 方法一
(typeof window !== 'undefined'
   ? window
   : (typeof process === 'object' &&
      typeof require === 'function' &&
      typeof global === 'object')
     ? global
     : this);

// 方法二
var getGlobal = function () {
  if (typeof self !== 'undefined') { return self; }
  if (typeof window !== 'undefined') { return window; }
  if (typeof global !== 'undefined') { return global; }
  throw new Error('unable to locate global object');
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

ES2020 在语言标准的层面,引入globalThis作为顶层对象。也就是说,任何环境下,globalThis都是存在的,都可以从它拿到顶层对象,指向全局环境下的this。

垫片库global-this模拟了这个提案,可以在所有环境拿到globalThis。

# 三、变量的解构赋值

  • 数组的解构赋值
  • 对象的解构赋值
  • 字符串的解构赋值
  • 数值和布尔值的解构赋值
  • 函数参数的解构赋值
  • 圆括号问题
  • 用途

解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于undefined和null无法转为对象,所以对它们进行解构赋值,都会报错。

# 1. 数组的解构赋值

// 不用解构的写法:
let a = 1
let b = 2
let c = 3

// 解构的写法:
let [a, b, c] = [1, 2, 3]
1
2
3
4
5
6
7
let [foo, [[bar], baz]] = [1, [[2], 3]];
foo // 1
bar // 2
baz // 3

let [ , , third] = ["foo", "bar", "baz"];
third // "baz"

let [x, , y] = [1, 2, 3];
x // 1
y // 3

// 剩余参数
let [head, ...tail] = [1, 2, 3, 4];
head // 1
tail // [2, 3, 4]

let [x, y, ...z] = ['a'];
x // "a"
y // undefined
z // []

// 不完全解构
let [x, y] = [1, 2, 3];
x // 1
y // 2

let [a, [b], d] = [1, [2, 3], 4];
a // 1
b // 2
d // 4

// 对于 Set 结构,也可以使用数组的解构赋值。

let [x, y, z] = new Set(['a', 'b', 'c']);
x // "a"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 只要某种数据结构具有 Iterator 接口,都可以采用数组形式的解构赋值
// Generator 函数实现的斐波拉契数列,
function* fibs() {
  let a = 0
  let b = 1
  while (true) {
    yield a; // 分号不能少
    [a, b] = [b, a + b]
  }
}

// Generator 函数具有 Iterator 接口。解构赋值会依次从这个接口获取值。
let [first, second, third, fourth, fifth, sixth] = fibs()
console.log(first, second, third, fourth, fifth, sixth) // 0 1 1 2 3 5

1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 数组解构的默认值

变量可设置默认值,若数组中没有足够的值结构出来赋值,则使用默认值,无默认值则为undefined

let [a, [b, c], d, e, f = 'default'] = [1, [2, 3], 4]
console.log(a, b, c, d, e, f) // 1 2 3 4 undefined "default"
1
2
# 默认值遇到 undefinednull 的区别

ES6 内部使用严格相等运算符(===),判断一个位置是否有值。所以,只有当一个数组成员严格等于undefined,默认值才会生效。

let [a, b = 20] = [10, undefined]
console.log(a, b) //10 20,undefined表示无值,则 b 会取默认值。

let [a, b = 20] = [10, null] // null不严格等于undefined
console.log(a, b) //10 null, null表示值为null, 则 b 不用默认值而用null
1
2
3
4
5
# 默认值为表达式

如果默认值为表达式,那么这个表达式是惰性求值的,即只有在用到的时候,才会求值。

function f() {
  console.log('函数执行')
}

let [x = f()] = [1] // x 能取到值,所以函数f根本不会执行。


// 以上代码等价于:

let x;
if ([1][0] === undefined) {
  x = f();
} else {
  x = [1][0];
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 默认值可以引用解构赋值的其他变量,但该变量必须已经声明
let [x = 1, y = x] = [];     // x=1; y=1
let [x = 1, y = x] = [2];    // x=2; y=2
let [x = 1, y = x] = [1, 2]; // x=1; y=2
let [x = y, y = 1] = [];     // ReferenceError: y is not defined

1
2
3
4
5

# 2. 对象的解构赋值

# 对象不按顺序而是按key结构,key相同才能成功结构并赋值
let {z, y, x} = {x: 10, y: 20}
console.log(x, y); // 10 20 undefined
1
2
# 对象的解构赋值,可以很方便地将现有对象的方法,赋值到某个变量
// 例一
let { random, round, max } = Math
console.log(random()) // 0.14743980691009595
console.log(round(3.1415)) // 3
console.log(max(1, 4, 7, 8, 5, 2)) // 8

// 例二
const { log } = console
log('hello') // hello
1
2
3
4
5
6
7
8
9
# 变量名与属性名不一致
let obj = { first: 'hello', last: 'world' }
let { first: f, last: l } = obj // 将对象中取出的 first, last 命名为变量 f, l
console.log(f, l) // hello world

1
2
3
4

以上例子说明。对象的解构赋值是下面形式的简写:

let { foo: foo, bar: bar } = { foo: 'aaa', bar: 'bbb' };
1
# 嵌套解构
// 例一:
let obj = {
  p: [
    'Hello',
    { y: 'World' }
  ]
}

let { p: [x, { y }] } = obj // 此写法 不能访问 p,若要访问须单独取出
let { p, p: [x, { y }] } = obj // 此写法可以访问 p

console.log(x, y) // Hello World

// 例二:
const node = {
  loc: {
    start: {
      line: 1,
      column: 5
    }
  }
};

let { loc, loc: { start }, loc: { start: { line }} } = node;
line // 1
loc  // Object {start: Object}
start // Object {line: 1, column: 5}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 嵌套赋值
// 将 obj2 中的: a 放入 obj,b 放入 arr
let obj2 = {
  a: 1,
  b: '1'
}
let obj = {}
let arr = []

/*
// 原始写法:
obj.a = obj2.a
arr[0] = obj2.b

console.log(obj) // {a: 1}
console.log(arr) // ["1"]
*/

// 解构写法:
;({a: obj.a, b: arr[0]} = obj2) // 圆括号之前须保证前一句代码结束,以防万一加分号结束前一句。

console.log(obj) // {a: 1}
console.log(arr) // ["1"]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 对象的解构赋值可以取到继承的属性
const obj1 = {};
const obj2 = { foo: 'bar' };
Object.setPrototypeOf(obj1, obj2);

const { foo } = obj1;
foo // "bar"

1
2
3
4
5
6
7

对象obj1的原型对象是obj2。foo属性不是obj1自身的属性,而是继承自obj2的属性,解构赋值可以取到这个属性。

# 对象解构的默认值

# 默认值生效的条件是,对象的属性值严格等于(===)undefined
let {x = 3} = {};
x // 3

let {x, y = 5} = {x: 1};
x // 1
y // 5

let {x: y = 3} = {};
y // 3

let {x: y = 3} = {x: 5};
y // 5

let { message: msg = 'Something went wrong' } = {};
msg // "Something went wrong"

let {x = 3} = {x: undefined};
x // 3

let {x = 3} = {x: null};
x // null

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 将一个已经声明的变量用于解构赋值

如果在解构之前就定义了变量,再解构会报错。也就是解构时前面没有 let

let a
{a} = {a: 10} // 报错 Declaration or statement expected.
1
2

因为 JavaScript 引擎会将{x}理解成一个代码块,从而发生语法错误。只有不将大括号写在行首,避免 JavaScript 将其解释为代码块,才能解决这个问题。

要解决报错,只需在解构的语句外边加一个圆括号即可。

let a;
({a} = {a: 10});
console.log(a);
1
2
3
# 将数组像对象一样解构
let arr = [1, 2, 3]
let {0 : first, [arr.length - 1] : last} = arr // 方括号这种写法,属于“属性名表达式”
console.log(first, last) // 1 3

1
2
3
4

# 3. 字符串的解构赋值

字符串也可以解构,因为,此时字符串会被转换成一个类数组对象。

let [a, b, c, d, e] = 'hello'
console.log(a, b, c, d, e) // h e l l o
1
2

类似数组的对象都有一个length属性,因此还可以对这个属性解构赋值。

let {length : len} = 'hello';
len // 5
1
2

# 4. 数值和布尔值的解构赋值

解构赋值时,如果等号右边是数值和布尔值,则会先转为对象。

let {toString: s} = 123
s === Number.prototype.toString // true

let {toString: s} = true
s === Boolean.prototype.toString // true
1
2
3
4
5

上面代码中,数值和布尔值的包装对象都有toString属性,因此变量s都能取到值。

解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。由于undefined和null无法转为对象,所以对它们进行解构赋值,都会报错。

let { prop: x } = undefined // TypeError
let { prop: y } = null // TypeError
1
2

# 5. 函数参数的解构赋值

function add([x, y]){
  return x + y
}

add([1, 2]) // 3

function add2({x, y}){
  return x + y
}

add2({x: 1, y: 2}) // 3


[[1, 2], [3, 4]].map(([a, b]) => a + b) // [ 3, 7 ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 使用默认值的参数解构
function move({x = 0, y = 0} = {}) {
  return [x, y];
}

move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, 0]
move({}); // [0, 0]
move(); // [0, 0]

// 以下写法和上述结果不同:
function move({x, y} = { x: 0, y: 0 }) {
  return [x, y];
}

move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, undefined]
move({}); // [undefined, undefined]
move(); // [0, 0]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 6. 圆括号问题

解构赋值虽然很方便,但是解析起来并不容易。对于编译器来说,一个式子到底是模式,还是表达式,没有办法从一开始就知道,必须解析到(或解析不到)等号才能知道。

由此带来的问题是,如果模式中出现圆括号怎么处理。ES6 的规则是,只要有可能导致解构的歧义,就不得使用圆括号。

但是,这条规则实际上不那么容易辨别,处理起来相当麻烦。因此,建议只要有可能,就不要在模式中放置圆括号。

# 不能使用圆括号的情况

# (1)变量声明语句
// 全部报错
let [(a)] = [1];

let {x: (c)} = {};
let ({x: c}) = {};
let {(x: c)} = {};
let {(x): c} = {};

let { o: ({ p: p }) } = { o: { p: 2 } };
1
2
3
4
5
6
7
8
9

上面 6 个语句都会报错,因为它们都是变量声明语句,模式不能使用圆括号。

# (2)函数参数

函数参数也属于变量声明,因此不能带有圆括号。

// 报错
function f([(z)]) { return z; }
// 报错
function f([z,(x)]) { return x; }
1
2
3
4
# (3)赋值语句的模式
// 全部报错
({ p: a }) = { p: 42 };
([a]) = [5];
1
2
3

上面代码将整个模式放在圆括号之中,导致报错。

// 报错
[({ p: a }), { x: c }] = [{}, {}];
1
2

上面代码将一部分模式放在圆括号之中,导致报错。

# 可以使用圆括号的情况

可以使用圆括号的情况只有一种:赋值语句的非模式部分,可以使用圆括号。

[(b)] = [3]; // 正确
({ p: (d) } = {}); // 正确
[(parseInt.prop)] = [3]; // 正确
1
2
3

上面三行语句都可以正确执行,因为首先它们都是赋值语句,而不是声明语句;其次它们的圆括号都不属于模式的一部分。第一行语句中,模式是取数组的第一个成员,跟圆括号无关;第二行语句中,模式是p,而不是d;第三行语句与第一行语句的性质一致。

# 四、字符串的扩展

  • 字符的 Unicode 表示法
  • 字符串的遍历器接口
  • 直接输入 U+2028 和 U+2029
  • JSON.stringify() 的改造
  • 模板字符串
  • 实例:模板编译
  • 标签模板
  • 模板字符串的限制

# 1. 字符的 Unicode 表示法

允许采用 \uxxxx 形式表示一个字符,其中xxxx表示字符的 Unicode 码点。

console.log("\u0061") // a
1

这种表示法只限于码点在\u0000~\uFFFF之间的字符。超出这个范围的字符,必须用两个双字节的形式表示。

console.log("\uD842\uDFB7") // "𠮷"

console.log("\u20BB7") // "₻7"
1
2
3

\u后面跟上超过 0xFFFF的数值(比如 \u20BB7 ),JavaScript 会理解成\u20BB (₻) + 7。所以打印出 ₻7

ES6 对这一点做出了改进,只要将码点放入大括号,就能正确解读该字符。

"\u{20BB7}" // "𠮷"

"\u{41}\u{42}\u{43}" // "ABC"

let hello = 123;
hell\u{6F} // 123

'\u{1F680}' === '\uD83D\uDE80' // 大括号表示法与四字节的 UTF-16 编码是等价的。
// true

1
2
3
4
5
6
7
8
9
10

# 2. 字符串的遍历器接口

ES6 为字符串添加了遍历器接口,使得字符串可以被for...of循环遍历。

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

除了遍历字符串,这个遍历器最大的优点是可以识别大于0xFFFF的码点,传统的for循环无法识别这样的码点。

let text = String.fromCodePoint(0x20BB7);
console.log(text) // 𠮷
console.log(text.length) // 2

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

for (let i of text) {
  console.log(i);
}
// "𠮷"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

上面代码中,字符串text只有一个字符,但是for循环会认为它包含两个字符(都不可打印),而for...of循环会正确识别出这一个字符。

# 3. 字符串转义字符

JavaScript 字符串允许直接输入字符,以及输入字符的转义形式。

'中' === '\u4e2d' // true
1

但 JavaScript 规定有5个字符,不能在字符串里面直接使用,只能使用转义形式。

  • U+005C:反斜杠(reverse solidus)
  • U+000D:回车(carriage return)
  • U+2028:行分隔符(line separator)
  • U+2029:段分隔符(paragraph separator)
  • U+000A:换行符(line feed)
// 反斜杠
console.log('hello\world') // helloworld,反斜杠被忽略
console.log('hello\\world') // hello\world
console.log('hello\u005cworld') // hello\world

// 换行
console.log('hello\nworld')
// hello
// world
console.log('hello\u000aworld')
// hello
// world

1
2
3
4
5
6
7
8
9
10
11
12
13

# 4. 模板字符串

  • 用反引号(`)标识。可当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。将变量名写在${}之中,大括号内部可以放入任意的 JavaScript 表达式,如计算,访问对象属性,调用函数等
  • 如果大括号中的值不是字符串,将按照一般的规则转为字符串。比如,大括号中是一个对象,将默认调用对象的toString方法。 如果模板字符串中的变量没有声明,将报错。大括号内部是一个字符串,将会原样输出。
  • 使用模板字符串表示多行字符串,所有的空格和缩进都会被保留在输出之中。
  • 在模板字符串中需要使用反引号,则前面要用反斜杠转义。 \`
  • 模板字符串还能嵌套。
// 普通字符串
console.log(`In JavaScript '\n' is a line-feed.`)


// 多行字符串
console.log(`In JavaScript this is
not legal.`)


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

// 字符串中嵌入变量
let name = "Bob", time = "today";
console.log(`Hello ${name}, how are you ${time}?`)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 5. 字符串的新增方法

  • String.fromCodePoint()
  • String.raw()
  • 实例方法:codePointAt()
  • 实例方法:normalize()
  • 实例方法:includes(), startsWith(), endsWith()
  • 实例方法:repeat()
  • 实例方法:padStart(),padEnd()
  • 实例方法:trimStart(),trimEnd()
  • 实例方法:matchAll()
# String.fromCodePoint()

用于替换 String.fromCharCode() 的方法,因为需要识别码点大于0xFFFF的字符。

# String.raw()

可以作为处理模板字符串的基本方法,它会将所有变量替换,而且对斜杠进行转义

String.raw`Hi\n${2+3}!`
1

作为正常的函数使用

String.raw({ raw: 'test' }, 0, 1, 2);
// 't0e1s2t'

// 等同于
String.raw({ raw: ['t','e','s','t'] }, 0, 1, 2);

1
2
3
4
5
6
# string.prototype.codePointAt()

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

let s = '𠮷a';
console.log(s.length) // 3 ,长度本应为 2
s.codePointAt(0).toString(16) // "20bb7"
s.codePointAt(2).toString(16) // "61" 第二个字符下标本应传入1,此方法实际需要传入2。
1
2
3
4
# 实例方法:includes(str, i), startsWith(str, i), endsWith(str, i)

传统用 indexOf 方法来判断一个字符串是否包含在另一个字符串中。新增三个方法。 includes() 是否包含 , startsWith() 是否在开头, endsWith() 是否在结尾

# repeat()

repeat方法返回一个新字符串,表示将原字符串重复n次。n 若为小数,小数部分直接忽略。n 不能为负数或Infinity。n 为 NaN 等同于 0.

# 实例方法:padStart(),padEnd()

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

'123456'.padStart(10, '0') // "0000123456"
'12'.padStart(10, 'YYYY-MM-DD') // "YYYY-MM-12"
'09-12'.padStart(10, 'YYYY-MM-DD') // "YYYY-09-12"
1
2
3

第一个参数是字符串补全生效的最大长度,第二个参数是用来补全的字符串。
原字符串的长度,等于或大于最大长度,则字符串补全不生效,返回原字符串。
如果用来补全的字符串与原字符串,两者的长度之和超过了最大长度,则会截去超出位数的补全字符串。
如果省略第二个参数,默认使用空格补全长度。

# 实例方法:trimStart(),trimEnd()

trimStart()消除字符串头部的空格,trimEnd()消除尾部的空格。 trimLeft()是trimStart()的别名,trimRight()是trimEnd()的别名。

# matchAll()

返回一个正则表达式在当前字符串的所有匹配