ES6 新特性
ES6, 全称 ECMAScript 6.0 ,2015.06 发版。ES6 主要是为了解决 ES5 的先天不足,比如 JavaScript 里并没有类的概念,但是目前浏览器的 JavaScript 是 ES5 版本,大多数高版本的浏览器也支持 ES6,不过只实现了 ES6 的部分特性和功能。
平时项目开发也用得很久,现在整体复习下。
1、let、const、block作用域
let 允许创建块级作用域(最靠近的一个花括号内有效),不具备变量提升,不允许重复声明:
// 块级作用域 { let a = 1 console.log(a) // 1 } console.log(a) // Uncaught ReferenceError: a is not defined // 变量不提升 { console.log(a) // Uncaught ReferenceError: Cannot access 'a' before initialization let a = 1 } // 不能重复声明 { let a = 1 let a = 2 // Uncaught SyntaxError: Identifier 'a' has already been declared }
const 允许创建块级作用域(最靠近的一个花括号内有效)、变量声明不提升、const 在声明时必须被赋值、声明时大写变量(默认规则):
// 块级作用域 { const A = 1 console.log(A) } console.log(A) // Uncaught ReferenceError: A is not defined // 变量不提升 { console.log(A) // Uncaught ReferenceError: Cannot access 'A' before initialization const A = 1 } // 声明时必须赋值 { const A // Uncaught SyntaxError: Missing initializer in const declaration }
2、箭头函数
ES6 中,箭头函数就是函数的一种简写形式,使用括号包裹参数,跟随一个 =>,紧接着是函数体:
// ES5写法 var getPrice = function() { return 4.55; }; // ES6箭头函数写法,不需要return语句 var getPrice = () => 4.55;
箭头函数不具备this变量指向改变:
// ES5 function Person() { this.age = 0; setInterval(function growUp() { // 在非严格模式下,growUp() 函数的 this 指向 window 对象 this.age++; }, 1000); } var person = new Person(); // ES6 function Person(){ this.age = 0; setInterval(() => { // |this| 指向 person 对象 this.age++; }, 1000); } var person = new Person();
3、函数参数默认值
ES6 中允许你对函数参数设置默认值:
// ES5 var getFinalPrice = function (price, tax) { tax = tax || 0.7 return price + price * tax; } // ES6 var getFinalPrice = function (price, tax = 0.7) { return price + price * tax; }
4、Spread/Rest操作符
Spread / Rest 操作符指的是运算符...:
// Spread,可以简单理解为分散 function foo(x,y,z) { console.log(x,y,z); } let arr = [1,2,3]; foo(...arr); // 1 2 3 // Rest,可以简单理解为合并 function foo(...args) { console.log(args); } foo( 1, 2, 3, 4, 5); // [1, 2, 3, 4, 5]
5、对象词法扩展
ES6 允许声明在对象字面量时使用简写语法,来初始化属性变量和函数的定义方法,并且允许在对象属性中进行计算操作:
function getCar(make, value) { return { // 简写变量 make, // 等同于 make: make value, // 等同于 value: value // 属性可以使用表达式计算值 ['make' + make]: true, // 忽略 `function` 关键词简写对象函数 depreciate() { this.value -= 2500; }, // ES5写法 depreciateBak: function () { console.log(this['make' + this.make]) } } };
6、二进制和八进制字面量
ES6 支持二进制和八进制的字面量,通过在数字前面添加 0o 或者0O 即可将其转换为八进制值:
let oValue = 0o10; console.log(oValue); // 8 let bValue = 0b10; // 二进制使用 `0b` 或者 `0B` console.log(bValue); // 2
7、对象和数组的结构
解构可以避免在对象赋值时产生中间变量:
let obj = { a: 1, b: 2, c: 3 } let { a: a1, b: b1, c: c1 } = obj console.log(a1, b1, c1) // 1 2 3 // let { a, b, c } = obj // 等同上面 // console.log(a, b, c) // 1 2 3 let arr = [1, 2, 3] let [d, e, f] = arr console.log(d, e, f) // 1 2 3
8、对象超类
ES6 允许在对象中使用 super 方法:
var parent = { foo() { console.log("Hello from the Parent"); } } var child = { foo() { super.foo(); console.log("Hello from the Child"); } } Object.setPrototypeOf(child, parent); // child.__proto__ = parent child.foo(); // Hello from the Parent // Hello from the Child
9、模板语法和分隔符
一种十分简洁的方法组装一堆字符串和变量。
let user = 'Barret'; // ``作为分隔符,${ ... }用来渲染一个变量 console.log(`Hi ${user}!`); // Hi Barret!
10、for...of VS for...in
for...of 用于遍历一个迭代器,如数组:
let nicknames = ['di', 'boo', 'punkeye']; nicknames.size = 3; for (let nickname of nicknames) { console.log(nickname); } // 结果: di, boo, punkeye
for...in 用来遍历对象中的属性(只能访问可枚举的):
let nicknames = ['di', 'boo', 'punkeye']; nicknames.size = 3; for (let nickname in nicknames) { console.log(nickname); } // 0, 1, 2, size
11、Map VS WeakMap
ES6 中两种新的数据结构集:Map 和 WeakMap。事实上每个对象都可以看作是一个 Map。
一个对象由多个 key-val 对构成,在 Map 中,任何类型都可以作为对象的 key,如:
var myMap = new Map(); var keyString = "a string", keyObj = {}, keyFunc = function () {}; // 设置值 myMap.set(keyString, "value 与 'a string' 关联"); myMap.set(keyObj, "value 与 keyObj 关联"); myMap.set(keyFunc, "value 与 keyFunc 关联"); myMap.size; // 3 // 获取值 myMap.get(keyString); // "value 与 'a string' 关联" myMap.get(keyObj); // "value 与 keyObj 关联" myMap.get(keyFunc); // "value 与 keyFunc 关联"
WeakMap 就是一个 Map,只不过它的所有 key 都是弱引用,意思就是 WeakMap 中的东西垃圾回收时不考虑,使用它不用担心内存泄漏问题。
另一个需要注意的点是,WeakMap 的所有 key 必须是对象。它只有四个方法 delete(key),has(key),get(key) 和set(key, val):
let w = new WeakMap(); w.set('a', 'b'); // Uncaught TypeError: Invalid value used as weak map key var o1 = {}, o2 = function(){}, o3 = window; w.set(o1, 37); w.set(o2, "azerty"); w.set(o3, undefined); w.get(o3); // undefined, because that is the set value w.has(o1); // true w.delete(o1); w.has(o1); // false
12、Set VS WeakSet
Set 对象是一组不重复的值,重复的值将被忽略,值类型可以是原始类型和引用类型:
let mySet = new Set([1, 1, 2, 2, 3, 3]); mySet.size; // 3 mySet.has(1); // true mySet.add('strings'); mySet.add({ a: 1, b:2 });
基于Set对象的值不重复,即有一种非常方便的数组去重方法:
// 得到Set格式的值 let mySet = new Set([1, 1, 2, 2, 3, 3]); // mySet => Set(3) {1, 2, 3} // Object.prototype.toString.call(mySet) // [object Set] let toArray = Array.from(mySet) // [1, 2, 3] Object.prototype.toString.call(toArray) // [object Array]
可以通过 forEach 和 for...of 来遍历 Set 对象:
mySet.forEach((item) => { console.log(item); // 1 // 2 // 3 // 'strings' // Object { a: 1, b: 2 } }); for (let value of mySet) { console.log(value); // 1 // 2 // 3 // 'strings' // Object { a: 1, b: 2 } }
Set 同样有 delete() 和 clear() 方法。
类似于 WeakMap,WeakSet 对象可以让你在一个集合中保存对象的弱引用,在 WeakSet 中的对象只允许出现一次:
var ws = new WeakSet(); var obj = {}; var foo = {}; ws.add(window); ws.add(obj); ws.has(window); // true ws.has(foo); // false, foo 没有添加成功 ws.delete(window); // 从结合中删除 window 对象 ws.has(window); // false, window 对象已经被删除
13、类
ES6 中有 class 语法。值得注意是,这里的 class 不是新的对象继承模型,它只是原型链的语法糖表现形式。
函数中使用 static 关键词定义构造函数的的方法和属性:
class Task { constructor() { console.log("task instantiated!"); } showId() { console.log(23); } static loadAll() { console.log("Loading all tasks.."); } } console.log(typeof Task); // function let task = new Task(); // "task instantiated!" task.showId(); // 23 Task.loadAll(); // "Loading all tasks.."
类中的继承和超集:
class Car { constructor() { console.log("Creating a new car"); } call(val) { console.log('parent --->', val) } } class Porsche extends Car { constructor() { super(); console.log("Creating Porsche"); super.call('inner') } call2(val) { super.call(val) } } let c = new Porsche(); c.call2('outer') // Creating a new car // Creating Porsche // parent ---> inner // parent ---> outer
extends 允许一个子类继承父类,需要注意的是,子类的constructor 函数中需要执行 super() 函数。
当然,你也可以在子类方法中调用父类的方法,如super.parentMethodName()。
有几点值得注意的是:
- 类的声明不会提升(hoisting),如果你要使用某个 Class,那你必须在使用之前定义它,否则会抛出一个 ReferenceError 的错误
- 在类中定义函数不需要使用 function 关键词
14、Symbol
Symbol 是一种新的数据类型,它的值是唯一的,不可变的。ES6 中提出 symbol 的目的是为了生成一个唯一的标识符,不过你访问不到这个标识符:
var sym = Symbol( "some optional description" ); console.log(typeof sym); // symbol
注意,这里 Symbol 前面不能使用 new 操作符。
如果它被用作一个对象的属性,那么这个属性会是不可枚举的:
// 因为Symbol的值是唯一的 Symbol(1) == Symbol(1) // false let ran = Symbol("random") var o = { val: 10, [ran]: "I'm a symbol", }; console.log(Object.getOwnPropertyNames(o)) // val console.log(o[ran]) // "I'm a symbol"
15、迭代器(Iterators)
迭代器允许每次访问数据集合的一个元素,当指针指向数据集合最后一个元素是,迭代器便会退出。它提供了 next() 函数来遍历一个序列,这个方法返回一个包含 done 和 value 属性的对象。
ES6 中可以通过 Symbol.iterator 给对象设置默认的遍历器,无论什么时候对象需要被遍历,执行它的 @@iterator 方法便可以返回一个用于获取值的迭代器。
数组默认就是一个迭代器:
var arr = [11,12,13]; var itr = arr[Symbol.iterator](); itr.next(); // { value: 11, done: false } itr.next(); // { value: 12, done: false } itr.next(); // { value: 13, done: false } itr.next(); // { value: undefined, done: true }
可以通过 [Symbol.iterator]() 自定义一个对象的迭代器。
16、Generators
Generator 函数是 ES6 的新特性,它允许一个函数返回的可遍历对象生成多个值(返回的值为一个迭代器对象)。
在使用中你会看到 * 语法和一个新的关键词 yield:
function *infiniteNumbers() { var n = 1; while (true){ yield n++; } } var numbers = infiniteNumbers(); // returns an iterable object numbers.next(); // { value: 1, done: false } numbers.next(); // { value: 2, done: false } numbers.next(); // { value: 3, done: false }
每次执行 yield 时,返回的值变为迭代器的下一个值。
17、Promise
ES6 对 Promise 有了原生的支持,一个 Promise 是一个等待被异步执行的对象,当它执行完成后,其状态会变成 resolved 或者rejected。
var p = new Promise(function(resolve, reject) { if (/* condition */) { // fulfilled successfully resolve(/* value */); } else { // error, rejected reject(/* reason */); } }); p.then((val) => console.log("Promise Resolved", val), (err) => console.log("Promise Rejected", err));
18、Proxy 与 Reflect
Proxy 可以对目标对象的读取、函数调用等操作进行拦截,然后进行操作处理。它不直接操作对象,而是像代理模式,通过对象的代理对象进行操作,在进行这些操作时,可以添加一些需要的额外操作。
Reflect 可以用于获取目标对象的行为,它与 Object 类似,但是更易读,为操作对象提供了一种更优雅的方式。它的方法与 Proxy 是对应的。
Proxy详情,点这里。
Reflect详情,点这里。
19、字符串
ES6 对字符串操作方法的扩展。
- includes():返回布尔值,判断是否找到参数字符串。
- startsWith():返回布尔值,判断参数字符串是否在原字符串的头部。
- endsWith():返回布尔值,判断参数字符串是否在原字符串的尾部。
- repeat():返回新的字符串,表示将字符串重复指定次数返回。
- padStart:返回新的字符串,表示用参数字符串从头部(左侧)补全原字符串。
- padEnd:返回新的字符串,表示用参数字符串从尾部(右侧)补全原字符串。
20、模块
在 ES6 前, 实现模块化使用的是 RequireJS 或者 seaJS(分别是基于 AMD 规范的模块化库, 和基于 CMD 规范的模块化库)。
ES6 引入了模块化,其设计思想是在编译时就能确定模块的依赖关系,以及输入和输出的变量。
ES6 的模块化分为导出(export) @与导入(import)两个模块。
export 与 import:
- 导出的函数声明与类声明必须要有名称(export default 命令另外考虑)。
- 不仅能导出声明还能导出引用(例如函数)。
- export 命令可以出现在模块的任何位置,但必需处于模块顶层。
- import 命令会提升到整个模块的头部,首先执行。
as的用法:
export 命令导出的接口名称,须和模块内部的变量有一一对应关系。导入的变量名,须和导出的接口名称相同,即顺序可以不一致。使用 as 重新定义导出的接口名称,隐藏模块内部的变量
export default 命令:
- 在一个文件或模块中,export、import 可以有多个,export default 仅有一个。
- export default 中的 default 是对应的导出接口变量。
- 通过 export 方式导出,在导入时要加{ },export default 则不需要。
- export default 向外暴露的成员,可以使用任意变量来接收。
21、async / await
async 是 ES7 才有的与异步操作有关的关键字,和 Promise , Generator 有很大关联的。
async 函数返回一个 Promise 对象,可以使用 then 方法添加回调函数。
async 函数中可能会有 await 表达式,async 函数执行时,如果遇到 await 就会先暂停执行 ,等到触发的异步操作完成后,恢复 async 函数的执行并返回解析值。
await 关键字只在 async 函数内有效,否则会报错。await 返回 Promise 对象的处理结果,如果等待的不是 Promise 对象,则返回该值本身。如果一个 Promise 被传递给一个 await 操作符,await 将等待 Promise 正常处理完成并返回其处理结果。
await针对所跟不同表达式的处理方式:
- Promise 对象:await 会暂停执行,等待 Promise 对象 resolve,然后恢复 async 函数的执行并返回解析值。
- 非 Promise 对象:直接返回对应的值。
原址菜鸟:https://www.runoob.com/w3cnote/es6-concise-tutorial.html