为什么我不能覆盖字符串实例的迭代器而不是 String.prototype?

问题描述

这段代码允许使用扩展运算符来分割句子的单词,正如预期的那样:

String.prototype[Symbol.iterator] = function* () {
  yield* this.split(' ');
}

const result = [...'The quick brown fox jumped over the lazy dog.'];

console.log(result);

我想尝试在实例级别覆盖 Symbol.iterator 以避免改变 String.prototype,这是我的尝试:

const sentence = 'The quick brown fox jumped over the lazy dog.';

sentence[Symbol.iterator] = function* () {
  yield* this.split(' ');
}

const result = [...sentence];

console.log(result); // Not the expected result

console.log(sentence[Symbol.iterator]); // Native String iterator

原生 String.prototype[Symbol.iterator] 在实例级别看起来不可覆盖,我不明白为什么。我的猜测是 sentence一个原始实例,而不是真正的 String 实例。那么我如何实现这一目标?

解决方法

因为字符串原语不是字符串对象。相反,当您对它们使用方法(或访问它们的属性)时,就好像它们首先被转换为对象。 (在松散模式下,实际上创建了一个对象,但是当方法返回时该对象被丢弃,除非某些东西保留对它的引用。)这意味着这段代码修改了一个永远不会保存的临时对象(不是字符串原语;字符串原语是不可变的):

sentence[Symbol.iterator] = function* () {
  yield* this.split(' ');
}

因为它从来没有保存过,以后当你使用迭代器时,你会得到默认的。

您可以改为创建字符串对象:

const sentence = new String('The quick brown fox jumped over the lazy dog.');
// −−−−−−−−−−−−−−^^^^^^^^^^^−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−^

sentence[Symbol.iterator] = function* () {
    yield* this.split(' ');
};

const result = [...sentence];

console.log(result);

您可以在字符串上定义自己的 words 方法:

Object.defineProperty(String.prototype,"words",{
    *value() {
        yield* this.split(" ");
    },configurable: true,writable: true,});

const sentence = "The quick brown fox jumped over the lazy dog.";

const result = [...sentence.words()];

console.log(result);

强烈不鼓励修改库或类似库中的内置原型,但如果您意识到未来命名冲突等的危险,您可以在自己的页面或应用程序上进行.

或者作为 VLAZ points out,只需编写一个将字符串传入的函数。 :-)

,

您不能更改“实例”,因为原语不是一个。例如 "hello" 只是一个字符串原语,它自己没有任何属性。与 42 等其他原语相同。

调用任何对象方法或在原语上使用属性都会将值转换为仅用于该操作的对象:("hello").split(" ")(42).toFixed(2)。原语本身是不可变的。

无需污染任何原型或更改任何对象,您只需创建一个可重用的生成器函数并传播其结果:

const sentence = 'The quick brown fox jumped over the lazy dog.';

function* toWords(str) {
  yield* str.split(' ');
}

const result = [...toWords(sentence)];

console.log(result);