参考书籍

https://es6.ruanyifeng.com/

ES6简介

  • 与javascript关系
    • 是javascript的规格
    • javascirpt是其实现
  • 与ES2015关系
    • ES6是一个泛指,指5.1版本后的下一代标准,含ES2015 ES2016 ES2017等
  • Babel转码器
    • ES6代码转ES5

let与const

let

  • let声明的变量只在let所在的代码块有效
  • for循环中和适合用let

    1. for (let i = 0; i < 3; i++) {
    2. let i = 'abc';
    3. console.log(i);
    4. }
    5. // abc
    6. // abc
    7. // abc
    8. //这表明函数内部的变量i与循环变量i不在同一个作用域,有各自单独的作用域。
  • 不存在变量提升 ```javascript // var 的情况 console.log(foo); // 输出undefined var foo = 2;

// let 的情况 console.log(bar); // 报错ReferenceError let bar = 2;

  1. - 暂时性死区
  2. ```javascript
  3. var tmp = 123;
  4. if (true) {
  5. tmp = 'abc'; // ReferenceError
  6. let tmp;
  7. }
  8. //隐蔽的死区 是因为参数x默认值等于另一个参数y,而此时y还没有声明,属于“死区”。
  9. function bar(x = y, y = 2) {
  10. return [x, y];
  11. }
  12. bar(); // 报错
  1. 总之,在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“**暂时性死 区**”(temporal dead zone,简称 TDZ
  • 不允许重复声明,因此,不能在函数内部重新声明参数。

    块级作用域

  • 为何需要块级作用域 ```javascript var tmp = new Date();

function f() { console.log(tmp); if (false) { var tmp = ‘hello world’; } }

f(); // undefined 变量提升,导致内层的tmp变量覆盖了外层的tmp变量。

var s = ‘hello’;

for (var i = 0; i < s.length; i++) { console.log(s[i]); }

console.log(i); // 5 循环结束后,它并没有消失,泄露成了全局变量。

  1. - ES6 的块级作用域
  2. - 块级作用域与函数声明 //实现和语法规定有些不一样
  3. <a name="SnFIC"></a>
  4. ### Const命令
  5. - 声明一个只读的常量。一旦声明,常量的值就不能改变。
  6. - const的作用域与let命令相同:只在声明所在的块级作用域内有效。
  7. - 本质
  8. - 地址不变,所以在将一个对象声明为常量必须非常小心。如果真的想将对象冻结,应该使用Object.freeze方法。
  9. - ES6 声明变量的六种方法
  10. - var function let const import class
  11. <a name="yKDxh"></a>
  12. ### 顶层对象属性
  13. - es5 ,浏览器中顶层对象是windows 所以 a=1 windows.a=1 等价
  14. - es6中将 顶层对象与全局变量脱钩,为保持兼容性, var命令和function命令声明的全局变量,依旧是顶层对象的属性;另一方面规定,let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性
  15. <a name="L1mLO"></a>
  16. ### globalThis 对象
  17. - 顶层对象在各种实现里面是不统一的。
  18. - 勉强可以在任意实现环境下拿到顶层对象的代码如下
  19. ```javascript
  20. // 方法一
  21. (typeof window !== 'undefined'
  22. ? window
  23. : (typeof process === 'object' &&
  24. typeof require === 'function' &&
  25. typeof global === 'object')
  26. ? global
  27. : this);
  28. // 方法二
  29. var getGlobal = function () {
  30. if (typeof self !== 'undefined') { return self; }
  31. if (typeof window !== 'undefined') { return window; }
  32. if (typeof global !== 'undefined') { return global; }
  33. throw new Error('unable to locate global object');
  34. };
  • ES2020 引入globalThis 拿取顶层对象

    变量的解构赋值

    数组的解构赋值

  • 基本用法

    • 只要某种数据结构具有 Iterator 接口,都可以采用数组形式的解构赋值。 ```javascript let [a, b, c] = [1, 2, 3]; 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 //结构不成功 变量的值就等于undefined z // []

  1. - 默认值
  2. ```javascript
  3. let [foo = true] = [];
  4. foo // true
  5. let [x, y = 'b'] = ['a']; // x='a', y='b'
  6. let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'

对象的解构赋值

  • 简介 ```javascript let { foo, bar } = { foo: ‘aaa’, bar: ‘bbb’ }; foo // “aaa” bar // “bbb” let {foo} = {bar: ‘baz’}; foo // undefined

// 例一 let { log, sin, cos } = Math; //将Math对象的对数、正弦、余弦三个方法,赋值到对应的变量上,使用起来就会方便很多

// 例二 const { log } = console; log(‘hello’) // hello

let obj = { first: ‘hello’, last: ‘world’ }; let { first: f, last: l } = obj; f // ‘hello’ l // ‘world’

//嵌套的情况

let obj = { p: [ ‘Hello’, { y: ‘World’ } ] };

let { p: [x, { y }] } = obj; x // “Hello” y // “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}

//有三次解构赋值

//可以取到继承的属性 const obj1 = {}; const obj2 = { foo: ‘bar’ }; Object.setPrototypeOf(obj1, obj2);

const { foo } = obj1; foo // “bar”

  1. - 默认值
  2. ```javascript
  3. var {x = 3} = {};
  4. x // 3
  5. var {x, y = 5} = {x: 1};
  6. x // 1
  7. y // 5
  8. var {x: y = 3} = {};
  9. y // 3
  10. var {x: y = 3} = {x: 5};
  11. y // 5
  12. var { message: msg = 'Something went wrong' } = {};
  13. msg // "Something went wrong"
  14. var {x = 3} = {x: undefined};
  15. x // 3
  16. var {x = 3} = {x: null};
  17. x // null
  • 注意点
    1. let arr = [1, 2, 3];
    2. let {0 : first, [arr.length - 1] : last} = arr;
    3. first // 1
    4. last // 3

    字符串的解构赋值

    ```javascript //字符串会被转换成类似数组的类型, 有属性length const [a, b, c, d, e] = ‘hello’; a // “h” b // “e” c // “l” d // “l” e // “o”

let {length : len} = ‘hello’; len // 5

  1. <a name="fBREj"></a>
  2. ### 数值和布尔值的解构赋值
  3. ```javascript
  4. //数值和布尔值的包装对象都有toString属性,因此变量s都能取到值。
  5. let {toString: s} = 123;
  6. s === Number.prototype.toString // true
  7. let {toString: s} = true;
  8. s === Boolean.prototype.toString // true
  9. 解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。
  10. 由于undefined和null无法转为对象,所以对它们进行解构赋值,都会报错。

圆括号问题

  • 不能使用的情况 ```javascript // 全部报错 因为它们都是变量声明语句,模式不能使用圆括号。 let [(a)] = [1];

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

let { o: ({ p: p }) } = { o: { p: 2 } };

// 报错 函数参数也属于变量声明,因此不能带有圆括号。 function f([(z)]) { return z; } // 报错 function f([z,(x)]) { return x; }

// 全部报错 赋值语句的模式 ({ p: a }) = { p: 42 }; ([a]) = [5];

  1. - 能使用的情况
  2. - 可以使用圆括号的情况只有一种:赋值语句的非模式部分,可以使用圆括号。
  3. ```javascript
  4. [(b)] = [3]; // 正确
  5. ({ p: (d) } = {}); // 正确
  6. [(parseInt.prop)] = [3]; // 正确

用途

  • 交换变量
  • 从函数返回多个值
  • 函数参数的定义
  • 提取 JSON 数据 ```javascript let jsonData = { id: 42, status: “OK”, data: [867, 5309] };

let { id, status, data: number } = jsonData;

console.log(id, status, number); // 42, “OK”, [867, 5309]

  1. - 函数参数的默认值
  2. ```javascript
  3. jQuery.ajax = function (url, {
  4. async = true,
  5. beforeSend = function () {},
  6. cache = true,
  7. complete = function () {},
  8. crossDomain = false,
  9. global = true,
  10. // ... more config
  11. } = {}) {
  12. // ... do stuff
  13. };
  • 遍历 Map 结构 ```javascript for (let [key, value] of map) { console.log(key + “ is “ + value); } // first is hello // second is world // 获取键名 for (let [key] of map) { // … }

// 获取键值 for (let [,value] of map) { // … }

  1. - 输入模块的指定方法
  2. ```javascript
  3. const { SourceMapConsumer, SourceNode } = require("source-map");

字符串扩展

字符的unicode表示方法

  1. //6种表示字符方法
  2. '\z' === 'z' // true
  3. '\172' === 'z' // true
  4. '\x7A' === 'z' // true
  5. '\u007A' === 'z' // true 这种表示法只限于码点在\u0000~\uFFFF之间的字符。
  6. 超出这个范围的字符,必须用两个双字节的形式表示。
  7. '\u{7A}' === 'z' // true
  8. //如果直接在\u后面跟上超过0xFFFF的数值(比如\u20BB7),JavaScript 会理解成\u20BB+7。由于\u20BB是一个不可打印字符,所以只会显示一个空格,后面跟着一个7。

字符串的遍历器接口

  • es6为字符串添加了遍历接口
  • 这个遍历器最大的优点是可以识别大于0xFFFF的码点,传统的for循环无法识别这样的码点。 ```javascript for (let codePoint of ‘foo’) { console.log(codePoint) } // “f” // “o” // “o”

let text = String.fromCodePoint(0x20BB7);

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

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

  1. <a name="WPxY4"></a>
  2. ### 直接输入 U+2028 和 U+2029
  3. - JavaScript 字符串允许直接输入字符,以及输入字符的转义形式
  4. ```javascript
  5. '中' === '\u4e2d' // true
  • JavaScript 规定有5个字符,不能在字符串里面直接使用,只能使用转义形式。

    • U+005C:反斜杠(reverse solidus)
    • U+000D:回车(carriage return)
    • U+2028:行分隔符(line separator)
    • U+2029:段分隔符(paragraph separator)
    • U+000A:换行符(line feed)

      JSON.stringify() 的改造

  • \uD834\uDF06是两个码点,但是必须放在一起配对使用,代表字符𝌆

  • ES2019 改变了JSON.stringify()的行为。如果遇到0xD800到0xDFFF之间的单个码点,或者不存在的配对形式,它会返回转义字符串,留给应用自己决定下一步的处理。

    模板字符串

  • 模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。

  • 如果使用模板字符串表示多行字符串,所有的空格和缩进都会被保留在输出之中
  • 模板字符串中嵌入变量,需要将变量名写在${}之中。
  • 大括号内部可以放入任意的 JavaScript 表达式,可以进行运算,以及引用对象属性。
  • 模板字符串之中还能调用函数。 ```javascript //传统 (‘#result’).append( ‘There are ‘ + basket.count + ‘ ‘ + ‘items in your basket, ‘ + ‘‘ + basket.onSale + ‘ are on sale!’ );

//ES6 $(‘#result’).append(There are <b>${basket.count}</b> items in your basket, <em>${basket.onSale}</em> are on sale!);

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

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

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

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

function fn() { return “Hello World”; }

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

  1. - 模板字符串甚至还能嵌套。
  2. ```javascript
  3. const tmpl = addrs => `
  4. <table>
  5. ${addrs.map(addr => `
  6. <tr><td>${addr.first}</td></tr>
  7. <tr><td>${addr.last}</td></tr>
  8. `).join('')}
  9. </table>
  10. `;
  11. const data = [
  12. { first: '<Jane>', last: 'Bond' },
  13. { first: 'Lars', last: '<Croft>' },
  14. ];
  15. console.log(tmpl(data));
  16. // <table>
  17. //
  18. // <tr><td><Jane></td></tr>
  19. // <tr><td>Bond</td></tr>
  20. //
  21. // <tr><td>Lars</td></tr>
  22. // <tr><td><Croft></td></tr>
  23. //
  24. // </table>
  25. let func = (name) => `Hello ${name}!`;
  26. func('Jack') // "Hello Jack!"

模板编译

  1. let template = `
  2. <ul>
  3. <% for(let i=0; i < data.supplies.length; i++) { %>
  4. <li><%= data.supplies[i] %></li>
  5. <% } %>
  6. </ul>
  7. `;
  8. function compile(template){
  9. const evalExpr = /<%=(.+?)%>/g;
  10. const expr = /<%([\s\S]+?)%>/g;
  11. template = template
  12. .replace(evalExpr, '`); \n echo( $1 ); \n echo(`')
  13. .replace(expr, '`); \n $1 \n echo(`');
  14. template = 'echo(`' + template + '`);';
  15. let script =
  16. `(function parse(data){
  17. let output = "";
  18. function echo(html){
  19. output += html;
  20. }
  21. ${ template }
  22. return output;
  23. })`;
  24. return script;
  25. }
  26. let parse = eval(compile(template));
  27. div.innerHTML = parse({ supplies: [ "broom", "mop", "cleaner" ] });
  28. // <ul>
  29. // <li>broom</li>
  30. // <li>mop</li>
  31. // <li>cleaner</li>
  32. // </ul>

标签模板

  • “标签模板”的一个重要应用,就是过滤 HTML 字符串,防止用户输入恶意内容。 ```javascript let a = 5; let b = 10;

function tag(s, v1, v2) { console.log(s[0]); console.log(s[1]); console.log(s[2]); console.log(v1); console.log(v2);

return “OK”; }

tagHello ${ a + b } world ${ a * b}; // “Hello “ // “ world “ // “” // 15 // 50 // “OK”

  1. <a name="v3aOK"></a>
  2. ### 模板字符串的限制
  3. <br />- ES2018 [放松](https://tc39.github.io/proposal-template-literal-revision/)了对标签模板里面的字符串转义的限制。如果遇到不合法的字符串转义,就返回undefined,而不是报错,并且从raw属性上面可以得到原始字符串。
  4. <a name="CyiZo"></a>
  5. ## 字符串新增方法
  6. <a name="q3tev"></a>
  7. ### String.fromCodePoint
  8. - String.fromCharCode()不能识别大于0xFFFF的码点,所以0x20BB7就发生了溢出,最高位2被舍弃了
  9. - ES6 提供了String.fromCodePoint()方法,可以识别大于0xFFFF的字符
  10. <a name="wB6ff"></a>
  11. ### String.raw
  12. ```javascript
  13. String.raw`Hi\n${2+3}!`
  14. // 实际返回 "Hi\\n5!",显示的是转义后的结果 "Hi\n5!"
  15. String.raw`Hi\\n`
  16. // 返回 "Hi\\\\n"
  17. String.raw`Hi\\n` === "Hi\\\\n" // true
  18. // `foo${1 + 2}bar`
  19. // 等同于
  20. String.raw({ raw: ['foo', 'bar'] }, 1 + 2) // "foo3bar"

codePointAt

  • codePointAt()方法会正确返回 32 位的 UTF-16 字符的码点。对于那些两个字节储存的常规字符,它的返回结果与charCodeAt()方法相同。 ```javascript et s = ‘𠮷a’;

s.codePointAt(0).toString(16) // “20bb7” s.codePointAt(2).toString(16) // “61”

//字符a在字符串s的正确位置序号应该是 1,但是必须向codePointAt()方法传入 2。 解决这个问题的一个办法是使用for…of循环,因为它会正确识别 32 位的 UTF-16 字符。 let s = ‘𠮷a’; for (let ch of s) { console.log(ch.codePointAt(0).toString(16)); } // 20bb7 // 61

codePointAt()方法是测试一个字符由两个字节还是由四个字节组成的最简单方法。 function is32Bit(c) { return c.codePointAt(0) > 0xFFFF; }

is32Bit(“𠮷”) // true is32Bit(“a”) // false

  1. <a name="ozyXQ"></a>
  2. ### 实例方法:normalize()
  3. ```javascript
  4. // O(\u004F)和ˇ(\u030C)合成Ǒ(\u004F\u030C)
  5. 但js不支持
  6. '\u01D1'==='\u004F\u030C' //false
  7. '\u01D1'.length // 1
  8. '\u004F\u030C'.length // 2
  9. ES6 提供字符串实例的normalize()方法,用来将字符的不同表示方法统一为同样的形式,这称为 Unicode 正规化。
  10. '\u01D1'.normalize() === '\u004F\u030C'.normalize()
  11. // true
  12. //有不同标准的
  13. '\u004F\u030C'.normalize('NFC').length // 1
  14. '\u004F\u030C'.normalize('NFD').length // 2

includes, startsWith,endsWith

  1. let s = 'Hello world!';
  2. s.startsWith('world', 6) // true
  3. s.endsWith('Hello', 5) // true
  4. s.includes('Hello', 6) // false

repeat

padsStart padsEnd

trimStart trimEnd

matchAll

replaceAll

正则扩展

RegExp 构造函数

  1. var regex = new RegExp('xyz', 'i');
  2. // 等价于
  3. var regex = /xyz/i;
  4. var regex = new RegExp(/xyz/i);
  5. // 等价于
  6. var regex = /xyz/i;

字符串的正则方法

  1. String.prototype.match 调用 RegExp.prototype[Symbol.match]
  2. String.prototype.replace 调用 RegExp.prototype[Symbol.replace]
  3. String.prototype.search 调用 RegExp.prototype[Symbol.search]
  4. String.prototype.split 调用 RegExp.prototype[Symbol.split]

u修饰符

  1. /^\uD83D/u.test('\uD83D\uDC2A') // false
  2. /^\uD83D/.test('\uD83D\uDC2A') // true
  • 点字符 ```javascript var s = ‘𠮷’;

/^.$/.test(s) // false /^.$/u.test(s) // true

  1. - **Unicode 字符表示法**
  2. ```javascript
  3. /\u{61}/.test('a') // false
  4. /\u{61}/u.test('a') // true
  5. /\u{20BB7}/u.test('𠮷') // true
  • 量词

    1. /a{2}/.test('aa') // true
    2. /a{2}/u.test('aa') // true
    3. /𠮷{2}/.test('𠮷𠮷') // false
    4. /𠮷{2}/u.test('𠮷𠮷') // true
  • 预定义模式 ```javascript /^\S$/.test(‘𠮷’) // false /^\S$/u.test(‘𠮷’) // true // \S是预定义模式,匹配所有非空白字符

function codePointLength(text) { var result = text.match(/[\s\S]/gu); return result ? result.length : 0; }

var s = ‘𠮷𠮷’;

s.length // 4 codePointLength(s) // 2

  1. - **i 修饰符**
  2. ```javascript
  3. /[a-z]/i.test('\u212A') // false
  4. /[a-z]/iu.test('\u212A') // true
  • 转义

RegExp.prototype.unicode 属性

  1. //正则实例对象新增unicode属性,表示是否设置了u修饰符。
  2. const r1 = /hello/;
  3. const r2 = /hello/u;
  4. r1.unicode // false
  5. r2.unicode // true

y修饰符

  • 粘连修饰符 ```javascript var s = ‘aaa_aa_a’; var r1 = /a+/g; var r2 = /a+/y;

r1.exec(s) // [“aaa”] r2.exec(s) // [“aaa”]

r1.exec(s) // [“aa”] r2.exec(s) // null //第一次执行的时候,两者行为相同,剩余字符串都是_aa_a。由于g修饰没有位置要求, //所以第二次执行会返回结果,而y修饰符要求匹配必须从头部开始,所以返回null。

  1. <a name="CntJ7"></a>
  2. ### RegExp.prototype.sticky 属性
  3. ```javascript
  4. //ES6 的正则实例对象多了sticky属性,表示是否设置了y修饰符。
  5. var r = /hello\d/y;
  6. r.sticky // true

RegExp.prototype.flags 属性

  1. // ES5 的 source 属性
  2. // 返回正则表达式的正文
  3. /abc/ig.source
  4. // "abc"
  5. // ES6 的 flags 属性
  6. // 返回正则表达式的修饰符
  7. /abc/ig.flags
  8. // 'gi'

s 修饰符:dotAll 模式

  1. // . 除了个字节的 UTF-16 字符和终止符都可以表示
  2. //ES2018 引入s修饰符,使得.可以匹配任意单个字符。
  3. const re = /foo.bar/s;
  4. // 另一种写法
  5. // const re = new RegExp('foo.bar', 's');
  6. re.test('foo\nbar') // true
  7. re.dotAll // true
  8. re.flags // 's'

后行断言

//TODO 记不住

Unicode 属性类

  1. ES2018 引入了一种新的类的写法\p{...}和\P{...},允许正则表达式匹配符合 Unicode 某种属性的所有字符。
  2. const regexGreekSymbol = /\p{Script=Greek}/u;
  3. regexGreekSymbol.test('π') // true
  4. // 匹配所有空格
  5. \p{White_Space}
  6. // 匹配各种文字的所有字母,等同于 Unicode 版的 \w
  7. [\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}]
  8. // 匹配各种文字的所有非字母的字符,等同于 Unicode 版的 \W
  9. [^\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}]
  10. // 匹配 Emoji
  11. /\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation}|\p{Emoji}\uFE0F/gu
  12. // 匹配所有的箭头字符
  13. const regexArrows = /^\p{Block=Arrows}+$/u;
  14. regexArrows.test('←↑→↓↔↕↖↗↘↙⇏⇐⇑⇒⇓⇔⇕⇖⇗⇘⇙⇧⇩') // true

