|
|
// 启用严格模式,在严格模式下,JavaScript 代码会遵循更严格的语法和行为规范,有助于发现更多潜在的错误并提升代码质量。
|
|
|
"use strict";
|
|
|
|
|
|
// 引入 `es5-ext/array/#/clear` 模块,可能用于清空数组相关操作,从模块路径推测它与扩展的数组功能相关。
|
|
|
// 这里将引入的模块赋值给 `clear` 变量,以便后续在代码中使用对应的功能。
|
|
|
var clear = require("es5-ext/array/#/clear")
|
|
|
// 引入 `es5-ext/object/assign` 模块,通常用于将多个对象的属性合并到一个目标对象上,类似 `Object.assign` 的功能,
|
|
|
// 但可能是经过扩展或具有特定兼容性处理的版本,赋值给 `assign` 变量供后续使用。
|
|
|
, assign = require("es5-ext/object/assign")
|
|
|
// 引入 `es5-ext/object/valid-callable` 模块,可能用于验证某个值是否为可调用的(比如函数),保障后续代码中对可调用对象的使用安全,
|
|
|
// 并将其赋值给 `callable` 变量。
|
|
|
, callable = require("es5-ext/object/valid-callable")
|
|
|
// 引入 `es5-ext/object/valid-value` 模块,大概是用于验证某个值是否符合特定的有效性要求,具体取决于该模块内部实现,
|
|
|
// 赋值为 `value` 变量供代码中进行值的有效性判断使用。
|
|
|
, value = require("es5-ext/object/valid-value")
|
|
|
// 引入 `d` 模块,从代码使用情况看,它可能是一个用于创建具有特定属性描述符的对象属性的工具函数(类似 `Object.defineProperty` 的简化或扩展用法),
|
|
|
// 具体功能需看其内部实现细节,赋值给 `d` 变量以便后续使用。
|
|
|
, d = require("d")
|
|
|
// 引入 `d/auto-bind` 模块,推测是用于自动绑定函数上下文(比如将函数的 `this` 指向特定对象)的功能模块,
|
|
|
// 赋值给 `autoBind` 变量用于后续相关操作。
|
|
|
, autoBind = require("d/auto-bind")
|
|
|
// 引入 `es6-symbol` 模块,用于在 JavaScript 中支持 `Symbol` 类型,`Symbol` 常用于创建唯一的、不可变的标识符,
|
|
|
// 在这里用于定义一些特殊的属性键等情况,赋值给 `Symbol` 变量供代码使用。
|
|
|
, Symbol = require("es6-symbol");
|
|
|
|
|
|
// 获取 `Object.defineProperty` 和 `Object.defineProperties` 方法的引用,分别用于定义单个对象属性和多个对象属性,
|
|
|
// 后续会使用它们来创建具有特定行为和属性描述符的对象属性,确保对象属性的正确设置和访问控制等。
|
|
|
var defineProperty = Object.defineProperty, defineProperties = Object.defineProperties, Iterator;
|
|
|
|
|
|
// 将 `Iterator` 函数作为模块的导出内容,同时在当前模块内也使用 `Iterator` 变量来指代这个函数,
|
|
|
// 该函数用于创建一个迭代器对象,用于遍历某个列表(`list`)并处理相关的更新操作(如添加、删除、清空元素等情况)。
|
|
|
module.exports = Iterator = function (list, context) {
|
|
|
// 检查是否使用 `new` 关键字来调用 `Iterator` 构造函数,如果不是,则抛出一个 `TypeError` 异常,
|
|
|
// 这是遵循构造函数的常规使用规范,确保对象实例化的正确方式。
|
|
|
if (!(this instanceof Iterator)) throw new TypeError("Constructor requires 'new'");
|
|
|
// 使用 `Object.defineProperties` 方法为当前实例(`this`)定义三个属性:`__list__`、`__context__` 和 `__nextIndex__`。
|
|
|
// 通过 `d` 函数(具体功能由其模块实现决定)来设置属性的描述符,使其具有特定的可写性(`"w"` 表示可写)等特征。
|
|
|
// `__list__` 属性存储传入的要迭代的列表数据(通过 `value` 函数验证其有效性后赋值),
|
|
|
// `__context__` 属性存储传入的上下文对象(如果有的话),`__nextIndex__` 属性初始化为 `0`,用于记录迭代的位置索引。
|
|
|
defineProperties(this, {
|
|
|
__list__: d("w", value(list)),
|
|
|
__context__: d("w", context),
|
|
|
__nextIndex__: d("w", 0)
|
|
|
});
|
|
|
// 如果没有传入上下文对象(`context` 为 `null` 或 `undefined`),则直接返回,不进行后续与上下文相关的操作(比如监听上下文事件等)。
|
|
|
if (!context) return;
|
|
|
// 使用 `callable` 函数验证上下文对象的 `on` 方法是否是可调用的(即是否为函数),确保后续可以安全地进行事件监听绑定操作。
|
|
|
callable(context.on);
|
|
|
// 在上下文对象上监听 `_add` 事件,当该事件触发时,调用当前迭代器实例的 `_onAdd` 方法进行处理,
|
|
|
// 这样迭代器可以根据列表元素添加的情况更新自身的迭代状态等信息。
|
|
|
context.on("_add", this._onAdd);
|
|
|
// 类似地,监听 `_delete` 事件,触发时调用 `_onDelete` 方法,用于处理列表元素删除时迭代器的相应调整。
|
|
|
context.on("_delete", this._onDelete);
|
|
|
// 监听 `_clear` 事件,触发时调用 `_onClear` 方法,以应对列表被清空的情况对迭代器状态进行处理。
|
|
|
context.on("_clear", this._onClear);
|
|
|
};
|
|
|
|
|
|
// 删除 `Iterator` 函数原型对象上的 `constructor` 属性,这样在使用迭代器实例时,不会默认暴露其构造函数引用,
|
|
|
// 可能是出于封装或避免不必要的外部访问构造函数的考虑。
|
|
|
delete Iterator.prototype.constructor;
|
|
|
|
|
|
// 使用 `Object.defineProperties` 方法为 `Iterator` 函数的原型对象(`Iterator.prototype`)定义多个属性和方法,
|
|
|
// 通过 `assign` 函数将多个属性描述对象合并后进行定义,实现了迭代器的核心功能以及相关的辅助方法。
|
|
|
defineProperties(
|
|
|
Iterator.prototype,
|
|
|
assign(
|
|
|
{
|
|
|
// `_next` 方法是迭代器内部用于获取下一个元素索引的私有方法(以下划线开头通常表示私有方法,虽然 JavaScript 并没有真正的私有方法概念,但这是一种约定)。
|
|
|
// 它的逻辑如下:
|
|
|
// 如果 `__list__` 属性不存在(可能表示列表已经被清空或者未正确初始化等情况),则返回 `undefined`,表示迭代结束。
|
|
|
// 如果存在 `__redo__` 属性(这个属性可能用于记录需要重新处理的索引情况,具体看其使用场景),则从 `__redo__` 数组中取出第一个元素作为索引 `i`,
|
|
|
// 如果取出的元素不为 `undefined`,则返回该索引值,表示下一个要处理的元素索引;否则继续后续判断。
|
|
|
// 如果当前的 `__nextIndex__` 小于列表的长度(即还有未迭代的元素),则返回当前 `__nextIndex__` 的值,并将其自增 `1`,用于指向下一个待迭代元素的索引。
|
|
|
// 如果以上条件都不满足,说明迭代已经完成,调用 `_unBind` 方法进行一些清理和解除绑定操作,然后返回 `undefined`,表示迭代结束。
|
|
|
_next: d(function () {
|
|
|
var i;
|
|
|
if (!this.__list__) return undefined;
|
|
|
if (this.__redo__) {
|
|
|
i = this.__redo__.shift();
|
|
|
if (i!== undefined) return i;
|
|
|
}
|
|
|
if (this.__nextIndex__ < this.__list__.length) return this.__nextIndex__++;
|
|
|
this._unBind();
|
|
|
return undefined;
|
|
|
}),
|
|
|
// `next` 方法是符合迭代器协议的公开方法,用于获取下一个迭代元素的结果对象。
|
|
|
// 它调用 `_next` 方法获取下一个元素的索引,然后将索引传递给 `_createResult` 方法来创建并返回包含 `done`(表示是否迭代完成)和 `value`(迭代元素的值)属性的结果对象。
|
|
|
next: d(function () {
|
|
|
return this._createResult(this._next());
|
|
|
}),
|
|
|
// `_createResult` 方法用于根据传入的索引值创建迭代结果对象。
|
|
|
// 如果索引值为 `undefined`,表示迭代已经完成,返回一个 `{ done: true, value: undefined }` 的对象,符合迭代器协议中迭代结束的表示形式。
|
|
|
// 如果索引值不为 `undefined`,则表示还有元素可迭代,通过调用 `_resolve` 方法获取对应索引位置的元素值,并返回一个 `{ done: false, value: 元素值 }` 的对象,
|
|
|
// 表示迭代未结束且包含当前迭代元素的值。
|
|
|
_createResult: d(function (i) {
|
|
|
if (i === undefined) return { done: true, value: undefined };
|
|
|
return { done: false, value: this._resolve(i) };
|
|
|
}),
|
|
|
// `_resolve` 方法用于根据索引值从 `__list__` 列表中获取对应的元素,简单地返回 `__list__` 数组中指定索引位置的元素,
|
|
|
// 是获取迭代元素实际值的一个辅助方法。
|
|
|
_resolve: d(function (i) {
|
|
|
return this.__list__[i];
|
|
|
}),
|
|
|
// `_unBind` 方法用于解除迭代器与相关对象的绑定关系以及进行一些清理操作。
|
|
|
// 首先将 `__list__` 属性设置为 `null`,表示不再关联列表数据,然后删除 `__redo__` 属性(如果存在)。
|
|
|
// 如果存在 `__context__` 属性(即有绑定的上下文对象),则在上下文对象上移除对 `_add`、`_delete`、`_clear` 事件的监听(通过 `off` 方法,与之前的 `on` 方法对应),
|
|
|
// 最后将 `__context__` 属性也设置为 `null`,彻底解除与上下文的关联,完成清理和解除绑定的过程。
|
|
|
_unBind: d(function () {
|
|
|
this.__list__ = null;
|
|
|
delete this.__redo__;
|
|
|
if (!this.__context__) return;
|
|
|
this.__context__.off("_add", this._onAdd);
|
|
|
this.__context__.off("_delete", this._onDelete);
|
|
|
this.__context__.off("_clear", this._onClear);
|
|
|
this.__context__ = null;
|
|
|
}),
|
|
|
// `toString` 方法用于返回迭代器对象的字符串表示形式,遵循 `[object 类型名称]` 的格式,
|
|
|
// 如果对象定义了 `Symbol.toStringTag` 属性,则使用该属性的值作为类型名称,否则使用 `Object` 作为默认类型名称。
|
|
|
toString: d(function () {
|
|
|
return "[object " + (this[Symbol.toStringTag] || "Object") + "]";
|
|
|
})
|
|
|
},
|
|
|
// 使用 `autoBind` 函数(来自 `d/auto-bind` 模块)对 `_onAdd`、`_onDelete`、`_onClear` 这几个方法进行自动绑定操作,
|
|
|
// 使得这些方法内部的 `this` 指针始终指向当前迭代器实例,方便在事件触发等场景下正确访问和操作迭代器的属性和方法。
|
|
|
autoBind({
|
|
|
// `_onAdd` 方法用于处理列表中元素添加的情况,它接收添加元素的索引 `index` 作为参数,逻辑如下:
|
|
|
// 如果添加元素的索引大于等于当前迭代器的 `__nextIndex__`(表示添加的元素在当前迭代位置之后,不会影响当前迭代过程),则直接返回,不做处理。
|
|
|
// 如果添加元素的索引小于 `__nextIndex__`,说明添加的元素影响了当前的迭代顺序,需要调整迭代器的状态,将 `__nextIndex__` 自增 `1`,表示下一个迭代元素的索引往后移一位。
|
|
|
// 如果不存在 `__redo__` 属性(可能之前没有需要重新处理的索引情况),则创建一个新的 `__redo__` 属性,它是一个数组,并将添加元素的索引 `index` 放入数组中,
|
|
|
// 这样后续迭代时可以根据这个数组来重新处理添加元素后的情况。
|
|
|
// 如果已经存在 `__redo__` 属性,则遍历 `__redo__` 数组,对于每个元素 `redo`,如果它大于等于添加元素的索引 `index`,则将其自增 `1`,
|
|
|
// 表示这些需要重新处理的索引位置也需要往后移一位,以适应新添加的元素,最后将添加元素的索引 `index` 也添加到 `__redo__` 数组末尾。
|
|
|
_onAdd: d(function (index) {
|
|
|
if (index >= this.__nextIndex__) return;
|
|
|
++this.__nextIndex__;
|
|
|
if (!this.__redo__) {
|
|
|
defineProperty(this, "__redo__", d("c", [index]));
|
|
|
return;
|
|
|
}
|
|
|
this.__redo__.forEach(function (redo, i) {
|
|
|
if (redo >= index) this.__redo__[i] = ++redo;
|
|
|
}, this);
|
|
|
this.__redo__.push(index);
|
|
|
}),
|
|
|
// `_onDelete` 方法用于处理列表中元素删除的情况,接收要删除元素的索引 `index` 作为参数,逻辑如下:
|
|
|
// 如果要删除元素的索引大于等于当前迭代器的 `__nextIndex__`(表示删除的元素在当前迭代位置之后,不影响当前迭代过程),则直接返回,不做处理。
|
|
|
// 如果要删除元素的索引小于 `__nextIndex__`,说明删除的元素影响了当前的迭代顺序,需要调整迭代器的状态,将 `__nextIndex__` 自减 `1`,表示下一个迭代元素的索引往前移一位。
|
|
|
// 如果不存在 `__redo__` 属性(即之前没有需要重新处理的索引情况),则直接返回,因为没有需要调整的额外索引记录。
|
|
|
// 如果存在 `__redo__` 属性,则先查找要删除元素的索引 `index` 在 `__redo__` 数组中的位置 `i`,如果找到(`i` 不为 `-1`),则从数组中删除该元素(通过 `splice` 方法)。
|
|
|
// 接着遍历 `__redo__` 数组,对于每个元素 `redo`,如果它大于要删除元素的索引 `index`,则将其自减 `1`,
|
|
|
// 表示这些需要重新处理的索引位置需要往前移一位,以适应元素被删除后的情况。
|
|
|
_onDelete: d(function (index) {
|
|
|
var i;
|
|
|
if (index >= this.__nextIndex__) return;
|
|
|
--this.__nextIndex__;
|
|
|
if (!this.__redo__) return;
|
|
|
i = this.__redo__.indexOf(index);
|
|
|
if (i!== -1) this.__redo__.splice(i, 1);
|
|
|
this.__redo__.forEach(function (redo, j) {
|
|
|
if (redo > index) this.__redo__[j] = --redo;
|
|
|
}, this);
|
|
|
}),
|
|
|
// `_onClear` 方法用于处理列表被清空的情况,逻辑如下:
|
|
|
// 如果存在 `__redo__` 属性(表示之前有需要重新处理的索引情况),则调用 `clear` 函数(来自 `es5-ext/array/#/clear` 模块,用于清空数组)清空 `__redo__` 数组。
|
|
|
// 最后将 `__nextIndex__` 属性重置为 `0`,表示迭代器回到初始状态,等待新的列表数据进行迭代。
|
|
|
_onClear: d(function () {
|
|
|
if (this.__redo__) clear.call(this.__redo__);
|
|
|
this.__nextIndex__ = 0;
|
|
|
})
|
|
|
})
|
|
|
)
|
|
|
);
|
|
|
|
|
|
// 使用 `Object.defineProperty` 方法在 `Iterator` 函数的原型对象上定义一个 `Symbol.iterator` 属性,
|
|
|
// 其值是一个函数,该函数直接返回当前迭代器对象本身,这符合迭代器协议的要求,使得该迭代器对象可以被用于 `for...of` 循环等支持迭代器的语法结构中。
|
|
|
defineProperty(
|
|
|
Iterator.prototype,
|
|
|
Symbol.iterator,
|
|
|
d(function () {
|
|
|
return this;
|
|
|
})
|
|
|
);
|