ES6入门笔记------10-18数据结构,同异步相关

1.Symbol:js语言的第七种类型

产生原因:为了防止对象属性名冲突。在引入后,对象的属性名除了原有的字符串,还可以是Symbol类型,且只要是Symbol,就是独一无二的。

Symbol 函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述,主要是为了在控制台显示,或者转为字符串时比较容易区分。否则显示都是Symbol()。如果参数是一个对象,会被toString

注意:1.Symbol值不是对象,不能new,不能添加属性,是类似字符串的数据类型

           2.Symbol值不能和其他类型的值进行运算,会报错

           3.Symbol值可以转字符串和Boolean,但不能转数值      

<1>Symbol作为属性名

var mySymbol = Symbol();
var a = {};

a[mySymbol] = 'hello';           //写法一
Object.defineProperty(a, mySymbol , { value :'hello'});           //写法二

var a={               //写法三
    [mySymbol]:'hello';
}
a[mySymbol]  //'hello'

作为属性名的时候,必须放在[ ]中(不放则为字符串),不能使用点运算符,因为点运算符后面总是字符串。

<2>消除魔术字符串:指的是某一个具体的字符串或者数值,与代码形成强烈耦合,不利于维护修改

     消除的话,就是把它写成一个变量来代替,为了不予其他属性值冲突,可以用Symbol值

<3>作为属性名遍历:一些常规获取键名的方法无法获取Symbol键名,可以用

      1.Object.getOwnPropertySymbols 方法来返回对象所有用作属性名的Symbol值。

      2.Reflect.ownKeys 方法可以返回所有类型的键名,包括常规键名和Symbol键名。

<4>Symbol.for():传入一个参数,全局搜索以该参数为名称的Symbol值,有则返回该Symbol值,无则创建并返回该Symbol值

因为这种该方法有登记机制,所以每次调用返回的都是同一个,而Symbol()写法没有,所以每次调用返回的都是不同值

Symbol.keyFor():返回一个已等级的Symbol类型值的key(即for所传入的参数),如果未登记,则返回undefined

<5>11个内置的Symbol值,指向语言内部使用的方法(有需要再):

       1.Symbol.hasInstance,会在使用 instanceof 运算符的使用调用,使用的是[ Symbol.hasInstance ] (参数){ }

       2.Symbol.isConcatSpreadable,值为Boolean,表示当用concat()的时候,里面的参数是否要展开

       3.Symbol.species:创造实例时调用,会使用这个属性返回的函数作为构造函数来创建实例对象

       4.Symbol.match:当执行str.match()时,如果该属性存在,会调用它返回该方法的返回值

       5.Symbol.replace:当String.prototype.replace调用的时候返回该方法返回值

       6.Symbol.search:当String.prototype.search调用的时候返回该方法返回值

       7.Symbol.split:当String.prototype.split调用的时候返回该方法返回值

       8.Symbol.iterator:指向该对象的默认遍历器方法(for...of)

       9.Symbol.toPrimitive:放对象被转为原始类型的值的时会调用这个方法,返回该对象对应的原始类型值,调用是接受一个参数(Number,String,Default(数字或字符串都行))

       10.Symbol.toStringTag:调用Object.prototype.toString时,如果该属性存在,其返回值会出现在toString方法返回的字符串中,表示对象的类型

       11.Symbol.unscopables,指定了使用with关键字时,哪些属性会被width环境排除

 

2.Set和Map数据结构

<1>Set基础介绍:Set本身是一个构造函数,用来生成Set数据结构,它类似于数组,但成员的值都是唯一的

                        值加入时,不会发生类型转换,即类似===,但NaN相等,两个对象总是不相等,衡量长度用size

                        Array.from可以将Set结构转为数组(或者用扩展符可以将Set转为数组),这就提供了一种数组去重的方法

