ES6学习笔记(二)(feat阮一峰ES6教程)

ES6 新增及扩展

内容简介:
十二、Symbol
十三、Set 和 Map 数据结构
十四、Proxy
十五、Reflect
十六、Promise 对象
十七、Iterator 和 for…of 循环
十八、Generator 函数的语法
十九、Generator 函数的异步应用
二十、async 函数
二十一、Class 的基本语法
二十二、Class 的继承
二十三、Module 的语法
二十四、Module 的加载实现
二十七、异步遍历器

十二、Symbol

1. 概述
    1)ES6引入了一种新的数据类型Symbol,表示独一无二的值。它是JavaScript语言中的第七种数据类型;
    2)Symbol值通过Symbol函数生成;eg:let s = Symbol();其值通过类型检测会显示为Symbol类型;
    3)Symbol可以接受一个字符串作为参数,表示对Symbol实例的描述,主要是为了在控制台显示,或者转为字符串时,容易区分;
    4) 如果 Symbol 的参数是一个对象,就会调用该对象的toString方法,将其转为字符串,然后才生成一个 Symbol 值。
    5) 注意,Symbol函数的参数只是表示对当前 Symbol 值的描述,因此相同参数的Symbol函数的返回值是不相等的。
    6) Symbol 值不能与其他类型的值进行运算,会报错。
    7)Symbol值可以显示的转为字符串和布尔值,但是不能转换为数值;
2. Symbol.prototype.description
    1)创建Symbol值是可以传入字符串参数作为该值的描述,但是获取该描述时需要将该值显示的转为字符串才可以获取。ES2019提供了一个实例属性description,直接返回Symbol的描述;
3. 作为属性名的Symbol
    1)由于每一个Symbol值都是不相等的,将Symbol值作为对象的属性名,可以保证不会出现同名属性;
    2)使用Symbol值作为对象属性名时,不能使用点运算符。因为点运算符后面总是字符串,所以不会读取Symbol值作为标识符所指代的值,导致设置的实际是个字符串属性名,而不是一个Symbol值;
    3)在对象内部,使用Symbol值定义属性时,Symbol值必须放在“[]”中;
    4) Symbol 类型还可以用于定义一组常量,保证这组常量的值都是不相等的。
    5)Symbol值作为属性名时,该属性是公开属性,不是私有属性;
4. 实例:消除魔法字符串
    1) 魔术字符串指的是,在代码之中多次出现、与代码形成强耦合的某一个具体的字符串或者数值。它的出现,不利于将来的修改和维护。
    2)常用的消除魔法字符串的方法就是把它赋值给一个变量;
5. 属性名的遍历
    1) Symbol 作为属性名,遍历对象的时候,该属性不会出现在for...in、for...of循环中,也不会被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。
    2) Object.getOwnPropertySymbols()方法可以获取有Symbol值作为属性名的属性。该方法返回一个数组,成员是当前对象所有用作属性名的Symbol值;
    3) Reflect.ownKeys()方法可以返回所有类型的键名,包括常规键名和 Symbol 键名。
    4) 由于以 Symbol 值作为键名,不会被常规方法遍历得到。我们可以利用这个特性,为对象定义一些非私有的、但又希望只用于内部的方法
6. Symbol.for()、Symbol.keyFor()
    1) Symbol.for()接受一个字符串作为参数,然后搜索有没有以该参数作为名称的 Symbol 值。如果有,就返回这个 Symbol 值,否则就新建一个以该字符串为名称的 Symbol 值,并将其注册到全局。
    2) Symbol.for()与Symbol()这两种写法,都会生成新的 Symbol。它们的区别是,Symbol.for()会被登记在全局环境中供搜索,Symbol()不会。Symbol.for()不会每次调用就返回一个新的 Symbol 类型的值,而是会先检查给定的key是否已经存在,如果不存在才会新建一个值。
    3) Symbol.keyFor()方法返回一个已登记的 Symbol 类型值的key。 Symbol.for()为 Symbol 值登记的名字,是全局环境的,不管有没有在全局环境运行。
    4) Symbol.for()的这个全局登记特性,可以用在不同的 iframe 或 service worker 中取到同一个值。
7. 实例:模块的Singleton模式
    1) Singleton 模式指的是调用一个类,任何时候返回的都是同一个实例。
    2)该模式存在的意义: 对于 Node 来说,模块文件可以看成是一个类。为了保证每次执行这个模块文件,都返回的都是同一个实例。但是如果多次运行此脚本,每次得到的值还是不一样。虽然Node可以缓存脚本,但是用户也可以手动清除缓存,所有该模式也不是绝对可靠的;