具名组匹配

  • 正则表达式使用圆括号进行组匹配。 ```javascript const RE_DATE = /(\d{4})-(\d{2})-(\d{2})/;

const matchObj = RE_DATE.exec(‘1999-12-31’); const year = matchObj[1]; // 1999 const month = matchObj[2]; // 12 const day = matchObj[3]; // 31

  1. <a name="xU6iJ"></a>
  2. ### 解构赋值和替换
  3. ```javascript
  4. let {groups: {one, two}} = /^(?<one>.*):(?<two>.*)$/u.exec('foo:bar');
  5. one // foo
  6. two // bar
  7. let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
  8. '2015-01-02'.replace(re, '$<day>/$<month>/$<year>')
  9. // '02/01/2015'
  10. '2015-01-02'.replace(re, (
  11. matched, // 整个匹配结果 2015-01-02
  12. capture1, // 第一个组匹配 2015
  13. capture2, // 第二个组匹配 01
  14. capture3, // 第三个组匹配 02
  15. position, // 匹配开始的位置 0
  16. S, // 原字符串 2015-01-02
  17. groups // 具名组构成的一个对象 {year, month, day}
  18. ) => {
  19. let {day, month, year} = groups;
  20. return `${day}/${month}/${year}`;
  21. });

引用

  • 如果要在正则表达式内部引用某个“具名组匹配”,可以使用\k<组名>的写法。 ```javascript const RE_TWICE = /^(?[a-z]+)!\k$/; RE_TWICE.test(‘abc!abc’) // true RE_TWICE.test(‘abc!ab’) // false

const RE_TWICE = /^(?[a-z]+)!\1$/; RE_TWICE.test(‘abc!abc’) // true RE_TWICE.test(‘abc!ab’) // false const RE_TWICE = /^(?[a-z]+)!\k!\1$/; RE_TWICE.test(‘abc!abc!abc’) // true RE_TWICE.test(‘abc!abc!ab’) // false

  1. <a name="fm02T"></a>
  2. ### 正则匹配索引
  3. ```javascript
  4. const text = 'zabbcdef';
  5. const re = /ab/;
  6. const result = re.exec(text);
  7. result.index // 1
  8. result.indices // [ [1, 3] ]
  9. const re = /ab+(cd)/;
  10. const result = re.exec(text);
  11. result.indices // [ [ 1, 6 ], [ 4, 6 ] ]

