ES6 标准入门第2版 笔记

2017-06-14  本文已影响0人  汪汪仙贝


看这本书是在markdown在做的笔记,更友好的阅读方式访问: github es6.md(https://github.com/EarlyBirdss/Study-Diary/blob/master/es6.md)

ES6

https://babeljs.io/repl/ 在线转码


let


let 声明一个变量, 只在let命令所在的代码块有效(非常适合在for循环中)

let没有变量提升, 这意味着typeof不再是百分比安全的操作

只要在块级作用域中存在let命令,它所在声明的变量就绑定(binding)在这个区域, 不受外界影响

暂时性死区:在代码块内使用let命令声明变量之前, 该变量都是不可用的

let 不允许在相同作用域内重复声明同一个变量

可以使用let很方便的块级作用域 (IIFE)


const

用来声明常量,一旦声明,其值不可修改(很多规则跟let一样,理解成继承吧)

必须一声明就立即初始化,不能留到以后赋值

对于复杂的数据结构(对象,数组), 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是模式不是变量名

函数参数的解构赋值

酷!

圆括号问题

对于编译器来说, 一个式子是模式还是表达式,没有办法从一开始就知道,带来的问题就是, 如果模式中出现圆括号怎么处理。

建议: 不要在模式中放圆括号

几种情况下在模式中使用圆括号会报错

用途!!!!

交换变量的值 [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; //很方便就能把想用的拿出来


reg

u修饰符: 用于处理4个字节的utf=16编码, 如(根本打不出来, 浏览器跟markdown也显示不了好伐, 这个问题忽略)

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

() //将类数组(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()用来比较两个值是否严格相等,与严格等(===)的行为基本一致,不同之处只有两个

1. +0不等于-0

2. NaN等于自身

Object.assign(): 用来将源对象的所有可枚举属性赋值到目标对象 //Object.assign(target, source1, source2);


Symbol

(研究了大半天终于发现意义在哪里,现在表示当前对象的属性有三种 obj.a、obj['a']、obj[a],一二种是ES5的字符串属性名,第三种是Symbol类型,这样就实现唯一的标识了)

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和Reflect

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

新的数据结构,类似于数组,成员的值都是唯一的,没有重复的值

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节点,不必担心这些节点从文档移除时会引发内存泄漏(没有理解……)

Map

理解: 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

遍历器,是一种接口,为各种不同的数据结构提供统一的接口的访问机制,任何数据结构只要部署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接口的场合

1. 解构赋值(会默认调用Symbol.iterator方法)

2. 扩展运算符(...)

3. for...of,Array.from(),Map、Set、WeakMap、WeakSet,Promise.all(),Promise.race();

字符串可以访问Sybmol.iterator方法

for...of(遍历所有数据结构的统一方法)


Generator

概念: 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

应用

1. 异步操作的同步化表达: 处理异步操作,改写回调函数

function* main(){

var result = yield request("http://some.url.com");

var resp = JSON.parse(result);

doSomething(resp);

}

2. 在任意对象上部署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);

}

3. 作为数据结构

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');

}


Promise

一个对象,用来传递异步操作消息

有两个特点

1. 对象的状态不受外界干扰。Pending,Resolved(Fulfilled),Rejected。只有异步操作的结果可以决定当前是哪一种状态。

2. 一旦状态改变就不会再变,任何时候都可以得到这个结果。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基本语法

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)

1. 修饰器是一个表达式,用于修改类的行为

2. 修饰器本质是能在编译时执行的函数

3. 修饰器函数可以接受3个参数: 目标函数(必要),属性名,该属性的描述对象

方法的修饰(Class 内部方法)

不能用于普通的函数(因为函数提升)

@autobind @override @deprecate


Module

Class只是语法糖,并没有解决模块化问题

Module应对大型程序


编码风格

块级作用域let代替var

全局变量应该只能设置常量,优先使用const

静态字符串使用单引号或反引号,动态字符串使用反引号(`)

解构赋值


使用数组成语对变量赋值,优先使用解构赋值

函数的参数如果是对象的成员,优先使用解构赋值

如果函数返回多个值,优先使用对象的解构赋值而不是数组的解构赋值(便于调整顺序)

对象

单行定义的对象,最后一个成员不以逗号结尾;多行定义的对象,最后一个成员使用逗号结尾

对象尽量静态化,如果属性不可避免要添加,使用Object.assign()方法

如果对象的属性名是动态的,创建对象时使用属性表达式定义

使用扩展运算符(...)复制数组

函数

立即执行函数可以写成箭头函数的形式

(() => {

doSomething();

});

使用函数表达式的场合尽量用箭头函数代替(绑定了this)

简单的单行的不会复用的函数用箭头函数

所有的配置项都应该几种在一个对象,放在最后一个参数,布尔值不可以直接作为参数

总是使用Class代替需要prototype的操作(class写法更简洁)

使用extends实现继承

上一篇下一篇

猜你喜欢

热点阅读