<2>Set实例方法分为两大类:

       数据操作:add():添加值,返回Set本身,

                         delete():删除某个值,返回Boolean,表示是否删除成功

                         has():返回Boolean,表示参数是否为Set成员

                         clear():清除所有成员,没有返回值

       遍历操作:遍历顺序为插入顺序

                         keys:返回键名,无键名,所以行为同values()

                         values():返回键值

                         entries():返回键值对,键名同键值

                         foreach():遍历每个成员

                         map()和filter()方法:先转为数组处理,处理完再放进Set

<3>WeakSet:与Set类似,也是不重复的值的集合,和Set有两个区别:

      1.WeakSet的成员只能是对象,没有size属性,无法遍历其成员

      2.WeakSet中的对象都是弱引用,垃圾回收机制不考虑WeakSet对该对象的引用,即当其他对象不再引用该对象中,垃圾回收机制会自动给回收该对象所占内存。

<4>Map基础介绍:类似对象,但什么类型的值都可以当作键。任何具有Iterator接口,且每个成员都是一个双元素数组的数据结构都可以作为Map构造函数的参数(双元素:前者为键,后者为值)

!!!注意:Map的键实际上是和内存地址绑定的,只要内存地址不一样,就是两个键(解决了同名属性碰撞的问题)

<5>Map实例的属性和操作方法

      1.属性:size,返回成员总数

      2.set(key,value)赋值,可以用链式写法;get(key)读取;has,delete,clear和遍历方法和转化成数组的方法同set

      3.Map和对象互转:Map的所有键得都是字符串,都是通过for循环,一个个成员赋值

      4.Map转JSON:

              一、Map的键名都是字符串,则JSON.stringify(Map数据)

              二、Map的键名有非字符串,可以选择转为数组JSON:JSON.stringify( [ ...Map数据 ] )

      5.JSON转Map:

              一、JSON的键名都是字符串,则同对象

              二、JSON是一个数组,new Map(数组JSON)

<6>WeakMap:与Map类似,和Map的区别:

      1.WeakSet只接受对象作为键名( null 除外)

      2.WeakSet的键名所指向的对象不计入垃圾回收机制(解决内存泄漏)。

      3.没有size属性,没有遍历操作,不支持clear方法

  用途:eg:DOM节点相关的值或者方法存入,一旦节点删除,方法和值将自动消失;;;还可以部署私有属性

 

3.Proxy:访问对象前,先通过Proxy,它用自己的定义覆盖了原始定义

<1>概述:ES6提供了原生的Proxy构造函数:var proxy= new Proxy(target, handler) ; 

      target是要拦截的目标对象,handle参数是一个配置对象,用来定制拦截行为,要使Proxy起作用,必须是对Proxy的实例进行操作,而不是target

      有个技巧是可以将Proxy对象设置到object.proxy属性上,从而可以在object对象上调用

      Proxy实例可以作为其他对象的原型对象

      如果一个属性不可配置( configurable )或者不可写 ( writable ),则该属性不可被代理,通过Proxy对象访问则会报错