String.prototype.matchAll()

  1. ///ES 2020 提供了这个方法
  2. const string = 'test1test2test3';
  3. const regex = /t(e)(st(\d?))/g;
  4. for (const match of string.matchAll(regex)) {
  5. console.log(match);
  6. }
  7. // ["test1", "e", "st1", "1", index: 0, input: "test1test2test3"]
  8. // ["test2", "e", "st2", "2", index: 5, input: "test1test2test3"]
  9. // ["test3", "e", "st3", "3", index: 10, input: "test1test2test3"]

数值扩展

二进制和八进制表示法

  • ES6 提供了二进制和八进制数值的新的写法,分别用前缀0b(或0B)和0o(或0O)表示。 ```javascript 0b111110111 === 503 // true 0o767 === 503 // true

// 非严格模式 (function(){ console.log(0o11 === 011); })() // true

// 严格模式 (function(){ ‘use strict’; console.log(0o11 === 011); })() // Uncaught SyntaxError: Octal literals are not allowed in strict

//如果要将0b和0o前缀的字符串数值转为十进制,要使用Number方法。

Number(‘0b111’) // 7 Number(‘0o10’) // 8

  1. <a name="kPOY2"></a>
  2. ### 数值分隔符
  3. - 欧美语言中,较长的数值允许每三位添加一个分隔符(通常是一个逗号),增加数值的可读性。比如,1000可以写作1,000
  4. - [ES2021](https://github.com/tc39/proposal-numeric-separator),允许 JavaScript 的数值使用下划线(_)作为分隔符。
  5. - 下面三个将字符串转成数值的函数,不支持数值分隔符。
  6. - Number()
  7. - parseInt()
  8. - parseFloat()
  9. <a name="EY19p"></a>
  10. ### Number.isFinite(), Number.isNaN()
  11. ```javascript
  12. isFinite(25) // true
  13. isFinite("25") // true
  14. Number.isFinite(25) // true
  15. Number.isFinite("25") // false
  16. isNaN(NaN) // true
  17. isNaN("NaN") // true
  18. Number.isNaN(NaN) // true
  19. Number.isNaN("NaN") // false
  20. Number.isNaN(1) // false

