一、基础介绍
在不暴露对象内部结构的同时,可以顺序地访问聚合对象内部的元素。
二、分类介绍
2.1 数组迭代器
var eachArray = function (arr, fn) {
if (Object.prototype.toString.call(arr) !== '[object Array]') throw new Error("不是数组");
var i = 0,
len = arr.length;
for(; i < len; i++) {
if (fn.call(arr[i], i, arr[i]) === false) {
break;
}
}
}
2.2 对象迭代器
var eachObject = function(obj, fn) {
if (Object.prototype.toString.call(arr) !== '[object Object]') throw new Error("不是对象");
for(var i in obj) {
if (fn.call(obj[i], i, obj[i]) === false) {
break;
}
}
}
2.3 内部迭代器
迭代器内部已经定义好了迭代规则。
举例: 上面提到的数组和对象迭代器就是内部迭代器。
2.4 外部迭代器
外部迭代器必须显示地请求迭代下一个元素。
举例:Generator 生成的迭代器
var createIterator = function (items) {
var current = 0;
return {
next() {
var done = current >= items.length;
var value = !done ? items[current] : undefined;
current++;
return {
done,
value
}
}
}
}
2.5 倒序迭代器
即改变遍历的顺序为倒序。
2.6 中止迭代器
迭代器内部提供跳出循环的方法。例如上方数组迭代器中的:
if (fn.call(arr[i], i, arr[i]) === false) {
break;
}
表示当回调函数的返回值为 false ,则提前终止循环。
2.7 同步变量迭代器
通俗的讲就是采用迭代器自动处理,对于获取嵌套属性的判断。
比如下面的数据结构,直接获取 A.info.id
可能会报错。一般都是 A && A.info && A.info.id
,但是这样的判断导致代码冗余。
同步变量
var A = {
str: 'lee',
arr: [],
obj: {
id: 123
}
}
getter
// root 表示外层变量
// key 表示内部链式调用
function getter(root, key) {
if (!root) return undefined;
var keys = key.split('.');
let result = root;
for( var i = 0, len = keys.length; i < len; i++) {
if (result[keys[i]] !== undefined) {
result = result[keys[i]];
} else {
return undefined;
}
}
return result;
}
setter
function setter(root, key, val) {
if (!root) return false;
let result = root;
let keys = key.split('.');
for( var i = 0, len = keys.length; i < len - 1; i++) {
// 如果第 i 层属性对应的值不存在,则定义成对象
if (result[keys[i]] === undefined) {
result[keys[i]] = {};
}
// 如果第 i 层属性对应的值不是对象的一个实例,则抛出错误
if (!(result[keys[i]] instanceof Object)) {
throw new Error('root.' + keys.splice(0, i + 1).join(',') + 'is not object');
return false;
}
result = result[keys[i]];
}
// 返回设置成功的属性值,注意此时 i 代表的是 len - 1
return result[keys[i]] = val;
}
-
给已经存在的 obj 属性添加新的属性 name
-
给不存在的属性 serve 添加属性 name,这里 serve 被初始化成对象了
-
给非对象的属性 str 添加属性 name,很明显 str 是基本类型,不能继续添加属性
2.8 排他方式处理
比如有 5 个元素,除了第 3 个元素之外,都要执行赋值为 0 的操作。
// 处理某个元素
dealItem() {
// do things
}
// 排除
except(arr, n) {
for (let i = 0; i < arr.length; i++) {
if (i !== n) {
dealItem();
}
}
}
这种方式比手工调用多次 dealItem() 好很多。
2.9 应用迭代器消除条件语句分支
结合策略模式
如果条件已知,不同条件对应的处理方式之间没有关系,互相独立,适合采用策略模式。-
结合中止迭代器
如果条件语句需要动态计算,存在优先级,或者存在关系,适合采用中止迭代器。举例:浏览器嗅探
参考
《JavaScript 设计模式与开发实践》曾探
《JavaScript 设计模式》张容铭
Javascript设计模式系统讲解与应用