<2>Proxy实例的方法介绍

     1.get( target,propKey,receiver ):拦截某个属性的读取操作;参数为访问目标,属性名,

             如果没有拦截,访问对象不存在的属性,返回undefined;

             get方法可继承

      2.set( target,propKey,value, receiver ) :拦截属性赋值操作:可用于赋值的值的验证,还可以实现数据绑定

               结合get(),可以做到防止内部属性(一般属性名的第一个字符使用下划线开头)被外部读写

      3.apply( target, object目标对象上下文对象this, args):拦截函数的调用、call、apply操作

      4.has( target,propKey):判断对象是否具有某个属性时生效(HasProperty操作,而不是HasOwnProperty操作。即不判断是自身还是继承),典型操作就是 in 运算符 ( 属性名 in 对象),对for...in不生效

      5.construct( target,args):用来拦截new命令,return回来的必须是一个对象,否则报错

      6.deleteProperty( target,propKey, propDesc):用来拦截delete操作,如果该方法抛出错误或者返回false,则当前属性无法被delete命令删除。   (delete obj.propKey)

      7.defineProperty( target,propKey, propDesc):拦截Object.defineProperty操作,如果该方法返回false,添加新属性会抛出错误。如果目标对象不可扩展(extensible),该方法不能增加目标对象中不存在的属性

      8.getOwnPropertyDescriptor( target,propKey):拦截Object.getOwnPropertyDescriptor(),返回一个属性描述或undefined

      9.getPropertyOf( target,proto):主要用来拦截获取对象原型,具体为:(返回值必须为对象或者null)

              Object.prototype._proto_

              Object.prototype.isPrototypeOf()

              Object.getPrototypeOf()

              Reflect.getPrototypeOf()

              instanceof

      10.isExtensible( target ):拦截Object.isExtensible操作,返回为Boolean,不是的也会被转为Boolean,返回值必须与目标对象的isExtensible属性保持一致

      11.ownKeys( target ):拦截对象自身属性的读取操作,具体为:(返回的必须是数组,数组成员只能是字符串或Symbol值)

              Object.getOwnPrototypeNames()

              Object.getOwnPrototypeSymbols()

              Object.keys():使用该方法的时候,有三类属性会被自动过滤,不存在的,Symbol值,不可变遍历的属性

    注意:如果目标对象包含不可配置的属性,那么该属性必须被ownKeys方法返回,否则会报错;    

               如果目标对象是不可扩展的non-extensition,ownKeys返回的数组之中必须包含原对象的所有属性,且不能有其他多余的属性,否则报错

      12.preventExtensions( target ):拦截Object.preventExtensions(),返回一个Boolean,不是的也被转Boolean。有个限制,当目标为不可扩展时(即Object.isExtensible(proxy)为false),proxy.preventExtensions才能返回true,否则报错。为了防止返回true报错,通常在该方法中调用一次Object.preventExtensions(target)即先设置为不可扩展

      13.setPropertyOf( target,proto):拦截Object.setPropertyOf方法,如果目标对象不可扩展,不能改目标对象的原型。此外该方法也只能返回Boolean

<3>Proxy.recocable(target,handler):返回一个可取消的Proxy实例,该方法返回一个对象:{ proxy,revoke}。其中proxy为Proxy实例,revoke属性是一个函数,可以取消Proxy实例,所以当执行过revoke函数后( revoke() ),再访问Proxy实例,则会抛出一个错误。使用场景,目标对象只允许通过代理访问,一旦访问结束,不允许再次访问

<4>this问题:一旦proxy代理对象,不管是否有做拦截,目标对象的this都指向proxy,而不是目标对象。有些属性只有通过正确的this才能获取,所以代理无法获取这些属性,得通过将this绑定到target上来解决问题。

 

4.Reflect

<1>概述:

     设计目的:1.将Object对象一些明显属于语言内部的方法(eg:Object.defineProperty)放到Reflect对象上,现在是某些方法同时在Object和Reflect上部署,将来只会在Reflect上

             2.修改某些Object方法返回的结果:有些方法在无法定义时会抛出错误,但Reflect的则会返回false

             3.让Object操作都变成函数行为:有些Object操作是命令式,比如delect obj.prokey,而Reflect.deleteProperty(obj, prokey)则让他们变成函数行为。

             4.Reflect对象的方法和Proxy对象的方法一一对应,所以Proxy对象可以方便的调用对应的Reflect方法来完成默认行为,然后再完成额外的功能