Number.parseInt(), Number.parseFloat()

  1. // ES5的写法
  2. parseInt('12.34') // 12
  3. parseFloat('123.45#') // 123.45
  4. // ES6的写法
  5. Number.parseInt('12.34') // 12
  6. Number.parseFloat('123.45#') // 123.45

Number.isInteger()

  1. //精度有限
  2. Number.isInteger(3.0000000000000002) // true
  3. Number.isInteger(5E-324) // false
  4. Number.isInteger(5E-325) // true

Number.EPSILON

  • Number.EPSILON实际上是 JavaScript 能够表示的最小精度。误差如果小于这个值,就可以认为已经没有意义了,即不存在误差了。 ```javascript Number.EPSILON === Math.pow(2, -52) // true Number.EPSILON // 2.220446049250313e-16 Number.EPSILON.toFixed(20) // “0.00000000000000022204”

//Number.EPSILON可以用来设置“能够接受的误差范围”。比如,误差范围设为 2 的-50 次方(即Number.EPSILON * Math.pow(2, 2))

function withinErrorMargin (left, right) { return Math.abs(left - right) < Number.EPSILON * Math.pow(2, 2); }

0.1 + 0.2 === 0.3 // false withinErrorMargin(0.1 + 0.2, 0.3) // true

1.1 + 1.3 === 2.4 // false withinErrorMargin(1.1 + 1.3, 2.4) // true

  1. <a name="Mq61p"></a>
  2. ### 安全整数和 Number.isSafeInteger()
  3. - JavaScript 能够准确表示的整数范围在-2^53到2^53之间
  4. - ES6 引入了Number.MAX_SAFE_INTEGER和Number.MIN_SAFE_INTEGER这两个常量,
  5. ```javascript
  6. Number.MAX_SAFE_INTEGER === Math.pow(2, 53) - 1
  7. // true
  8. Number.MAX_SAFE_INTEGER === 9007199254740991
  9. // true
  10. Number.MIN_SAFE_INTEGER === -Number.MAX_SAFE_INTEGER
  11. // true
  12. Number.MIN_SAFE_INTEGER === -9007199254740991
  13. // true

