深入理解ES6 - Nicholas C.Zakas(二)

avatarplhDigital nomad
经典继续阅读,虽然好像越来越没动力去读了。

第八章 迭代器Iterator 和生成器Generator

循环语句的迭代器可以极大简便的简化数据操作,,例如for...in循环,...扩展符,甚至异步编程都是运用的迭代器。 也许你写过for循环

for (let index = 0; index < array.length; index++) {
  const element = array[index];
}

是不是很麻烦,迭代器随之诞生,它是为专门为迭代过程设计的接口,所有的迭代器对象都有一个next()方法,每次调用这个都返回一个结果对象,

{
  value:"下一个要返回的值", 
  done: "true/false"
}

为了深入了解迭代器,我们打算手写一个迭代器函数

function createIterator (items){
  var i = 0;
  return {
    next: function (){
      var done = (i >= items.length);
      var value = !done ? items[i++] : undefined;
      return { done,value }
    }
  }
}
var iterator = createIterator([1,2,3,4,5]);
console.log(iterator.next());  // {done: false, value: 1}
console.log(iterator.next());  // {done: false, value: 2}
console.log(iterator.next());  // {done: false, value: 3}
console.log(iterator.next());  // {done: false, value: 4}
console.log(iterator.next());  // {done: false, value: 5}
console.log(iterator.next());  // {done: true, value: undefined}
console.log(iterator.next());  // {done: true, value: undefined}
console.log(iterator.next());  // {done: true, value: undefined}

上面通过闭包原理简单实现了一个迭代器,迭代器内置next()方法,其实就是返回的对象有next()方法,以及一个私有变量i,他会自增。

function *createIterator(items) {
  items.forEach(arr=>{
    yield items[i]
  })
}
let iterator = createIterator([1,2,3]);
console.log(iterator.next());
function *createIterator(items) {
  for (let i = 0; i < items.length; i++) {
    yield items[i]
  }
}
let iterator = createIterator([1,2,3]);
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());

同样的道理,迭代器关键字,yield不能在其他函数内部使用,即便是箭头函数这种伪函数同样不行,

内建迭代器

es6中3种类型的集合对象,数组,map集合,set集合,他们都有以下3种对象

  • extries() 返回一个迭代器,其值为多个键值对。
  • values() 返回一个迭代器,其值为集合的值。
  • keys() 返回一个迭代器,其值为集合种的所有键名,

跳过吧,这东西看来无用。

高级迭代功能

给迭代器传递参数,给next方法传递参数,这个参数会成为上一个yield语句的返回值,

function *createIterator(){
  let first = yield 1;
  let second = yield first + 2;
  yield second + 3;
}
let iterator = createIterator();
console.log(iterator.next());      // {value: 1, done: false}
console.log(iterator.next(4));    // {value: 6, done: false}
console.log(iterator.next(5));    // {value: 8, done: false}
console.log(iterator.next());      // {value: undefined, done: true}

第一次调用next方法,无论传什么参数都会被丢弃。由于传给next()方法会代替上一次yield返回值,

异步执行任务

不如async+promise语法糖好用

JavaScript 中的类

JavaScript不支持面向对象,所以class语法糖应劫而生。

为何适用类

尽管类与自定义类型之间有诸多相似之处,我们仍然需要牢记他们的这些差异:

  • 函数声明可以被提升,而class不能被提升,真正执行声明语句之前,他们会一直存在于临时死区。
  • 类型声明中所有的代码将自动允许在严格模式,而且无法让其脱离严格模式。
由此可见,严格模式是大势所趋,后续代码默认适用严格模式
  • 在自定义类型中,需要通过Object.defineProperty()方法手工指定某个方法不可枚举,,而在类中,所有方法都不可枚举。
  • 每个类中都有[[Construct]]的内部方法,通过关键字new调用。
  • 适用关键字new以外的方式,调用类的构造函数会导致程序抛出错误。
  • 在类中,修改类名会报错。

以下是class语法糖的原生写法

let PersonType2 = (function (){
  "use strict";
  const PersonType2 = function (name){
    if(typeof new.target === 'undefined'){
      throw new Error('必须通过new 关键字调用');
    }
    this.name = name;
  }
  Object.defineProperty(PersonType2, "sayName", {
    value: function (){
      // 确保不会通过关键字new 调用方法
      if (typeof new.target !== "undefined") {
        throw new Error('不能通过 new 关键字调用');
      }
      console.log(this.name);
    },
    enumerable: false,
    writable: true,
    configurable: true,
  })
  return PersonType2;
}())

类class名字可以在外部修改,但是不能在内部修改。类是一等公民。

静态成员

在ECMAscript5或者早期版本,直接将方法添加到构造函数中模拟静态成员是一种常见的形态。例如:

function PersonType(name){
  this.name = name;
}
// 静态方法
PersonType.create = function(name){
  return new PersonType(name);
}
// 实例方法
PersonType.prototype.sayname = function(){
  console.log(this.name)
}
var person = PersonType.create('Nicholas')

下面是等价代码es6版本

class PersonClass {
  constructor(name) {
    this.name = name;
  }
  sayname(){
      console.log(this.name)
  }
  static create(){
      return new PersonClass(name);
  }
}

关于super,

super就是代表的扩展的类 extends class 在es6的时候没有静态属性,只有静态方法,但是在es7中却可以这样定义静态属性。非常神奇。。。