<2>Reflect对象与Proxy对应的13个方法:Reflect的target如果不是对象,大部分会报错

     1.get( target,name,receiver ):查找并返回target对象的name属性,如果name属性上部署了读取函数,则读取函数的this绑定receiver。

      2.set( target,name,value, receiver ) :设置target对象的name属性值为value,如果name属性上部署了读取函数,则读取函数的this绑定receiver。

      注意:该函数会触发Proxy.defineProperty拦截

      3.apply( func, thisArg, args):(267页,不太理解,需要例子)

      4.has( target,name):是 name in target的新写法

      5.construct( target,args):等同于new target(...args);

      6.deleteProperty( target,name):是 delete target.name的新写法

      7.defineProperty( target,propKey, propDesc):Object.defineProperty的新方法

      8.getOwnPropertyDescriptor( target,propKey):等同Object.getOwnPropertyDescriptor(),获取指定属性的描述对象

      9.getPropertyOf( target):Object.getPrototypeOf()的新写法,区别在于就写法的参数如果不是对象,则会转为对象,而新写法则会报错

      10.isExtensible( target ):等同Object.isExtensible操作,返回为Boolean

      11.ownKeys( target ):返回对象的所有属性,基本等同于Object.getOwnPrototypeNames和Object.getOwnPrototypeSymbols()之和。

      12.preventExtensions( target ):等同Object.preventExtensions

      13.setPropertyOf( target,newProto):Object.setPropertyOf的新方法,返回target,旧方法如果target不是对象,则返回target,新方法报错。target为undefined和null时,新旧方法都报错

<3>使用Proxy和Reflect实现观察者模式:

     当一个对象赋值的时候,Proxy拦截,handle函数里,首先用Reflect赋值到对象,然后for循环充当观察者的各个函数

 

5.Promise

<1>Promise的基本方法:

         new Promise之后立即执行(即Promise内部的function代码立即执行)

         如果一个promise实例test1的resolve方法将另一个promise实例test2作为参数,即一个异步操作的结果是返回另一个异步操作,test1得等test2的状态变成resolve或者rejected才会执行其回调函数

<2>Promise.prototype.then():Promise实例状态改变时的回调函数,第一个参数为resolve状态的回调,第二个是(可选)rejected的回调。then方法返回的是一个新的Promise实例,所以可以采用链式写法,即后面继续then

<3>Promise.prototype.catch():是then( null,rejection)的别名,发生错误时的回调函数,可以捕捉rejected的返回,也可以捕捉then的回调函数执行的时候发生的错误。

      在Promise里面,如果在Promise的状态变成resolved后,抛出错误,是不会被捕获的没等于没有抛出。Promise对象下错误具有“冒泡” 性质,会一直向后传递,知道被捕获为止(但不会传到外层代码),所以一般来说不在then里面定义rejected状态的回调函数,而是用catch。

      catch返回的是一个Promise对象,因此可以接着调用then

<4>Promise.all:将多个Promise包装成一个新的Promise实例,参数为具有Interator接口,且每个成员都是Promise实例。

      只有当参数的Promise实例都fulfilled,组合的Promise.all才会fulfilled,参数的返回值组成一个数组,传给all的回调函数。

      或参数的Promise实例中,只要有一个被Rejected,all就返回Rejected(如果Promise实例自身定义了catch方法,则不会触发all的catch)

<5>Promise.race():同样将多个Promise实例包装成一个新的,于all不同的是,只有有一个实例改变状态,race就跟着改变,实例的返回值传给race的回调

<6>Promise.resolve():将现有对象转为Promise对象:参数分四种情况

       1.Promise实例:原封不动返回这个实例

       2.thenable对象:具有then方法的对象 >> 对象转Promise并立即执行对象的then方法

       3.参数不是具有then方法的对象或根本不是对象:返回一个Promise对象,状态为resolved,参数传给回调函数

       4.无参数:直接返回一个resolved状态的Promise对象

注意:立即resolve的Promise对象是在本轮事件循环结束时,而不是下一轮开始时。

<7>Promise.reject():返回一个Promise实例,状态为rejected,回调函数会立即执行,参数传给回调函数

<8>两个不在ES6中但很有用的方法:

      1.done():需要自己实现,放在Promise回调链的尾端,来保证可以将最后可能出现的错误抛到全局

Promise.prototype.done = function (onFulfilled, onRejected) { 
    this.then(onFulfilled, onRejected)
        .catch(function (reason) { 
            //抛出一个全局错误
            setTimeout(() => { throw reason }, 0); 
    });
};
//实现eg
asyncFunc().then(f1).catch(r1).done();

     2.finally():不管Promise状态如何都会执行,接受一个普通的回调函数作为参数,该函数无论怎样都必须执行