Math 对象的扩展

  • Math.trunc()
    • 去除小数部分
  • Math.sign()
    • 参数为正数,返回+1;
    • 参数为负数,返回-1;
    • 参数为 0,返回0;
    • 参数为-0,返回-0;
    • 其他值,返回NaN。
  • Math.cbrt()
    • 立方根
  • Math.clz32()
    • Math.clz32()方法将参数转为 32 位无符号整数的形式,然后返回这个 32 位值里面有多少个前导 0。
  • Math.imul()
  • Math.fround()
    • Math.fround方法返回一个数的32位单精度浮点数形式。
  • //

函数的扩展

函数参数的默认值

  • 基本用法 ```javascript function Point(x = 0, y = 0) { this.x = x; this.y = y; }

const p = new Point(); p // { x: 0, y: 0 }

let x = 99; function foo(p = x + 1) { console.log(p); }

foo() // 100

x = 100; foo() // 101

  1. - 与解构赋值默认值结合使用
  2. ```javascript
  3. function foo({x, y = 5}) {
  4. console.log(x, y);
  5. }
  6. foo({}) // undefined 5
  7. foo({x: 1}) // 1 5
  8. foo({x: 1, y: 2}) // 1 2
  9. foo() // TypeError: Cannot read property 'x' of undefined
  10. function fetch(url, { body = '', method = 'GET', headers = {} }) {
  11. console.log(method);
  12. }
  13. fetch('http://example.com', {})
  14. // "GET"
  15. fetch('http://example.com')
  16. // 报错
  17. function fetch(url, { body = '', method = 'GET', headers = {} } = {}) {
  18. console.log(method);
  19. }
  20. fetch('http://example.com')
  21. // "GET"
  • 参数默认值的位置 ```javascript / 例一 function f(x = 1, y) { return [x, y]; }

f() // [1, undefined] f(2) // [2, undefined] f(, 1) // 报错 f(undefined, 1) // [1, 1]

// 例二 function f(x, y = 5, z) { return [x, y, z]; }

f() // [undefined, 5, undefined] f(1) // [1, 5, undefined] f(1, ,2) // 报错 f(1, undefined, 2) // [1, 5, 2]

  1. - 函数的 length 属性
  2. ```javascript
  3. //length属性的返回值,等于函数的参数个数减去指定了默认值的参数个数。
  4. (function (a) {}).length // 1
  5. (function (a = 5) {}).length // 0
  6. (function (a, b, c = 5) {}).length // 2
  • 作用域
    • 一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域(context)。 ```javascript var x = 1;

function f(x, y = x) { console.log(y); }

f(2) // 2

//////// let x = 1;

function f(y = x) { let x = 2; console.log(y); }

f() // 1 ////////////////// var x = 1;

function foo(x = x) { // … }

foo() // ReferenceError: x is not defined

////////////// function f(y = x) { let x = 2; console.log(y); }

f() // ReferenceError: x is not defined

  1. - 应用
  2. ```javascript
  3. function throwIfMissing() {
  4. throw new Error('Missing parameter');
  5. }
  6. function foo(mustBeProvided = throwIfMissing()) {
  7. return mustBeProvided;
  8. }
  9. foo()
  10. // Error: Missing parameter
  11. //表明这个参数可以省略
  12. function foo(optional = undefined) { ··· }