8. 内置的Symbol值
    1) 除了定义自己使用的 Symbol 值以外,ES6 还提供了 11 个内置的 Symbol 值,指向语言内部使用的方法。
    2)Symbol.hasInstance:该属性指向一个内部方法, 当其他对象使用instanceof运算符,判断是否为该对象的实例时,会调用这个方法。该方法可以设置如何对instanceof操作的数据进行返回;
    3)Symbol.isConcatSpreadable: 对象的该属性等于一个布尔值,表示该对象用于Array.prototype.concat()时,是否可以展开。其值默认为undefined;而该值等于true时,其效果也是可以展开;
        (1)数组对于该属性的默认行为是打开;
        (2) 类似数组的对象正好相反,默认不展开。它的Symbol.isConcatSpreadable属性设为true,才可以展开。
        (3)Symbol.isConcatSpreadable属性也可以定义在类里面;
    4)Symbol.species: 对象的该属性指向一个构造函数。创建衍生对象时,会使用该属性。
        (1) 定义了Symbol.species属性,创建衍生对象时就会使用这个属性返回的函数,作为构造函数。
        (2) 定义Symbol.species属性要采用get取值器。
        (3) Symbol.species的作用在于,实例对象在运行过程中,需要再次调用自身的构造函数时,会调用该属性指定的构造函数。它主要的用途是,有些类库是在基类的基础上修改的,那么子类使用继承的方法时,可能希望返回基类的实例,而不是子类的实例。
    5)Symbol.match: 对象的Symbol.match属性,指向一个函数。当执行str.match(myObject)时,如果该属性存在,会调用它,返回该方法的返回值。
    6)Symbol.replace: 对象的Symbol.replace属性指向一个方法,当该对象被String.prototype.replace方法调用时,会返回该方法的返回值。
        (1) Symbol.replace方法会收到两个参数,第一个参数是replace方法正在作用的对象,第二个参数是替换后的值;
    7)Symbol.search: 对象的Symbol.search属性,指向一个方法,当该对象被String.prototype.search方法调用时,会返回该方法的返回值。
    8)Symbol.split: 对象的Symbol.split属性,指向一个方法,当该对象被String.prototype.split方法调用时,会返回该方法的返回值。
    9)Symbol.iterator: 对象的Symbol.iterator属性,指向该对象的默认遍历器方法。 对象进行for...of循环时,会调用Symbol.iterator方法,返回该对象的默认遍历器;
    10)Symbol.toPrimitive: 对象的Symbol.toPrimitive属性,指向一个方法。该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。
        (1) Symbol.toPrimitive被调用时,会接受一个字符串参数,表示当前运算的模式,一共有三种模式。
                I.  Number:该场合需要转成数值
                II.  String:该场合需要转成字符串
                III.  Default:该场合可以转成数值,也可以转成字符串
    11)Symbol.toStringTag: 对象的Symbol.toStringTag属性,指向一个方法。在该对象上面调用Object.prototype.toString方法时,如果这个属性存在,它的返回值会出现在toString方法返回的字符串之中,表示对象的类型。
    12)Symbol.unscopables: 对象的Symbol.unscopables属性,指向一个对象。该对象指定了使用with关键字时,哪些属性会被with环境排除。

十三、Set和Map数据结构

