温故知新之ES6(一)
温故而知新
最近打算看一些 ES TS 计算机 相关的书籍、翻译一些日常搜集的优秀文章、分享一些工作心得,做一些笔记让自己查漏补缺,也分享给大家。
首先感谢阮一峰老师的《ES6标准入门》,写的博客、书、文章很精彩也很易懂。

1. 基本概念
1.1 是什么❓
ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,已经在2015年6月正式发布了.
1.2 目标是❓
使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。
1.3 ECMAScript 和 JavaScript 的关系
ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的一种实现(另外的 ECMAScript 方言还有 Jscript 和 ActionScript)。
日常场合,这两个词是可以互换的。
1.4 ES6 与 ECMAScript 2015 的关系
• ES6 既是一个历史名词,也是一个泛指,含义是5.1版以后的 JavaScript 的下一代标准,涵盖了ES2015、ES2016、ES2017等等
• ES2015 则是正式名称,特指该年发布的正式版本的语言标准。
2 let 与 const 区别
let
• 1. let声明的变量拥有块级作用域
• 2. let声明的全局变量不是全局对象的属性
• 3. 形如for (let x...)的循环在每次迭代时都为x创建新的绑定。
• 4. 不可以重定义
const: 声明一个只读的常量;
本质
· 1. 指向内存地址的变量不能变
· 2. 基本数据类型【数值、字符串、布尔】不可以变
· 3. 复合类型的数据【数组、对象】,内容可加,只是指针不可改
声明变量的六种方法
· var
· function
· let
· const
· import
· class
•
3. 解构
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。
1. 按照对应位置,对变量赋值。
// 1
let [a, b, c] = [1, 2, 3];
// 2
let [head, ...tail] = [1, 2, 3, 4];
head // 1
tail // [2, 3, 4]
// 3
let [x, y, ...z] = ['a'];
x // "a"
y // undefined 解构不成功,变量的值就等于 undefined 。
z // []
// 4
let [x, y, z] = new Set(['a', 'b', 'c']);
x // "a"
// 5
let [x, y] = [y, x];
2. 默认值
2.1 解构赋值允许指定默认值。
ES6 内部使用严格相等运算符( === ),判断一个位置是否有值。所以,如果一个数组成员不严格等于 undefined ,默认值是不会生效的。
// 1
let [x, y = 'b'] = ['a'];
// x='a', y='b'
// 2 【严格判断】
let [x = 1] = [undefined];
x // 1
// 3 【严格模式】null 不严格等于 undefined 。
let [x = 1] = [null];
x // null
2.2 对象的解构赋值
// 1 字段名与变量名不一致的写法,其实就是更名;
// 真正赋值的是后者,
var { foo: baz } = { foo: 'aaa', bar: 'bbb' };
baz // "aaa"
foo // error: foo is not defined
// 2 完整的对象解构的写法
let { foo: foo, bar: bar } = { foo: "aaa", bar: "bbb" };
// 简写
let { foo, bar } = { foo: "aaa", bar: "bbb" };
// 3 与数组一样,解构也可以用于嵌套结构的对象。
let obj = {
p: [
'Hello',
{ y: 'World' }
]
};
let { p: [x, { y }] } = obj;
x // "Hello"
y // "World"
2.3 字符串的解构赋值
// 1
let {length : len} = 'hello';
len // 5
// 2
const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
2.4 函数解构
// 1
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]
// 2 为函数 move 的参数指定默认值,而不是为变量 x 和 y 指定默认值,所以会得到与前一种写法不同的结果。
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]
// map 解构
var map = new Map();
map.set('first', 'hello');
map.set('second', 'world');
for (let [key, value] of map) {
console.log(key + " is " + value);
}
3 用途
1. 交换变量的值; [x, y] = [y, x]
2. 从函数返回多个值;
3. 函数参数的定义;
4. 提取JSON数据;
5. 函数参数的默认值;
6. 遍历Map结构;
7. 输入模块的指定方法
4. 字符串
JavaScript 共有6种方法可以表示一个字符。
'\z' === 'z' // true
'\172' === 'z' // true
'\x7A' === 'z' // true
'\u007A' === 'z' // true
'\u{7A}' === 'z' // true
1 codePointAt
ES6提供了 codePointAt 方法,能够正确处理4个字节储存的字符,返回一个字符的码点。
对应老的charAt()
var s = '𠮷a';
s.codePointAt(0) // 134071
s.codePointAt(1) // 57271
s.codePointAt(2) // 97
2. String.fromCodePoint()
ES5提供String.fromCharCode 方法,用于从码点返回对应字符,但是这个方法不能识别32位的UTF-16字符(Unicode编号大于 0xFFFF )。
String.fromCodePoint(0x20BB7)
// "𠮷"
String.fromCodePoint(0x78, 0x1f680, 0x79) === 'x\uD83D\uDE80y'
// true
上面代码中,如果 String.fromCodePoint 方法有多个参数,则它们会被合并成一个字符串返回。
注意, fromCodePoint 方法定义在 String 对象上,而 codePointAt 方法定义在字符串的实例对象上。
3. at()
// ES5
'abc'.charAt(0) // "a"
// ES6
'abc'.at(0) // "a"
4. normalize()
ES6 提供字符串实例的 normalize() 方法,用来将字符的不同表示方法统一为同样的形式,这称为 Unicode 正规化。
'\u01D1'==='\u004F\u030C' //false
'\u01D1'.length // 1
'\u004F\u030C'.length // 2
'\u01D1'.normalize() === '\u004F\u030C'.normalize()
// true
5. 其余汇总
1. includes():返回布尔值,表示是否找到了参数字符串。
2. startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
3. endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。
4. repeat(): 表示将原字符串重复 n 次。
5. padStart(): 某个字符串不够指定长度 头部补全
6. padEnd(): 某个字符串不够指定长度 尾部补全
var s = 'Hello world!';
s.startsWith('Hello') // true
s.endsWith('!') // true
s.includes('o') // true
'x'.repeat(3) // "xxx"
'na'.repeat(0) // ""
// 参数 NaN 等同于0。
'na'.repeat(NaN) // ""
'x'.padStart(5, 'ab') // 'ababx'
'x'.padStart(4, 'ab') // 'abax'
'x'.padEnd(5, 'ab') // 'xabab'
'x'.padEnd(4, 'ab') // 'xaba'
5. 数值扩展
1. 二进制和八进制表示法
ES6 提供了二进制和八进制数值的新的写法,分别用前缀 0b (或 0B )和 0o (或 0O )表示。
0b111110111 === 503 // true
0o767 === 503 // true
2. Number 新加方法
1. Number.isFinite(): Number.isFinite() 用来检查一个数值是否为有限的(finite)。
2. Number.isNaN(): Number.isNaN() 用来检查一个值是否为 NaN 。
3. Number.parseInt(): 格式化为int
4. Number.parseFloat(): 格式化为float
5. Number.isInteger() 判断是否是整数
6. 安全整数和Number.isSafeInteger()
JavaScript能够准确表示的整数范围在 -2^53 到 2^53 之间(不含两个端点),超过这个范围,无法精确表示这个值。
Number.isSafeInteger() 则是用来判断一个整数是否落在这个范围之内。
7. Number.EPSILON: 新增极小的常量,float是不准确的,
计算时与该值比较,如果小于,即可,
Number.EPSILON// 2.220446049250313e-16
Number.EPSILON.toFixed(20)
// '0.00000000000000022204'===
function withinErrorMargin (left, right) {
return Math.abs(left - right) < Number.EPSILON;
}
withinErrorMargin(0.1 + 0.2, 0.3)
// true
3. Math对象的扩展
新增方法
1. Math.trunc(): 去除一个数的小数部分,返回整数部分;
2. Math.sign(): 判断一个数到底是正数、负数、还是零。对于非数值,会先将其转换为数值。
参数为正数,返回+1;
参数为负数,返回-1;
参数为0,返回0;
参数为-0,返回-0;
其他值,返回NaN
3. Math.cbrt() 方法用于计算一个数的立方根。
4. Math.clz32() 方法返回一个数的32位无符号整数形式有多少个前导0。
5. Math.imul 方法返回两个数以32位带符号整数形式相乘的结果,返回的也是一个32位的带符号整数。
6. Math.fround方法返回一个数的单精度浮点数形式。
7. Math.hypot 方法返回所有参数的平方和的平方根。
8. Math.expm1(x) 返回ex - 1,即 Math.exp(x) - 1 。
9. Math.expm1(x) 返回ex - 1,即 Math.exp(x) - 1 。
10. Math.log10(x) 返回以10为底的 x 的对数。如果 x 小于0,则返回NaN。
11. Math.log2(x) 返回以2为底的 x 的对数。如果 x 小于0,则返回NaN。
12. ES2016 新增了一个指数运算符( ** )。
2 ** 2 // 4
2 ** 3 // 8
let a = 1.5;
a **= 2;
// 等同于 a = a * a;
a **= 3 // a = a * a * a
6. 函数的扩展
1. 函数参数的默认值【结合解构】
参数默认值不是传值的,而是每次都重新计算默认值表达式的值。也就是说,参数默认值是惰性求值的。
let x = 99;
function foo(p = x + 1) {
console.log(p);
}
foo() // 100
x = 100;
foo() // 101
2. 应用
function throwIfMissing() {
throw new Error('Missing parameter');
}
function foo(mustBeProvided = throwIfMissing()){
return mustBeProvided;
}
foo()
// Error: Missing parameter
3. rest 参数
ES6 引入 rest 参数(形式为 ...变量名 ),用于获取函数的多余参数,这样就不需要使用 arguments
ES6 引入 rest 参数(形式为 ...变量名 ),用于获取函数的多余参数,这样就不需要使用 arguments
4. name 属性
函数的 name 属性,返回该函数的函数名。
function foo() {}
foo.name // "foo"
5. 箭头函数
// 正常函数写法
[1,2,3].map(function (x) {
return x * x;
});
// 箭头函数写法
[1,2,3].map(x => x * x);
箭头函数有几个使用注意点。
(1)函数体内的 this 对象,就是定义时所在的对象,而不是使用时所在的对象。
(2)不可以当作构造函数,也就是说,不可以使用 new 命令,否则会抛出一个错误。
(3)不可以使用 arguments 对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
(4)不可以使用 yield 命令,因此箭头函数不能用作 Generator 函数。
function foo() {
// 箭头函数导致 this 总是指向函数定义生效时所在的对象(本例是 {id: 42} ),所以输出的是 42 。
setTimeout(() => {
console.log('id:', this.id);
}, 100);
}
var id = 21;
foo.call({ id: 42 });
// id: 42
下面函数有几个this ??
function foo() {
return () => {
return () => {
return () => {
console.log('id:', this.id);
};
};
};
}
var f = foo.call({id: 1});
var t1 = f.call({id: 2})()(); // id: 1
var t2 = f().call({id: 3})(); // id: 1
var t3 = f()().call({id: 4}); // id: 1
上面代码之中,只有一个 this ,就是函数 foo 的 this ,所以 t1 、 t2 、 t3 都输出同样的结果。因为所有的内层函数都是箭头函数,都没有自己的 this ,它们的 this 其实都是最外层 foo 函数的 this 。
除了 this ,以下三个变量在箭头函数之中也是不存在的,指向外层函数的对应变量: arguments 、 super 、 new.target 。
7. 绑定 this
箭头函数可以绑定 this 对象,大大减少了显式绑定 this 对象的写法( call 、 apply 、 bind )。但是,箭头函数并不适用于所有场合,所以ES7提出了“函数绑定”(function bind)运算符,用来取代 call 、 apply 、 bind 调用。虽然该语法还是ES7的一个提案,但是Babel转码器已经支持。
函数绑定运算符是并排的两个冒号(::),双冒号左边是一个对象,右边是一个函数。该运算符会自动将左边的对象,作为上下文环境(即this对象),绑定到右边的函数上面。
foo::bar;
// 等同于
bar.bind(foo);
foo::bar(...arguments);
// 等同于
bar.apply(foo, arguments);
const hasOwnProperty = Object.prototype.hasOwnProperty;
function hasOwn(obj, key) {
return obj::hasOwnProperty(key);
}