rest 参数

  • 注意,rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。
  • 函数的length属性,不包括 rest 参数。 ```java function add(…values) { let sum = 0;

    for (var val of values) { sum += val; }

    return sum; }

add(2, 5, 3) // 10

// arguments变量的写法 function sortNumbers() { return Array.from(arguments).sort();//arguments对象不是数组,而是一个类似数组的对象。所以为了使用数组的方法,必须使用Array.from先将其转为数组。rest 参数就不存在这个问题, }

// rest参数的写法 const sortNumbers = (…numbers) => numbers.sort();

  1. <a name="jP0nw"></a>
  2. ### 严格模式
  3. ```java
  4. function doSomething(a, b) {
  5. 'use strict';
  6. // code
  7. }
  8. //------------
  9. 'use strict';
  10. function doSomething(a, b = a) {
  11. // code
  12. }
  13. //------------
  14. const doSomething = (function () {
  15. 'use strict';
  16. return function(value = 42) {
  17. return value;
  18. };
  19. }());

name 属性

  1. var f = function () {};
  2. // ES5
  3. f.name // ""
  4. // ES6
  5. f.name // "f"
  6. const bar = function baz() {};
  7. // ES5
  8. bar.name // "baz"
  9. // ES6
  10. bar.name // "baz"
  11. (new Function).name // "anonymous"

箭头函数

  1. var f = () => 5;
  2. // 等同于
  3. var f = function () { return 5 };
  4. var sum = (num1, num2) => num1 + num2;
  5. // 等同于
  6. var sum = function(num1, num2) {
  7. return num1 + num2;
  8. };
  • 由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。 ```java // 报错 let getTempItem = id => { id: id, name: “Temp” };

// 不报错 let getTempItem = id => ({ id: id, name: “Temp” });

  1. - 箭头函数可以与变量解构结合使用。
  2. ```java
  3. const full = ({ first, last }) => first + ' ' + last;
  4. // 等同于
  5. function full(person) {
  6. return person.first + ' ' + person.last;
  7. }
  • 箭头函数的一个用处是简化回调函数。 ```java // 普通函数写法 var result = values.sort(function (a, b) { return a - b; });

// 箭头函数写法 var result = values.sort((a, b) => a - b); ```

使用注意点