- let 声明一个变量, 只在let命令所在的代码块有效(非常适合在for循环中)
- let没有变量提升, 这意味着typeof不再是百分比安全的操作
- 只要在块级作用域中存在let命令,它所在声明的变量就绑定(binding)在这个区域, 不受外界影响
- 暂时性死区:在代码块内使用let命令声明变量之前, 该变量都是不可用的
- let 不允许在相同作用域内重复声明同一个变量
- 可以使用let很方便的块级作用域 (IIFE)
- 用来声明常量,一旦声明,其值不可修改
- 必须一声明就立即初始化,不能留到以后赋值
- 对于复杂的数据结构(对象,数组), const只能保证保存该数据的地址不发生变化,不能保证其内部值发生变化。(可以使用Object.freeze()冻结对象)
- 解构: ES6按照一定模式,从数组和对象中提取值,对变量进行赋值,这称为解构
- 数组
- var [a,b,c] = [1,2,3]; //a=1,b=2,c=3
- var [head,,third,...tail]=[1,2,3,4]; //tail=[4]
- var [foo] = [];//如果解构不成功,变量的值就等于undefined
- let [a,[b],d] = [1,[2,3],4]; //b=2;不完全解构,解构依然可以成功
- 如果等号右边不是数组(可遍历的结构)就会报错;
- 默认值:
- let [x,y=1] = [0]; //x=0,y=1;
- let [x,y=1,z] = [0, undefined,2]; //x=0,y=1,z=2;(必须使用undefined/,因为es6内部使用严格等===)
- 惰性求值, 如果默认值是表达式, 只用用的时候才求值
- 对象
- 对象没有次序,变量必须和属性同名或使用以下方式,才能取得正确的值
- var {foo:baz} = {foo: 1, bar: 2}; //baz=1;
- 对象没有次序,变量必须和属性同名或使用以下方式,才能取得正确的值
- 字符串
- 因为字符串自带length属性,
- let {length: len} = 'jifag';
- 字符串被转换为类似数组的对象
- const [a,b,c,d,e] = 'hello'; //a='h',b='e'...
- 解构可以嵌套
- var {p: [x,{y}]} = {p:['hello',{y:'world'}]}; // x='hello',y='world';此时p是模式不是变量名
- 因为字符串自带length属性,
- 函数参数的解构赋值
- 酷!
- 圆括号问题
- 对于编译器来说, 一个式子是模式还是表达式,没有办法从一开始就知道,带来的问题就是, 如果模式中出现圆括号怎么处理。
- 建议: 不要在模式中放圆括号
- 几种情况下在模式中使用圆括号会报错
- 用途!!!!
- 交换变量的值 [x,y] = [y,x];
- 获取从函数返回的多个值 var [a,b,c] = test(); //test return [1,2,3];
- 函数参数的定义
- eg1: 参数是有序值function f([x,y,z]){}; f([1,2,3])
- eg2: 参数是无序值function f({x,y,z}){}; f({x:1,y:2,z=3})
- 提取json数据
- 函数参数的默认值(好用!!!)
- let {log, sin, cos} = Math; //很方便就能把想用的拿出来
-
u修饰符: 用于处理4个字节的utf=16编码, 如(打不出来)
-
y修饰符: ‘粘连’ 跟g一样是全局匹配, 但g是从上一次匹配的剩余位置开始匹配,y限制了只能从剩余位置的第一位匹配
var str = 'aaa_aa_a', g = /a+/g, y = /a+/y; g.exec(str); //执行两次, ['aaa'],['aa'] y.exec(str); //执行两次, ['aaa'],null
- () //将类数组(array like)对象和可遍历的对象转化为真正的数组,接受第二次参数类似于数组的map函数Array.from({'0':1, '1': 2, '2': 3}, x => x * x) //[1,4,9];
- 扩展运算符 (...)
- Array.of() //将一组值转化为数组 Array.of(1,2,3) //[1,2,3]
- 数组实例 Array.prototype.copyWithin(target, start = 0, end = this.length): 在数组内部将指定位置的成员复制到其他位置(覆盖)
- 数组实例 Array.prototype.find()和findIndex();
- find(function(value, index, arr){})有点像filter, find只找到第一个符合条件的值就返回这个值 findIndex返回该值的位置
- 数组实例 Array.prototype.fill(value, start = 0, end = this.length) //很方便的初始化空数组,如果不是空数组, 数组中已有的元素将被重置 new Array(3).fill(0) //[0,0,0]
- 数组实例 Array.prototype.entries(),keys(),values() 返回遍历器对象
- 数组实例 Array.includes() 返回布尔值
- 数据推导 [for (year of years) if (year > 2000) year];
- rest参数(...变量名): 替代arguments function add(...value){}
- 扩展运算符(...) 像reset参数的逆运算
- console.log(...[1,2,3]) //1 2 3 (1,2,3)
- 替代数组的apply方法
- 合并数组 [1,2,...more] //more=[3,4];
- 与解构赋值结合 const [first, ...rest] = [1,2,3,4];//first=1,rest=[2,3,4];
- 箭头函数
- var sum = (num1, num2) => num1 + num2;
- 如果函数内代码语句多于一行,要用大括号括起来,并使用return返回
- 简化回调函数 [1,2,3].map(x => x * x); //[1,4,9]; [6,1,8].sort((a,b) => a - b); //[1,6,8]; const number = (...nums) => nums; // number(1,2,3)=>[1,2,3]
- 属性的简洁表示法 let obj = {x,test(){}}
- 属性名表达式 let obj = {['a' + 'b']: 'ab'}
- 属性名表达式与简洁表示法不能同时使用,会报错
- Object.is()用来比较两个值是否严格相等,与严格等(===)的行为基本一致,不同之处只有两个
- +0不等于-0
- NaN等于自身
- Object.assign(): 用来将源对象的所有可枚举属性赋值到目标对象 //Object.assign(target, source1, source2);
var f = Symbol('foo');
var a = {};
var b = {};
a[f] = 'a[f]';
b.f = 'b.f';
Object.assign(b, a);
console.log(b[f], b.f); //'a[f]', 'b.f'
- ES6引用一种新的原始数据类型Symbol表示独一无二的值
- let s = Symbol();typeof s;//symbol 没有‘new’
- let s = Symbol('foo'),a={};a[s] = function(){};//不能使用点运算符(a.s),不加引号(a['s']);
- Symbol的参数是没有实际意义的,只表示对Symbol实例的描述,主要是为了方便区分
- symbol可以用来定义一组常量,保证这组常量的值都是不相等的
- 魔术字符串:在代码之中多次出现、与代码行程强耦合的某一个集体的字符串或数值。
- 消除魔术字符串的常用方便就是把它写成一个常量, 此时比较适合用Symbol
- Symbol类型的属性名,不会出现在for...in、for...of遍历中,也不会被Object.keys(),Object.getOwnPropertyNames()返回。也不是私有属性。Object.getOwnPropertySymbols可以获取指定对象的所有Symbol属性名
- Symbol.for(name):重新使用同一个symbol值
- var s1 = Symbol.for('foo'), s2 = Symbol.for('foo');s1 === s2; //true
- var s1 = Symbol('foo'), s2 = Symbol.for('foo');s1 === s2; //false
- 内置的Symbol值
-
proxy用于修改某些操作的默认行为,等同于在语言层面做出修改,属于“元程序”(对编程语言进行编程)。代理器,拦截器
-
作为构造函数Proxy接受两个参数,第一个参数是所要代理的目标对象,第二个参数是配置对象(包含有处理函数),要使Proxy起作用,必须针对Proxy实例进行操作,而不是针对目标对象
var proxy = new Proxy({},{ get: function(target, property){ return 35; } }); proxy.time; //35
-
set();利用Proxy,可以将读取属性的操作转变为执行某个函数,从而实现属性的链式调用
var pipe = (function() { var pipe; return function(value) { get: function(pipeObject, fnName) { if(fnName == 'get') { return pipe.reduce(function(val, fn) { return fn(val); }, value); } pipe.push(window[fnName]); return pipeObject; } } });
-
Proxy中设置set方法可以拦截某个属性的赋值操作
let validator = { set: function(obj, prop, value) { if(prop === 'age'){ if(!Number.isInteger(value)) { throw(new TypeError('The age is not an integer')); } if(value > 200) { throw new RangeError('The age seems invalid'); } } obj[prop] = value; } } let person = new Proxy({}, validator); person.age = 100; person.age = 400;//报错
-
apply()方法拦截函数的调用
-
has()方法可以隐藏某些属性不被in操作符发现
-
construct()方法用来拦截new命令
-
deleteProperty()方法拦截delete操作
-
defineProperty()方法拦截Object.defineProperty();
-
enumerate()方法拦截for...in循环
-
ownKeys()方法拦截Object.keys()
-
-
Reflect 将Object某些语言层面的方法部署到Reflect之中
- 新的数据结构,类似于数组,成员的值都是唯一的,没有重复的值
- set函数可以接受一个数组(或类数组)let set = new Set([1,2,3,3]); [...set]; //[1,2,3]
- 属性和方法set.prototype.size ,add(value), delete(value), has(value), clear()
- 遍历操作 keys(),values(),entries(),forEach();前三个返回遍历器。set结构只有键值没有键名,keys的返回值等于values
- WeakSet 内部成员只能是对象,不可遍历,没有clear方法,没有size属性,引用为存储dom节点,不必担心这些节点从文档移除时会引发内存泄漏(没有理解……)
-
理解: Object提供“字符串——值”的对应,Map提供“值——值”的对应
-
map接收一个数组作为参数,数组的成员为一个个表示键值对的数组;
var map = new Map([['name', 'Jack'], ['age', '12']]); map.size; //2 map.has('name'); //true map.get('name'); //jack map.set('gender', 'female'); // map.delete('age'); map.clear();
-
WeakMap: 只接收对象为键名(null除外)。同样应用于dom, 部署私有属性
-
遍历器,是一种接口,为各种不同的数据结构提供统一的接口的访问机制,任何数据结构只要部署iterator接口就可以完成遍历操作
-
不断调用next方法 返回({value:value, done: Boolean})
-
for...of 循环
-
一个数据结构只要具有Symbol.iterator属性,就被认为是可遍历的。调用Symbol.iterator方法就可以得到当前结构的遍历函数
-
以下三种结构原生具有iterator接口: 数组, 某些类似数组的对象, set和map结构
var map = new Map([['name', 'aya'], ['gender', 'female']]); var iter1= map[Symbol.iterator](); iter1.next(); // Object {"done": false,"value": Array ["name","aya"]} //类数组调用Symbol.iterator let iterable = { '0': 1, '1': 2, '2': 3, length: 3, [Symbol.iterator]: Array.prototype[Symbol.iterator] }; let ite = iterable[Symbol.iterator](); console.log(ite.next());
-
对象没有默认部署iterator接口,是因为哪个属性先遍历哪个后遍历是不确定的。需要开发者手动指定
// 为对象添加Iterator的一个实例 let obj = { data: ['hello','world'], [Symbol.iterator]() { const self = this; let index = 0; return { next() { if(index < self.data.length) { return { value: self.data[index++], done: false }; }else { return { value: undefined, done: true }; } } } } } for(let j of obj){ console.log(j); } var i = obj[Symbol.iterator](); i.next(); //'hello';
-
调用Iterator接口的场合
- 解构赋值(会默认调用Symbol.iterator方法)
- 扩展运算符(...)
- for...of,Array.from(),Map、Set、WeakMap、WeakSet,Promise.all(),Promise.race();
-
字符串可以访问Sybmol.iterator方法
-
for...of(遍历所有数据结构的统一方法)
- 概念: Generator是ES6提供的一种异步编程解决方案。状态机。执行Generator会返回一个遍历器对象。
- function与函数名之间有一个*号, 函数内部使用yield定义不同的内部状态
- yeild就像是暂停标志,generator.next()执行一次就从就返回一个{value:yeild语句后的值,done:},并暂停向下执行,再执行.next()方法再返回再暂停,直到遇到return语句为止,没有return就放回{value: undefined, done: true};
- yeild后面的表达式,只有当调用next方法,内部指针指向语句时才会执行(惰性求值)
- yeild语句不能出现在普通函数中(foreach改为使用for循环),yield语句如果在表达式中,必须加圆括号
- next的参数,会被当做上一条yield语句的返回值
- for...of,扩展运算符,解构赋值,Array.from
- 应用
- 异步操作的同步化表达: 处理异步操作,改写回调函数
-
function* main(){ var result = yield request("http://some.url.com"); var resp = JSON.parse(result); doSomething(resp); }
- 在任意对象上部署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 testObj = {hello: 'you', world: 'me'}; for(let [key,value] of iterEntries(testObj)){ console.log(key, value); }
- 作为数据结构
-
function* doStuff(){ yield fs.readFile.bind(null, 'hello.txt'); yield fs.readFile.bind(null, 'work.txt'); yield fs.readFile.bind(null, 'as-so-on.txt'); }
- 一个对象,用来传递异步操作消息
- 有两个特点
- 对象的状态不受外界干扰。Pending,Resolved(Fulfilled),Rejected。只有异步操作的结果可以决定当前是哪一种状态。
- 一旦状态改变就不会再变,任何时候都可以得到这个结果。promise只能从Pending变为Resolved或Pending变成Rejected。只要一种变化发生就不会再发生变化
- 实例方法then, catch
- Promise.all() 将多个Promise实例包装成一个新的promise实例 promise.all([p1,p2,p3]);
- Promise.race() 同样将多个promise实例包装成一个新的promise实例 promise.race([p1,p2,p3]);
- 区别: all方法,只有参数p全部变成resolved,p才为resolved(rejected同样);race方法,只要有一个参数变成resolved,p就变为resolved(rejected同样);
- Promise.resolve() 将现有对象转为promise对象
- promise对象可以不带参数, 如果希望得到一个新的promise对象,可以直接调用 var p = Promise.resolve();
- Promise.reject() 返回一个新的promise对象,状态为Rejected
- promise.done() 总是处于回调链的尾端,保证抛出任何可能出现的错误
- promise.finally() 跟promise.done最大的区别的是: finally接受普通函数作为参数,无论如何都会执行
- ES6的异步编程: 回调函数,事件监听, 发布/订阅, Promise对象
- 异步: 一个任何分为两段,先执行第一段,然后转而这行其他认识,等做好准备再回头执行第二段(不连续的操作)
- Thunk函数: 编译器的传名调用(如var m = 1; f(m+1),//m+1在f内用到才求值,而不是先求值再调用f(2)),实现往事先将函数放到一个临时函数中,再将这个临时函数传入函数体。这个临时函数就叫Thunk函数。
- js是传值调用(var m = 1; f(m+1);// 先求值等于2,在调用f(2);Thunk函数替换的不是表达式而是多参数函数,替换为单参数版本,且只接受回调函数作为参数。
class Point{
constructor(x, y) {
this.x = x;
this.y =y;
}
toString(){
return "x+y= " + this.x + this.y;
}
}
-
语法糖
-
类的内部定义的所有方法都是不可枚举的(跟ES5不同)
-
constructor方法是类的默认方法,new之后自动调用该函数
-
如果忘记加new调用Class,会报错
-
Class表达式
-
const MyClass = class Me { getClassName(){ return Me.name; } }
-
调用时使用new MyClass();Me只供Class的内部代码使用
-
不存在变量提升
-
代码内部为严格模式
-
extends 实现继承 * class ColorPoint extends Point { constructor(x, y, color) { super(x, y); this.color = color; } }
- super: 关键字,指代了父类的实例(即父类的this对象)
- 子类必须在constructor方法中调用super, 否则新建实例时会报错。因为如果不调用super方法,子类就得不到this对象
- 父类只要用prototype就能被子类继承, 父类可以是任意函数
- 对象总是继承其他对象的,所以可以再任意一个对象中使用super关键字
-
static 静态方法:在方法前面添加static关键字,不会被实例继承,通过类直接调用
-
Class只有静态方法没有静态属性
-
new.target属性: 返回new命令所作用的构造函数(在构造函数中使用)
- 类的修饰(Class)
- 修饰器是一个表达式,用于修改类的行为
- 修饰器本质是能在编译时执行的函数
- 修饰器函数可以接受3个参数: 目标函数(必要),属性名,该属性的描述对象
- 方法的修饰(Class 内部方法)
- 不能用于普通的函数(因为函数提升)
- @autobind @override @deprecate
- Class只是语法糖,并没有解决模块化问题
- Module应对大型程序
- 块级作用域let代替var
- 全局变量应该只能设置常量,优先使用const
- 静态字符串使用单引号或反引号,动态字符串使用反引号(`)
- 解构赋值
- 使用数组成语对变量赋值,优先使用解构赋值
- 函数的参数如果是对象的成员,优先使用解构赋值
- 如果函数返回多个值,优先使用对象的解构赋值而不是数组的解构赋值(便于调整顺序)
- 对象
- 单行定义的对象,最后一个成员不以逗号结尾;多行定义的对象,最后一个成员使用逗号结尾
- 对象尽量静态化,如果属性不可避免要添加,使用Object.assign()方法
- 如果对象的属性名是动态的,创建对象时使用属性表达式定义
- 使用扩展运算符(...)复制数组
- 函数
- 立即执行函数可以写成箭头函数的形式
-
(() => { doSomething(); });
- 使用函数表达式的场合尽量用箭头函数代替(绑定了this)
- 简单的单行的不会复用的函数用箭头函数
- 所有的配置项都应该几种在一个对象,放在最后一个参数,布尔值不可以直接作为参数
- 总是使用Class代替需要prototype的操作(class写法更简洁)
- 使用extends实现继承