一、 let、const 和 var let和const只在当前块级作用域中有效 const用来声明常量 var是全局作用域有效的 constants.js 模块 export const A = 1; export const B = 3; export const C = 4; test1.js 模块 import * as constants from './constants'; console.log(constants.A); // 1 console.log(constants.B); // 3 test2.js 模块 import {A, B} from './constants'; console.log(A); // 1 console.log(B); // 3 全局对象的属性 (下面是等价的) window.a = 1 var a = 1 global.a = 1
二、 解构赋值: ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring) 如: var [a, b, c] = [1, 2, 3]; a = 1 b = 2 c = 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 [foo] = 1; let [foo] = false; let [foo] = NaN; let [foo] = undefined; let [foo] = null; let [foo] = {}; ES6内部使用严格相等运算符(===),判断一个位置是否有值。所以,如果一个数组的员不严格等于undefined,默认值是不会生效的 var [x = 1] = [undefined]; x // 1 var [x = 1] = [null]; x // null 对象解构赋值中如果变量名与属性名不一致,必须写成下面这样 下面真正被赋值的是baz var { foo: baz } = { foo: "aaa", bar: "bbb" }; baz // "aaa" foo // error: foo is not defined foo是模式 let obj = { first: 'hello', last: 'world' }; let { first: f, last: l } = obj; f // 'hello' l // 'world' 解构赋值的规则是,只要等号右边的值不是对象,就先将其转为对象。由于undefined和null无法转为对象,所以对它们进行解构赋值,都会报错 let { prop: x } = undefined; // TypeError let { prop: y } = null; // TypeError 注: 模式不能带有圆括号
三、字符串的扩展 模板字符串, 用反引号标示普通字符串或者多行字符串, 使用反引号需要加反斜杠 传统的写法 $("#result").append( "There are <b>" + basket.count + "</b> " + "items in your basket, " + "<em>" + basket.onSale + "</em> are on sale!" ); 模板字符串写法 $("#result").append(` There are <b>${basket.count}</b> items in your basket, <em>${basket.onSale}</em> are on sale! `);
四、数组的扩展 Array.from() 将类数组转换成数组 Array.from('hello') // ['h', 'e', 'l', 'l', 'o'] Array.of() 将一组值转换为数组 Array.of(3, 11, 8) // [3,11,8] 只有当参数个数不少于2个时,Array()才会返回由参数组成的新数组。参数个数只有一个时,实际上是指定数组的长度 Array() // [] Array(3) // [, , ,] Array(3, 11, 8) // [3, 11, 8]
五、函数的扩展 rest参数,获取函数多余的参数 function add(...values) { let sum = 0; for (var val of values) { sum += val; } return sum; } add(2, 5, 3) // 10 扩展运算符(...), 将一个数据转为用逗号分隔的参数序列 function add(x, y) { return x + y; } var numbers = [4, 38]; add(...numbers) // 42 替代数组的apply方法 : 由于扩展运算符可以展开数组,所以不再需要apply方法,将数组转为函数的参数了 // ES5的写法 function f(x, y, z) { // ... } var args = [0, 1, 2]; f.apply(null, args); // ES6的写法 function f(x, y, z) { // ... } var args = [0, 1, 2]; f(...args); 箭头函数都没有自己的this, this表示的是最外层的 :: 双冒号左边是对象,右边是函数
六、对象的扩展 Object.is(,) 比较2个值是否严格相等,与(===)行为基本一致 不同之处为:Object.is的 +0不等于-0, Nan等于自身 Object.assign 用于对象的合并,同名后面的会覆盖前面 是浅拷贝,而不是深拷贝 var obj1 = {a: {b: 1}}; var obj2 = Object.assign({}, obj1); obj1.a.b = 2; obj2.a.b // 2
七、二进制数组 用ArrayBuffer对象、TypeArray视图和DataView视图操作JS的二进制数据, 直接操作内存数据 ArrayBuffer与字符串的相互转换 // ArrayBuffer转为字符串,参数为ArrayBuffer对象 function ab2str(buf) { return String.fromCharCode.apply(null, new Uint16Array(buf)); } // 字符串转为ArrayBuffer对象,参数为字符串 function str2ab(str) { var buf = new ArrayBuffer(str.length * 2); // 每个字符占用2个字节 var bufView = new Uint16Array(buf); for (var i = 0, strLen = str.length; i < strLen; i++) { bufView[i] = str.charCodeAt(i); } return buf; }
八、 Set和Map Set类似数组, 它的成员是唯一的,里面没有重复的值 var set = new Set([1, 2, 3, 4, 4] set.add(5) [...set] // [1, 2, 3, 4, 5] var items = new Set([1, 2, 3, 4, 5, 5, 5, 5]); items.size // 5 Set的遍历 keys():返回一个键名的遍历器 values():返回一个键值的遍历器 entries():返回一个键值对的遍历器 forEach():使用回调函数遍历每个成员 let set = new Set(['red', 'green', 'blue']); for ( let item of set.keys() ){ console.log(item); } // red // green // blue for ( let item of set.values() ){ // 可以省略values console.log(item); } // red // green // blue for ( let item of set.entries() ){ console.log(item); } // ["red", "red"] // ["green", "green"] // ["blue", "blue"] 数组的map和filter方法也可以用于Set了 let set = new Set([1, 2, 3]); set = new Set([...set].map(x => x * 2)); // [...set].map映射新的结构 // 上面的代码可用: new Set(Array.from(set, val => val * 2)) // Array.from映射新的结构 // 返回Set结构:{2, 4, 6} let set = new Set([1, 2, 3, 4, 5]); set = new Set([...set].filter(x => (x % 2) == 0)); // 返回Set结构:{2, 4} Set结构的实例的forEach方法,用于对每个成员执行某种操作,没有返回值 let set = new Set([1, 2, 3]); set.forEach((value, key) => console.log(value * 2) ) // 2 // 4 // 6 JS的Object只把字符串当作键 字符串--值, 而Map不限于字符串作键可以是各种类型 值--值 var m = new Map(); var o = {p: "Hello World"}; m.set(o, "content") m.get(o) // "content" m.has(o) // true m.delete(o) // true m.has(o) // false Map结构转数组结构 let map = new Map([ [1, 'one'], [2, 'two'], [3, 'three'], ]); [...map.keys()] // [1, 2, 3] [...map.values()] // ['one', 'two', 'three'] [...map.entries()] // [[1,'one'], [2, 'two'], [3, 'three']] [...map] // [[1,'one'], [2, 'two'], [3, 'three']]
九、Iterator(遍历器): 为各种不同的数据结构提供统一的访问机制,任何部署Iterator接口的数据结构们都有遍历操作 部署Iterator接口: 只有三类结构原生具有Iterator接口数组、类数组对象以及Set和Map 一个对象如果要有可被for...of循环调用的Iterator接口,就必须在Symbol.iterator的属性上部署遍历器生成方法(原型链上的对象具有该方法也可) class RangeIterator { constructor(start, stop) { this.value = start; this.stop = stop; } [Symbol.iterator]() { return this; } next() { var value = this.value; if (value < this.stop) { this.value++; return {done: false, value: value}; } else { return {done: true, value: undefined}; } } } function range(start, stop) { return new RangeIterator(start, stop); } for (var value of range(0, 3)) { console.log(value); } Symbol.iterator方法的最简单实现 var myIterable = {}; myIterable[Symbol.iterator] = function* () { yield 1; yield 2; yield 3; }; [...myIterable] // [1, 2, 3] // 或者采用下面的简洁写法 let obj = { * [Symbol.iterator]() { yield 'hello'; yield 'world'; } }; for (let x of obj) { console.log(x); } // hello // world for...of循环内部调用的是数据结构的Symbol.iterator方法, 是遍历所有数据结构的统一的方法 for...of循环可以代替数组实例的forEach方法 JavaScript原有的for...in循环,只能获得对象的键名,不能直接获取键值。ES6提供for...of循环,允许遍历获得键值
十、 Generator 用来处理异步操作 返回遍历对象 调用next执行yield定义的下一条,返回出对象, 直到value为undefined, done的值为true function* helloWorldGenerator() { yield 'hello'; yield 'world'; return 'ending'; } var hw = helloWorldGenerator(); hw.next(); // ()里面可以带参数 利用Generator函数,可以在任意对象上部署iterator接口 function* iterEntries(obj) { let keys = Object.keys(obj); for (let i=0; i < keys.length; i++) { let key = keys[i]; yield [key, obj[key]]; } } let myObj = { foo: 3, bar: 7 }; for (let [key, value] of iterEntries(myObj)) { console.log(key, value); } // foo 3 // bar 7
十一、Promise 是异步编程的一种解决方案 简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果 三种状态:Pending(进行中)、Resolved(已完成,又称Fulfilled)和Rejected(已失败) var promise = new Promise(function(resolve, reject) { // ... some code if (// 异步操作成功){ resolve(value); } else { reject(error); } }); Promise实例生成以后,可以用then方法分别指定Resolved状态和Reject状态的回调函数, 第二个函数参数是可选的 promise.then(function(value) { // success }, function(value) { // failure }); 示例: function timeout(ms) { return new Promise((resolve, reject) => { setTimeout(resolve, ms, 'done'); }); } timeout(100).then((value) => { console.log(value); }); Promise新建后就会立即执行,then方法是在当前脚本所有同步任务执行完成后再执行 Promise对象实现Ajax操作示例: var getJSON = function(url) { var promise = new Promise(function(resolve, reject){ var client = new XMLHttpRequest(); client.open("GET", url); client.onreadystatechange = handler; client.responseType = "json"; client.setRequestHeader("Accept", "application/json"); client.send(); function handler() { if ( this.readyState !== 4 ) { return; } if (this.status === 200) { resolve(this.response); } else { reject(new Error(this.statusText)); } }; }); return promise; }; getJSON("/posts.json").then(function(json) { console.log('Contents: ' + json); }, function(error) { console.error('出错了', error); });
十二、 异步操作和Async(ES7) JS异步编程方法: 回调函数 事件监听 发布/订阅 Promise 对象 async 函数的实现,就是将 Generator 函数和自动执行器,包装在一个函数里 // 50毫秒后输出“hello world” function timeout(ms) { return new Promise((resolve) => { setTimeout(resolve, ms); }); } async function asyncPrint(value, ms) { await timeout(ms); console.log(value) } asyncPrint('hello world', 50); Async的使用方式 // 函数声明 async function foo() {} // 函数表达式 const foo = async function () {}; // 对象的方法 let obj = { async foo() {} } // 箭头函数 const foo = async () => {}; 最好把await命令放在try...catch代码 async function myFunction() { try { await somethingThatReturnsAPromise(); } catch (err) { console.log(err); } } // 另一种写法 async function myFunction() { await somethingThatReturnsAPromise().catch(err => { console.log(err); }; } 如果希望多个请求并发执行,可以使用Promise.all方法 let [foo, bar] = await Promise.all([getFoo(), getBar()]);
十三、 类: 类的数据类型就是函数,类本身就指向构造函数 //定义类 class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return '(' + this.x + ', ' + this.y + ')'; } } 向类添加多个方法 class Point { constructor(){ // ... } } Object.assign(Point.prototype, { toString(){}, toValue(){} }) Class之间可以通过extends关键字实现继承 class Point { constructor(x, y) { this.x = x; this.y = y; } } class ColorPoint extends Point { constructor(x, y, color) { this.color = color; // ReferenceError super(x, y); this.color = color; // 正确 } } 子类的__proto__属性,表示构造函数的继承,总是指向父类 子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性 static调用静态方法(类似类方法), 父类的静态方法能被子类继承 class Foo { static classMethod() { return 'hello'; } } Foo.classMethod() // 'hello' var foo = new Foo(); foo.classMethod() // TypeError: undefined is not a function
十四、 Module export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能 // export命令: // 写法一 export var m = 1; // 写法二 var m = 1; export {m}; // 写法三 var n = 1; export {n as m}; // import命令: A、 import {firstName, lastName, year} from './profile'; function setName(element) { element.textContent = firstName + ' ' + lastName; } B、 import * as circle from './circle'; console.log('圆面积:' + circle.area(4)); console.log('圆周长:' + circle.circumference(14)); export default命令 : 一个模块只能有一个默认输出 // export-default.js export default function () { console.log('foo'); } // import-default.js: import命令可以为该匿名函数指定任意名字 如: customName import customName from './export-default'; customName(); // 'foo'