1. set
    1)基本用法
        (1)ES6提供了新的数据结构Set,类似于数组,但是其成员的值都是唯一的,没有重复的值;
        (2)Set本身是一个构造函数,用来生成Set数据结构;
        (3)Set函数可以接受一个数组(或具有Iterator接口的其他数据结构)作为参数,用来初始化;
        (4)Set函数可以用于数组的去重 => [...new Set(array)]
        (5)向Set中传值时不会发生类型转换,Set内部判断两个值是否相等时,使用的类似于(===),区别在于Set会判定NaN等于自身,而“===”中NaN判定不等于自身;
    2)Set实例的属性和方法
        (1)Set结构的实例有一下属性:
                I.  Set.prototype.constructor:构造函数,默认就是Set函数。
                II.  Set.prototype.size:返回Set实例的成员总数。
        (2)Set结构的方法分为操作方法(用于操作数据)和遍历方法(用于遍历成员)
                I. Set结构有四个用于操作数据的方法:
                    a.  Set.prototype.add(value):添加某个值,返回 Set 结构本身。
                    b.  Set.prototype.delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
                    c.  Set.prototype.has(value):返回一个布尔值,表示该值是否为Set的成员。
                    d.  Set.prototype.clear():清除所有成员,没有返回值。
                II.  Set 结构的实例有四个遍历方法,可以用于遍历成员。
                    a.  Set.prototype.keys():返回键名的遍历器。
                    b.  Set.prototype.values():返回键值的遍历器。
                    c.   Set.prototype.entries():返回键值对的遍历器。
                    d.  Set.prototype.forEach():使用回调函数遍历每个成员。
                    e.  需要特别指出的是,Set的遍历顺序就是插入顺序。这个特性有时非常有用,比如使用 Set保存一个回调函数列表,调用时就能保证按照添加顺序调用。
                III. keys()、values()、entries()
                    a.  keys方法、values方法、entries方法返回的都是遍历器对象。由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys方法和values方法的行为完全一致。
                IV.  Set 结构的实例默认可遍历,它的默认遍历器生成函数就是它的values方法。这意味着,可以省略values方法,直接用for...of循环遍历 Set。
                V. forEach(): Set 结构的实例与数组一样,也拥有forEach方法,用于对每个成员执行某种操作,没有返回值。 forEach方法还可以有第二个参数,表示绑定处理函数内部的this对象。
                VI. 遍历的应用
                    a.  扩展运算符(...)内部使用for...of循环,所以也可以用于 Set 结构。
                    b.  扩展运算符和 Set 结构相结合,就可以去除数组的重复成员。
                    c.  数组的map和filter方法也可以间接用于 Set 。 使用 Set 可以容易地实现并集、交集和差集;
                    d.  如果想在遍历操作中,同步改变原来的 Set 结构,目前没有直接的方法,但有两种变通方法。一种是利用原 Set 结构映射出一个新的结构,然后赋值给原来的 Set 结构;另一种是利用Array.from方法。
2. WeakSet
    1)含义: WeakSet 结构与 Set 类似,也是不重复的值的集合。
        (1) WeakSet与 Set 有两个区别:
                I.  WeakSet 的成员只能是对象,而不能是其他类型的值。
                II.  WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中。
        (2) WeakSet 适合临时存放一组对象,以及存放跟对象绑定的信息。只要这些对象在外部消失,它在 WeakSet 里面的引用就会自动消失。
        (3) WeakSet 的成员是不适合引用的,因为它会随时消失。另外,由于 WeakSet 内部有多少个成员,取决于垃圾回收机制有没有运行,运行前后很可能成员个数是不一样的,而垃圾回收机制何时运行是不可预测的,因此 ES6 规定 WeakSet 不可遍历。
    2)语法
        (1) WeakSet 是一个构造函数,可以使用new命令,创建 WeakSet 数据结构。
        (2) WeakSet 可以接受一个数组或类似数组的对象作为参数。(实际上,任何具有 Iterable 接口的对象,都可以作为 WeakSet 的参数。)该数组的所有成员,都会自动成为 WeakSet 实例对象的成员。
        (3) 注意,是数组的成员成为 WeakSet 的成员,而不是数组本身。所以数组的成员只能是对象。
        (4) WeakSet 结构有以下三个方法:
                I.  WeakSet.prototype.add(value):向 WeakSet 实例添加一个新成员。
                II.  WeakSet.prototype.delete(value):清除 WeakSet 实例的指定成员。
                III.  WeakSet.prototype.has(value):返回一个布尔值,表示某个值是否在 WeakSet 实例之中。
        (5) WeakSet 没有size属性,没有办法遍历它的成员。
        (6) WeakSet 的一个用处,是储存 DOM 节点,而不用担心这些节点从文档移除时,会引发内存泄漏。