Promise.prototype.finally = function (callback) { 
    let P = this.constructor; 
    return this.then( 
        value => P.resolve (callback()).then ( () => value), 
        reason=> P.resolve(callback()).then(() => { throw reason })
    );
};
//实现eg
asyncFunc().then(f1).finally(stop);

<9>Promise.try(): 提案,有些Promise代码库已经提供这个方法,用法:

Promise.try(一个Promise对象).then().catch();

本质是模拟了try代码块,只要抛出错误,无论是异步还是同步(只能用try,catch捕获),都可以捕获。

 

6.Iterator

<1>概念:统一的接口机制处理不同的数据结构(array,object,set,map等),使得数据结构成员能按照某种次序排列,主要为ES6的新命令for...of消费。

      具有next方法,返回value和done(遍历是否结束,boolean)

<2>默认的Iterator接口:部署在数据结构的Symbol.iterator属性(所以一个数据结构只有具有这个属性,即认为可遍历),调用Symbol.iterator方法可得当前数据结构默认的遍历器生成函数。Symbol.iterator本身是表达式。

      1.对象没有默认Iterator接口,因为对象属性的遍历顺序不确定,需要开发者手动指定。严格来说,部署遍历器部署很有必要,因为对象实际上被当作Map结构使用。

      2.遍历器是一种线性处理,对于非线性的数据结构,部署遍历器等于部署一种线性转换。

      3.类似数组的对象,部署Iterator接口可使用数组的Iterator接口:

           NodeList . prototype[Symbol . iterator] = Array . prototype[Symbol.iterator] ;

      4.如果Symbol.iterator返回的部署一个遍历器生成函数,会报错。

<3>调用iterator接口的场合

     1.任何接受数组作为参数的场合其实都调用了:for...of,Array.from(),Promise.all()等

     2.解构赋值

     3.扩展运算符:转为数组
     4.yield*后面跟可便利结构,则会调用该结构的遍历器接口

<4>遍历器对象的throw()和return():

      遍历器对象的next()是必需,throw()和return()可选。

      使用场合:return():循环提前退出          ------return()方法必须返回一个对象:return {done:true}

                        throw():主要配合Generator函数使用

<5>for...of:循环获得键值

      1.数组的遍历接口只返回具有数据索引的属性,eg:arr.foo

      2.Set和Map顺序按照成员被添加顺序遍历,Set只返回value,Map返回(name,value)

 

7.Generator函数

<1>概念:

      1.语法上来说,可以理解为状态机,封装了多个内部状态。执行Generator函数,返回一个遍历器对象,该遍历器会依次遍历Generator函数内部的每一个状态。

      2.形式上:是一个普通函数,但是function命令与函数名之间有一个*,内部使用yield语句定义不同的内部状态

      3.函数调用:调用同普通函数,调用后方法并不执行,返回不是运行结果,而是函数内部指针对象,也就是遍历器对象。执行必须调用next方法,使指针下移,每次执行,从上一次停的地方到下一条yield或者return语句。即函数是分段执行的。返回的是value,done。value为yield后面表达式的值或者return后面的,都没有则undefined。

      4.yield表达式只能在Generator函数内。另外如果用在另一个表达式之中,必须放在()内,作为函数参数或放在赋值表达式的右边,可以不加()

      5.与Iterator接口的关系:因为Generator函数就是遍历器生成函数(该对象本身也具有Symbol.iterator属性,执行后返回它自己),所以可以把它赋值给对象的Symbol.iterator属性,使其具有Iterator接口。

<2>next方法的参数

         yield本身没有值,next方法可以带一个参数,该参数会被当做上一条yield的返回值。(用来下一个next的时候可能有的赋值操作等,否则就是undefined进行操作)

