参考书籍
ES6简介
- 与javascript关系
- 是javascript的规格
- javascirpt是其实现
- 与ES2015关系
- ES6是一个泛指,指5.1版本后的下一代标准,含ES2015 ES2016 ES2017等
- Babel转码器
- ES6代码转ES5
let与const
let
- let声明的变量只在let所在的代码块有效
for循环中和适合用let
for (let i = 0; i < 3; i++) {
let i = 'abc';
console.log(i);
}
// abc
// abc
// abc
//这表明函数内部的变量i与循环变量i不在同一个作用域,有各自单独的作用域。
不存在变量提升 ```javascript // var 的情况 console.log(foo); // 输出undefined var foo = 2;
// let 的情况 console.log(bar); // 报错ReferenceError let bar = 2;
- 暂时性死区
```javascript
var tmp = 123;
if (true) {
tmp = 'abc'; // ReferenceError
let tmp;
}
//隐蔽的死区 是因为参数x默认值等于另一个参数y,而此时y还没有声明,属于“死区”。
function bar(x = y, y = 2) {
return [x, y];
}
bar(); // 报错
总之,在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“**暂时性死 区**”(temporal dead zone,简称 TDZ)
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 循环结束后,它并没有消失,泄露成了全局变量。
- ES6 的块级作用域
- 块级作用域与函数声明 //实现和语法规定有些不一样
<a name="SnFIC"></a>
### Const命令
- 声明一个只读的常量。一旦声明,常量的值就不能改变。
- const的作用域与let命令相同:只在声明所在的块级作用域内有效。
- 本质
- 地址不变,所以在将一个对象声明为常量必须非常小心。如果真的想将对象冻结,应该使用Object.freeze方法。
- ES6 声明变量的六种方法
- var function let const import class
<a name="yKDxh"></a>
### 顶层对象属性
- 在es5中 ,浏览器中顶层对象是windows 所以 a=1 与 windows.a=1 等价
- es6中将 顶层对象与全局变量脱钩,为保持兼容性, var命令和function命令声明的全局变量,依旧是顶层对象的属性;另一方面规定,let命令、const命令、class命令声明的全局变量,不属于顶层对象的属性
<a name="L1mLO"></a>
### globalThis 对象
- 顶层对象在各种实现里面是不统一的。
- 勉强可以在任意实现环境下拿到顶层对象的代码如下
```javascript
// 方法一
(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');
};
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 // []
- 默认值
```javascript
let [foo = true] = [];
foo // true
let [x, y = 'b'] = ['a']; // x='a', y='b'
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”
- 默认值
```javascript
var {x = 3} = {};
x // 3
var {x, y = 5} = {x: 1};
x // 1
y // 5
var {x: y = 3} = {};
y // 3
var {x: y = 3} = {x: 5};
y // 5
var { message: msg = 'Something went wrong' } = {};
msg // "Something went wrong"
var {x = 3} = {x: undefined};
x // 3
var {x = 3} = {x: null};
x // null
- 注意点
let arr = [1, 2, 3];
let {0 : first, [arr.length - 1] : last} = arr;
first // 1
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
<a name="fBREj"></a>
### 数值和布尔值的解构赋值
```javascript
//数值和布尔值的包装对象都有toString属性,因此变量s都能取到值。
let {toString: s} = 123;
s === Number.prototype.toString // true
let {toString: s} = true;
s === Boolean.prototype.toString // true
解构赋值的规则是,只要等号右边的值不是对象或数组,就先将其转为对象。
由于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];
- 能使用的情况
- 可以使用圆括号的情况只有一种:赋值语句的非模式部分,可以使用圆括号。
```javascript
[(b)] = [3]; // 正确
({ p: (d) } = {}); // 正确
[(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]
- 函数参数的默认值
```javascript
jQuery.ajax = function (url, {
async = true,
beforeSend = function () {},
cache = true,
complete = function () {},
crossDomain = false,
global = true,
// ... more config
} = {}) {
// ... do stuff
};
- 遍历 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) { // … }
- 输入模块的指定方法
```javascript
const { SourceMapConsumer, SourceNode } = require("source-map");
字符串扩展
字符的unicode表示方法
//6种表示字符方法
'\z' === 'z' // true
'\172' === 'z' // true
'\x7A' === 'z' // true
'\u007A' === 'z' // true 这种表示法只限于码点在\u0000~\uFFFF之间的字符。
超出这个范围的字符,必须用两个双字节的形式表示。
'\u{7A}' === 'z' // true
//如果直接在\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); } // “𠮷”
<a name="WPxY4"></a>
### 直接输入 U+2028 和 U+2029
- JavaScript 字符串允许直接输入字符,以及输入字符的转义形式
```javascript
'中' === '\u4e2d' // true
JavaScript 规定有5个字符,不能在字符串里面直接使用,只能使用转义形式。
\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
- 模板字符串甚至还能嵌套。
```javascript
const tmpl = addrs => `
<table>
${addrs.map(addr => `
<tr><td>${addr.first}</td></tr>
<tr><td>${addr.last}</td></tr>
`).join('')}
</table>
`;
const data = [
{ first: '<Jane>', last: 'Bond' },
{ first: 'Lars', last: '<Croft>' },
];
console.log(tmpl(data));
// <table>
//
// <tr><td><Jane></td></tr>
// <tr><td>Bond</td></tr>
//
// <tr><td>Lars</td></tr>
// <tr><td><Croft></td></tr>
//
// </table>
let func = (name) => `Hello ${name}!`;
func('Jack') // "Hello Jack!"
模板编译
let template = `
<ul>
<% for(let i=0; i < data.supplies.length; i++) { %>
<li><%= data.supplies[i] %></li>
<% } %>
</ul>
`;
function compile(template){
const evalExpr = /<%=(.+?)%>/g;
const expr = /<%([\s\S]+?)%>/g;
template = template
.replace(evalExpr, '`); \n echo( $1 ); \n echo(`')
.replace(expr, '`); \n $1 \n echo(`');
template = 'echo(`' + template + '`);';
let script =
`(function parse(data){
let output = "";
function echo(html){
output += html;
}
${ template }
return output;
})`;
return script;
}
let parse = eval(compile(template));
div.innerHTML = parse({ supplies: [ "broom", "mop", "cleaner" ] });
// <ul>
// <li>broom</li>
// <li>mop</li>
// <li>cleaner</li>
// </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”
<a name="v3aOK"></a>
### 模板字符串的限制
<br />- ES2018 [放松](https://tc39.github.io/proposal-template-literal-revision/)了对标签模板里面的字符串转义的限制。如果遇到不合法的字符串转义,就返回undefined,而不是报错,并且从raw属性上面可以得到原始字符串。
<a name="CyiZo"></a>
## 字符串新增方法
<a name="q3tev"></a>
### String.fromCodePoint
- String.fromCharCode()不能识别大于0xFFFF的码点,所以0x20BB7就发生了溢出,最高位2被舍弃了
- ES6 提供了String.fromCodePoint()方法,可以识别大于0xFFFF的字符
<a name="wB6ff"></a>
### String.raw
```javascript
String.raw`Hi\n${2+3}!`
// 实际返回 "Hi\\n5!",显示的是转义后的结果 "Hi\n5!"
String.raw`Hi\\n`
// 返回 "Hi\\\\n"
String.raw`Hi\\n` === "Hi\\\\n" // true
// `foo${1 + 2}bar`
// 等同于
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
<a name="ozyXQ"></a>
### 实例方法:normalize()
```javascript
// O(\u004F)和ˇ(\u030C)合成Ǒ(\u004F\u030C)
但js不支持
'\u01D1'==='\u004F\u030C' //false
'\u01D1'.length // 1
'\u004F\u030C'.length // 2
ES6 提供字符串实例的normalize()方法,用来将字符的不同表示方法统一为同样的形式,这称为 Unicode 正规化。
'\u01D1'.normalize() === '\u004F\u030C'.normalize()
// true
//有不同标准的
'\u004F\u030C'.normalize('NFC').length // 1
'\u004F\u030C'.normalize('NFD').length // 2
includes, startsWith,endsWith
let s = 'Hello world!';
s.startsWith('world', 6) // true
s.endsWith('Hello', 5) // true
s.includes('Hello', 6) // false
repeat
padsStart padsEnd
trimStart trimEnd
matchAll
replaceAll
正则扩展
RegExp 构造函数
var regex = new RegExp('xyz', 'i');
// 等价于
var regex = /xyz/i;
var regex = new RegExp(/xyz/i);
// 等价于
var regex = /xyz/i;
字符串的正则方法
String.prototype.match 调用 RegExp.prototype[Symbol.match]
String.prototype.replace 调用 RegExp.prototype[Symbol.replace]
String.prototype.search 调用 RegExp.prototype[Symbol.search]
String.prototype.split 调用 RegExp.prototype[Symbol.split]
u修饰符
/^\uD83D/u.test('\uD83D\uDC2A') // false
/^\uD83D/.test('\uD83D\uDC2A') // true
- 点字符 ```javascript var s = ‘𠮷’;
/^.$/.test(s) // false /^.$/u.test(s) // true
- **Unicode 字符表示法**
```javascript
/\u{61}/.test('a') // false
/\u{61}/u.test('a') // true
/\u{20BB7}/u.test('𠮷') // true
量词
/a{2}/.test('aa') // true
/a{2}/u.test('aa') // true
/𠮷{2}/.test('𠮷𠮷') // false
/𠮷{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
- **i 修饰符**
```javascript
/[a-z]/i.test('\u212A') // false
/[a-z]/iu.test('\u212A') // true
- 转义
RegExp.prototype.unicode 属性
//正则实例对象新增unicode属性,表示是否设置了u修饰符。
const r1 = /hello/;
const r2 = /hello/u;
r1.unicode // false
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。
<a name="CntJ7"></a>
### RegExp.prototype.sticky 属性
```javascript
//ES6 的正则实例对象多了sticky属性,表示是否设置了y修饰符。
var r = /hello\d/y;
r.sticky // true
RegExp.prototype.flags 属性
// ES5 的 source 属性
// 返回正则表达式的正文
/abc/ig.source
// "abc"
// ES6 的 flags 属性
// 返回正则表达式的修饰符
/abc/ig.flags
// 'gi'
s 修饰符:dotAll 模式
// . 除了个字节的 UTF-16 字符和终止符都可以表示
//ES2018 引入s修饰符,使得.可以匹配任意单个字符。
const re = /foo.bar/s;
// 另一种写法
// const re = new RegExp('foo.bar', 's');
re.test('foo\nbar') // true
re.dotAll // true
re.flags // 's'
后行断言
Unicode 属性类
ES2018 引入了一种新的类的写法\p{...}和\P{...},允许正则表达式匹配符合 Unicode 某种属性的所有字符。
const regexGreekSymbol = /\p{Script=Greek}/u;
regexGreekSymbol.test('π') // true
// 匹配所有空格
\p{White_Space}
// 匹配各种文字的所有字母,等同于 Unicode 版的 \w
[\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}]
// 匹配各种文字的所有非字母的字符,等同于 Unicode 版的 \W
[^\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}]
// 匹配 Emoji
/\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation}|\p{Emoji}\uFE0F/gu
// 匹配所有的箭头字符
const regexArrows = /^\p{Block=Arrows}+$/u;
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
<a name="xU6iJ"></a>
### 解构赋值和替换
```javascript
let {groups: {one, two}} = /^(?<one>.*):(?<two>.*)$/u.exec('foo:bar');
one // foo
two // bar
let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
'2015-01-02'.replace(re, '$<day>/$<month>/$<year>')
// '02/01/2015'
'2015-01-02'.replace(re, (
matched, // 整个匹配结果 2015-01-02
capture1, // 第一个组匹配 2015
capture2, // 第二个组匹配 01
capture3, // 第三个组匹配 02
position, // 匹配开始的位置 0
S, // 原字符串 2015-01-02
groups // 具名组构成的一个对象 {year, month, day}
) => {
let {day, month, year} = groups;
return `${day}/${month}/${year}`;
});
引用
- 如果要在正则表达式内部引用某个“具名组匹配”,可以使用\k<组名>的写法。
```javascript
const RE_TWICE = /^(?
[a-z]+)!\k $/; RE_TWICE.test(‘abc!abc’) // true RE_TWICE.test(‘abc!ab’) // false
const RE_TWICE = /^(?
<a name="fm02T"></a>
### 正则匹配索引
```javascript
const text = 'zabbcdef';
const re = /ab/;
const result = re.exec(text);
result.index // 1
result.indices // [ [1, 3] ]
const re = /ab+(cd)/;
const result = re.exec(text);
result.indices // [ [ 1, 6 ], [ 4, 6 ] ]
String.prototype.matchAll()
///ES 2020 提供了这个方法
const string = 'test1test2test3';
const regex = /t(e)(st(\d?))/g;
for (const match of string.matchAll(regex)) {
console.log(match);
}
// ["test1", "e", "st1", "1", index: 0, input: "test1test2test3"]
// ["test2", "e", "st2", "2", index: 5, input: "test1test2test3"]
// ["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
<a name="kPOY2"></a>
### 数值分隔符
- 欧美语言中,较长的数值允许每三位添加一个分隔符(通常是一个逗号),增加数值的可读性。比如,1000可以写作1,000
- [ES2021](https://github.com/tc39/proposal-numeric-separator),允许 JavaScript 的数值使用下划线(_)作为分隔符。
- 下面三个将字符串转成数值的函数,不支持数值分隔符。
- Number()
- parseInt()
- parseFloat()
<a name="EY19p"></a>
### Number.isFinite(), Number.isNaN()
```javascript
isFinite(25) // true
isFinite("25") // true
Number.isFinite(25) // true
Number.isFinite("25") // false
isNaN(NaN) // true
isNaN("NaN") // true
Number.isNaN(NaN) // true
Number.isNaN("NaN") // false
Number.isNaN(1) // false
Number.parseInt(), Number.parseFloat()
// ES5的写法
parseInt('12.34') // 12
parseFloat('123.45#') // 123.45
// ES6的写法
Number.parseInt('12.34') // 12
Number.parseFloat('123.45#') // 123.45
Number.isInteger()
//精度有限
Number.isInteger(3.0000000000000002) // true
Number.isInteger(5E-324) // false
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
<a name="Mq61p"></a>
### 安全整数和 Number.isSafeInteger()
- JavaScript 能够准确表示的整数范围在-2^53到2^53之间
- ES6 引入了Number.MAX_SAFE_INTEGER和Number.MIN_SAFE_INTEGER这两个常量,
```javascript
Number.MAX_SAFE_INTEGER === Math.pow(2, 53) - 1
// true
Number.MAX_SAFE_INTEGER === 9007199254740991
// true
Number.MIN_SAFE_INTEGER === -Number.MAX_SAFE_INTEGER
// true
Number.MIN_SAFE_INTEGER === -9007199254740991
// 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
- 与解构赋值默认值结合使用
```javascript
function foo({x, y = 5}) {
console.log(x, y);
}
foo({}) // undefined 5
foo({x: 1}) // 1 5
foo({x: 1, y: 2}) // 1 2
foo() // TypeError: Cannot read property 'x' of undefined
function fetch(url, { body = '', method = 'GET', headers = {} }) {
console.log(method);
}
fetch('http://example.com', {})
// "GET"
fetch('http://example.com')
// 报错
function fetch(url, { body = '', method = 'GET', headers = {} } = {}) {
console.log(method);
}
fetch('http://example.com')
// "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]
- 函数的 length 属性
```javascript
//length属性的返回值,等于函数的参数个数减去指定了默认值的参数个数。
(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(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
- 应用
```javascript
function throwIfMissing() {
throw new Error('Missing parameter');
}
function foo(mustBeProvided = throwIfMissing()) {
return mustBeProvided;
}
foo()
// Error: Missing parameter
//表明这个参数可以省略
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();
<a name="jP0nw"></a>
### 严格模式
```java
function doSomething(a, b) {
'use strict';
// code
}
//------------
'use strict';
function doSomething(a, b = a) {
// code
}
//------------
const doSomething = (function () {
'use strict';
return function(value = 42) {
return value;
};
}());
name 属性
var f = function () {};
// ES5
f.name // ""
// ES6
f.name // "f"
const bar = function baz() {};
// ES5
bar.name // "baz"
// ES6
bar.name // "baz"
(new Function).name // "anonymous"
箭头函数
var f = () => 5;
// 等同于
var f = function () { return 5 };
var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {
return num1 + num2;
};
- 由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。 ```java // 报错 let getTempItem = id => { id: id, name: “Temp” };
// 不报错 let getTempItem = id => ({ id: id, name: “Temp” });
- 箭头函数可以与变量解构结合使用。
```java
const full = ({ first, last }) => first + ' ' + last;
// 等同于
function full(person) {
return person.first + ' ' + person.last;
}
- 箭头函数的一个用处是简化回调函数。 ```java // 普通函数写法 var result = values.sort(function (a, b) { return a - b; });
// 箭头函数写法 var result = values.sort((a, b) => a - b); ```