3. Map
    1)含义和基本用法
        (1) ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。
        (2) Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。
        (3) 作为构造函数,Map 也可以接受一个数组作为参数。该数组的成员是一个个表示键值对的数组。
        (4) 事实上,不仅仅是数组,任何具有 Iterator 接口、且每个成员都是一个双元素的数组的数据结构都可以当作Map构造函数的参数。这就是说,Set和Map都可以用来生成新的 Map。
        (5) 如果对同一个键多次赋值,后面的值将覆盖前面的值。 如果读取一个未知的键,则返回 undefined。 注意,只有对同一个对象的引用,Map 结构才将其视为同一个键。同样的值的两个实例,在 Map 结构中被视为两个键。
        (6) Map 的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键。这就解决了同名属性碰撞(clash)的问题;
        (7) 如果 Map 的键是一个简单类型的值(数字、字符串、布尔值),则只要两个值严格相等,Map将其视为一个键。另外,undefined和null也是两个不同的键。虽然NaN不严格相等于自身,但Map 将其视为同一个键。
    2)实例的属性和操作方法
        (1)size属性:size属性返回Map结构的成员的总数;
        (2)Map.prototype.set(key, value):set方法设置键名key对应的键值为value,然后返回整个Map结构;如果key已经有值,则键值会被跟新,没有就新生成改建。set方法返回的是当前的Map对象,所以设置set时可以使用链式写法;
        (3)Map.prototype.get(key):该方法读取key对应的键值,没有则返回undefined;
        (4)Map.prototype.has(key):该方法返回一个布尔值,表示某个键是否在当前的Map对象中;
        (5)Map.prototype.delete(key):该方法用来删除某个键,成功返回true,失败返回false;
        (6)Map.prototype.clear():该方法清除所以Map对象中的成员,没有返回值;
    3)遍历方法
        (1)Map结构原生提供3个遍历器生成函数和一个遍历方法
                I.  Map.prototype.keys():返回键名的遍历器。
                II.  Map.prototype.values():返回键值的遍历器。
                III.  Map.prototype.entries():返回所有成员的遍历器。
                IV.  Map.prototype.forEach():遍历 Map 的所有成员。
                V.  需要特别注意的是,Map 的遍历顺序就是插入顺序。
        (2) Map 结构的默认遍历器接口(Symbol.iterator属性),就是entries方法。
        (3) Map 结构转为数组结构,比较快速的方法是使用扩展运算符(...)。
        (4) 结合数组的map方法、filter方法,可以实现 Map 的遍历和过滤(Map 本身没有map和filter方法)。
        (5) Map 的forEach方法,与数组的forEach方法类似,也可以实现遍历。 forEach方法还可以接受第二个参数,用来绑定this。
    4)与其他数据结构的相互转换
        (1)Map转为数组: Map 转为数组最方便的方法,就是使用扩展运算符(...)。
        (2)数组转为Map:将数组传入Map的构造函数,就可以把数组转为Map结构;
        (3)Map转为对象: 如果所有 Map 的键都是字符串,它可以无损地转为对象。 如果有非字符串的键名,那么这个键名会被转成字符串,再作为对象的键名。
        (4)对象转为Map: 对象转为 Map 可以通过Object.entries()。
        (5)Map转为JSON: Map 转为 JSON 要区分两种情况。一种情况是,Map 的键名都是字符串,这时可以选择转为对象 JSON。 另一种情况是,Map 的键名有非字符串,这时可以选择转为数组JSON。
        (6)JSON转为Map: JSON 转为 Map,正常情况下,所有键名都是字符串。 有一种特殊情况,整个 JSON 就是一个数组,且每个数组成员本身,又是一个有两个成员的数组。这时,它可以一一对应地转为 Map。这往往是 Map 转为数组 JSON 的逆操作。
4. WeakMap
    1)含义: WeakMap结构与Map结构类似,也是用于生成键值对的集合。
        (1) WeakMap与Map的区别有两点:
                I.  WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名。
                II.  WeakMap的键名所指向的对象,不计入垃圾回收机制。
        (2) WeakMap 就是为了解决需要在某些对象上存放一些数据,但是再不需要这个对象时不用手动去删除这个引用。因为它的键名所引用的对象都是弱引用,即垃圾回收机制不将该引用考虑在内。因此,只要所引用的对象的其他引用都被清除,垃圾回收机制就会释放该对象所占用的内存。
        (3) 基本上,如果你要往对象上添加数据,又不想干扰垃圾回收机制,就可以使用 WeakMap。一个典型应用场景是,在网页的 DOM 元素上添加数据,就可以使用WeakMap结构。当该 DOM 元素被清除,其所对应的WeakMap记录就会自动被移除。
        (4) WeakMap的专用场合就是,它的键所对应的对象,可能会在将来消失。WeakMap结构有助于防止内存泄漏。
        (5) WeakMap 弱引用的只是键名,而不是键值。键值依然是正常引用。
    2)WeakMap的语法
        (1)WeakMap 是一个构造函数,可以使用new命令,创建 WeakSet 数据结构。
        (2) WeakMap 与 Map 在 API 上的区别主要是两个:
            I.  没有遍历操作(即没有keys()、values()和entries()方法),也没有size属性。
            II.  为了防止出现不确定性,统一规定不能取到键名。二是无法清空,即不支持clear方法。因此, WeakMap只有四个方法可用:get()、set()、has()、delete()。
    3)WeakMap的示例
        (1) WeakMap 的例子很难演示,因为无法观察它里面的引用会自动消失。此时,其他引用都解除了,已经没有引用指向 WeakMap 的键名了,导致无法证实那个键名是不是存在。
    4)WeakMap的用途
        (1) WeakMap 应用的典型场合就是 DOM 节点作为键名。 一旦这个 DOM 节点删除,该状态就会自动消失,不存在内存泄漏风险。
        (2) WeakMap 的另一个用处是部署私有属性。因为 如果删除实例,它们也就随之消失,不会造成内存泄漏。