<3>for...of循环

      1.循环yield,一旦done为true,循环就终止,所以return 的返回不被遍历出来。(解构赋值等调用遍历器接口的同上)

      2.Generator函数除了赋值给对象的Symbol.iterator属性,还可以将对象传入,通过Generator函数加上接口。

<4>Generator.prototype.throw():在函数体内写好try,catch,在函数体外抛出错误(eg:i.throw('a')),会先被函数内的catch,函数内的catch过后,才被函数外的catch。不同于throw命令抛出,命令抛出的只会被函数外的catch捕获。

       Generator的throw方法被捕获后会附带执行下一条yield表达式(即报错也会照旧执行yield),所以只要再Generator内部部署了try...catch代码块,那么throw抛出的错误就不会影响下一次遍历。

        如果Generator没有部署try...catch代码块,一旦执行过程中抛出错误,就不会再执行下去。此后还调用next方法的话,会返回一个 value 属性等于 undefined 、 done 属性等于 true 的对象。

<5>Generator.prototype.throw():如果Generator内有try...finally代码块,那么执行Generator.return(参数)的时候,开始执行finally内的东西,直到执行完才执行return的。不然就是return参数,没有参数就是undefined,同时遍历终止,返回done为true

<6>yield*表达式:如果在 Generator 函数内部调用另一个 Generator 函数,默认情况下是没有效果的,但是yield* Generator 函数则可以,相当于for...of了这个内部Generator 函数(没有return语句时)。有 return语句时则需要用 var value =
yield* iterator 的形式获取 return 语句的值。

       任何数据结构只要有 Iterator 接口(eg:数组),就可以被 yield*遍历 。

<7>一个对象的属性是 Generator 函数

<8>Generator 函数的this:Generator函数返回的遍历器的它的实例,会继承Generator.prototype。但如果Generator函数在this对象上添加属性,那么实例对象拿不到。Generator也不能同new一起使用,因为不是构造函数。

      但是!可以用call和Generator.prototype来实现this的绑定

 

8.Generator函数的异步应用

<1>Thunk函数:编译器的“传名调用”(即调用的时候才操作)的实现往往是将参数放到一个临时函数之中,再将这个临时函数传入函数体。这个临时函数就称为Thunk。但是js是传值调用,他的Thunk函数替换的不是表达式,而是多参数函数,替换成一个只接受回调函数作为参数的单参数函数。

所以只要参数有回调函数,就能写成Thunk形式。生产环境建议用Thunkify模块

<2>利用Trunk或者Promise都可以做到Generator的自执行

<3>co模块,require引入,然后将Generator函数作为参数传入co函数,co函数返回Promise对象

 

8.async函数

<1>定义:可以看作Generator的语法糖,async=* ,await=yield。优点在于:

      1.自带执行器,同普通函数,不需要next去下一步;

      2.更好的语义:async代表有异步操作,await在哪里

      3.更广的适用性:co模块约定,yield后面只能是Promise对象或Thunk函数,但await后面都行

      4.async函数返回的是Promise

<2>await命令:

      1.后面是一个Promise对象,如果不是,会被转成一个立即resolve的Promise对象。

      2.后面的Promise对象如果变为reject状态,不管有没有return,都会被catch方法的回调收到。同时,async内只要有一个await语句后面的Promise变为reject,那么整个async函数都会中断执行。如果不要中断后面的操作,可以将这个await放进try..catch结构里面;或者在await的Promise对象后添加catch方法(反正就是加catch,把它捕获掉)

<3>并发和继发处理

async实现并发,可以用Promise.all()获取arr.push(await fun)

并发和继发交杂的,可以用多个async函数,函数内是继发。

<4>异步遍历器---提案

特点:调用next()不再直接返回{value,done},而是返回一个Promise,then之后的回调函数的参数为value和done

接口:Symbol.asynvIterator属性

异步遍历器的next方法可以连续调用,不用等Promise对象resolve,这种情况下next方法会累积起来,自动按照顺序运行下去。

for await...of,可以看作for...of的变种,用来遍历异步的Iterator接口

                        

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值