使您的objects可迭代

英文原文: https://krasimirtsonev.com/blog/article/make-your-objects-iterable

什么是可迭代协议

可迭代协议允许JavaScript对象定义或自定义其迭代行为。

资源

换句话说,JavaScript中的某些语句仅适用于实现该协议的对象。例如… of:

const arr = ['a', 'b', 'c'];

for (const item of arr) {
  console.log(item);
}

// output
// "a"
// "b"
// "c"

这是可行的,因为JavaScript内置数组 实现了可迭代的协议。为了将给定的对象称为“可迭代”,该对象需要一个@@iterator方法。考虑下面的代码:

const arr = [1, 2, 3, 4];

console.log(arr[Symbol.iterator])
// outputs:
// ƒ values() { [native code] }

Symbol.iterator基本上是一个常数@@iterator。

每当我们需要遍历对象时,@@iterator都会调用其方法。该方法应返回一个迭代器。

迭代器

迭代器是具有.next方法的每个对象。一旦调用,该方法应返回带有donevalue键的另一个对象。想象一下,我们有一个项目列表。donetrue当我们去了所有项目。每次next触发时,我们都会返回列表中的下一项。这是一个例子:

const user = {
  firstName: 'Krasimir',
  lastName: 'Tsonev',
  skills: ['JavaScript', 'HTML', 'CSS'],
  [Symbol.iterator]: function () {
    let i = 0;

    return {
      next: () => ({
        value: this.skills[ i++ ],
        done: i > this.skills.length
      })
    }
  }
}

for (const skill of user) {
  console.log(skill);
}
// output:
// "JavaScript"
// "HTML"
// "CSS"

user 绝对不是数组,但是我们可以对其进行迭代,因为我们实现了可迭代的协议。

对象的熟练接口

太酷了,但是我有另一个用例成为了我的最爱。销毁资料时使用相同的协议。例如:

const a = ['a', 'b', 'c'];
const [ first, , last ] = a;

console.log(first);
console.log(last);
// outputs:
// "a"
// "c"

现在,让我们返回上一部分中的代码。我们定义user对象的那个。想象一下,我们需要创建一种方法来更改用户名并添加新技能。我们可能会遵循以下条件:

const user = {
  firstName: 'Krasimir',
  lastName: 'Tsonev',
  skills: ['JavaScript', 'HTML', 'CSS'],
  changeName(newName) {
    const parts = newName.split(' ');
    this.firstName = parts[0];
    this.lastName = parts[1];
  },
  addSkill(newSkill) {
    this.skills.push(newSkill);
  }
}

user.changeName('Jon Snow');
user.addSkill('React');

console.log(`${ user.firstName } ${ user.lastName }`);
console.log(user.skills);

// outputs:
// Jon Snow
// (4) ["JavaScript", "HTML", "CSS", "React"]

那行得通,但是以下API呢?

const [ changeUserName, addUserSkill ] = user;

changeUserName('Jon Snow');
addUserSkill('React');

当然,如果我们没有实现适当的迭代器,那实际上是不可能的。上面的一个遍历skills数组。让我们定义一个新的调用api,它包含我们的changeNameaddSkill方法。

const user = {
  firstName: 'Krasimir',
  lastName: 'Tsonev',
  skills: ['JavaScript', 'HTML', 'CSS'],
  [Symbol.iterator]: function () {
    const api = [
      newName => {
        const parts = newName.split(' ');
        this.firstName = parts[0];
        this.lastName = parts[1];
      },
      newSkill => {
        this.skills.push(newSkill);
      }
    ]
    let i = 0;

    return {
      next: () => ({
        value: api[ i++ ],
        done: i > api.length
      })
    }
  }
}

const [ changeUserName, addUserSkill ] = user;

changeUserName('Jon Snow');
addUserSkill('React');

console.log(`${ user.firstName } ${ user.lastName }`);
console.log(user.skills);

我们使用析构来访问user对象上的方法。我在这里喜欢的是我必须处理公正的功能而忘记了存在的事实user。当然,我们可以使用bind以下方法实现相同的目的:

const changeUserName = user.changeName.bind(user);

英文原文: https://krasimirtsonev.com/blog/article/make-your-objects-iterable

李, 国轩