十四、Proxy

1. 概述
    1) Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。
    2) Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。
    3)ES6原生提供了Proxy构造函数,用来生成Proxy实例:var proxy = new Proxy(target, handler);
    4) Proxy 对象的所有用法,都是上面这种形式,不同的只是handler参数的写法。其中,new Proxy()表示生成一个Proxy实例,target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。
    5) 注意,要使得Proxy起作用,必须针对Proxy实例进行操作,而不是针对目标对象进行操作。
    6) 如果handler没有设置任何拦截,那就等同于直接通向原对象。
    7) 一个技巧是将 Proxy 对象,设置到object.proxy属性,从而可以在object对象上调用。
    8) Proxy 实例也可以作为其他对象的原型对象。
    9) 同一个拦截器函数,可以设置拦截多个操作。 对于可以设置、但没有设置拦截的操作,则直接落在目标对象上,按照原先的方式产生结果。
2. Proxy实例的方法
    1)get()
        (1) 该方法用于拦截某个属性的读取操作,可以接受三个参数,依次为目标对象、属性名和 proxy 实例本身(严格地说,是操作行为所针对的对象),其中最后一个参数可选。
        (2)get方法可以继承,只要把拦截操作定义在Prototype对象上面即可。
        (3) 利用 Proxy,可以将读取属性的操作(get),转变为执行某个函数,从而实现属性的链式操作。
        (4) 如果一个属性不可配置(configurable)且不可写(writable),则 Proxy 不能修改该属性,否则通过 Proxy 对象访问该属性会报错。
    2)set()
        (1) set方法用来拦截某个属性的赋值操作,可以接受四个参数,依次为目标对象、属性名、属性值和 Proxy 实例本身,其中最后一个参数可选。
        (2) 利用set方法,还可以数据绑定,即每当对象发生变化时,会自动更新 DOM。
        (3) 在对象上面设置内部属性,这些属性不应该被外部使用。结合get和set方法,就可以做到防止这些内部属性被外部读写。
        (4) set方法的第四个参数receiver,指的是原始的操作行为所在的那个对象,一般情况下是proxy实例本身;
        (5) 注意,如果目标对象自身的某个属性,不可写且不可配置,那么set方法将不起作用。
        (6) 注意,严格模式下,set代理如果没有返回true,就会报错。
    3)apply()
        (1) apply方法拦截函数的调用、call和apply操作。
        (2) apply方法可以接受三个参数,分别是目标对象、目标对象的上下文对象(this)和目标对象的参数数组。
        (3) 另外,直接调用Reflect.apply方法,也会被拦截。
    4)has()
        (1) has方法用来拦截HasProperty操作,即判断对象是否具有某个属性时,这个方法会生效。典型的操作就是in运算符。
        (2) has方法可以接受两个参数,分别是目标对象、需查询的属性名。
        (3) 如果原对象不可配置或者禁止扩展,这时has拦截会报错。
        (4) 值得注意的是,has方法拦截的是HasProperty操作,而不是HasOwnProperty操作,即has方法不判断一个属性是对象自身的属性,还是继承的属性。
        (5) 虽然for...in循环也用到了in运算符,但是has拦截对for...in循环不生效。
    5)construct()
        (1) construct方法用于拦截new命令;
                eg:

            var handle = {
                construct(target, args, newTarget) {
                    return new target(...args)
                }
            }
        (2) construct方法可以接受三个参数。
                I.  target:目标对象
                II.  args:构造函数的参数对象
                III.  newTarget:创造实例对象时,new命令作用的构造函数
        (3) construct方法返回的必须是一个对象,否则会报错。
    6)deleteProperty()
        (1) deleteProperty方法用于拦截delete操作,如果这个方法抛出错误或者返回false,当前属性就无法被delete命令删除。
        (2) 注意,目标对象自身的不可配置(configurable)的属性,不能被deleteProperty方法删除,否则报错。
    7)defineProperty()
        (1) defineProperty方法拦截了Object.defineProperty操作。
        (2) 注意,如果目标对象不可扩展(non-extensible),则defineProperty不能增加目标对象上不存在的属性,否则会报错。另外,如果目标对象的某个属性不可写(writable)或不可配置(configurable),则defineProperty方法不得改变这两个设置。
    8)getOwnPropertyDescriptor()
        (1) getOwnPropertyDescriptor方法拦截Object.getOwnPropertyDescriptor(),返回一个属性描述对象或者undefined。
    9)getPropertyOf()
        (
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值