JavaScript 设计模式 --- 迭代器模式

前言

迭代器模式应该算是比较 “低调” 的设计模式了,因为这种模式在日常工作中经常遇到,但由于本身这种模式的思想十分简单,所以一般不会特别的去关注他。
迭代器模式注重的是如何去迭代集合中每一个元素,大部分语言都内置了实现迭代器接口的数据结构,并且暴露出对应的方法供我们使用。

JavaScript中的迭代器模式体现
  1. 常用的数组就是一个可以迭代的对象,它提供了一个forEach方法可以让我们快速使用一个回调来处理数组中的每一个元素,但是不用关心如何具体迭代的细节。这里我们简单实现一个类似的each。
const each = (target, callback) => {
    for (let i = 0, l = target.length; i < l;i++) {
        callback.call(this,target[i],i);
    }
}
var a = ["foo","bar"];
each(a,(item, index) => {
    console.log(item, index);
})
// foo 0
// bar 1

调用方并不关心迭代器内部的具体实现,我们只要关注自己回调函数的实现即可。

值得一提的是,上述迭代器不仅仅对于数组有效,对于这样的对象也可以使用。

var obj = {
    0: 'foo',
    1: 'bar',
    length:2
};
each(obj,(item,index) => {
    console.log(item, index)
})
// foo 0
// bar 1

这个可被迭代的对象需要满足两个特性。

  1. 拥有length属性
  2. 可以通过下标访问。
实现一个外部迭代器

上面的例子中,迭代器的迭代逻辑是封装在内部的,他做了这样的事情:遍历传入的对象,将传入的回调作用在每一个对象中的元素,但是有时候我们想加入自己的一些业务逻辑,此时我们要实现一个简单的外部迭代器。
假如我们有这样的一个需求:
遍历某个对象数组,假如有某个对象的danger属性为true,则停止遍历。看一下这个Demo.

class Iterator{
    constructor(iterator){
        if (!(this instanceof Iterator)) throw new Error('must be used with new operator')
        if (iterator.length === undefined) throw new Error('the iterator must have "length" propoty')
        this.iterator = iterator;
        this.current = 0;
    }

    next(){
        this.current++;
    }

    getCurrent(){
        return this.iterator[this.current];
    }

    isDone(){
        return this.current >= this.iterator.length;
    }

}

function iteratorFactory(iterator){
    return new Iterator(iterator);
}

我定义了一个Iterator类,并且申明了一个工厂方法。下面看看怎么使用它。

var target = [
    {name:'foo', danger:false},
    {name:'bar', danger:true},
    {name:'tee', danger:false},
    {name:'nnk', danger:false},
];

const it= iteratorFactory(target);
while(!it.isDone()) {
    const ret = it.getCurrent();
    if (ret.danger) return;
    console.log(ret)
    it.next();
}
// { name: 'foo', danger: false }

我们在外部使用while循环,通过isDone控制结束条件,如果当前遍历的元素的danger属性为true则直接停止循环。否则输出该元素。
这样一来迭代器的逻辑就由我们自己来控制了。注意这里我们同样可以传入一个可被迭代的对象

应用场景

接下举例一下我工作中用到迭代器模式的真实场景

在跨端开发中,有时候会遇到依赖运行环境执行的代码,这里可能是H5(泛指PC端的页面和M站的页面),android,ios。原先是这么实现的

try {
	if (weex.util.isWeb()) {
		doWebCallback()
	} else if (weex.util.isAndroid) {
		doAndroidCallback()
	} else if (weex.util.isIos) {
		doIosCallback()
	}
} catch(e) {
	doDefaultCallback();
}

以上代码逻辑有很多if else 分支,并且放在了try catch 中,功能上没有什么问题,但是耦合严重,假如现在我要针对PC和Mobile做区分处理,不得不回到这里去修改if else 分支。又或者兜底逻辑变了,我又要去catch里面修改对应逻辑。

这里我们借用上面的外部迭代器来优化下这个操作。

webFn = () => {
    if (!weex.util.isWeb()) return false;
    return doWebCallback;
}

androidFn = () => {
    if (!weex.util.isAndroid()) return false;
    return doAndroidCallback;
}

iosFn = () => {
    if (!weex.util.isIos()) return false;
    return doIosCallback;
}

defaultFn = () => {
    return doDefaultCallback;
}

const Fns = [webFn,androidFn,iosFn]
Fns.push(defaultFn);
const it= iteratorFactory(Fns);
while(!it.isDone()) {
    const fn = it.getCurrent();
    if (fn && typeof fn === "function") {
        return fn();
    }
    it.next();
}

以后如果再加其他的分支逻辑,只要写一个按照以上标准的新方法,然后加入到Fns数组中即可。维护也方便。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值