zoukankan      html  css  js  c++  java
  • 在JavaScript中实现yield,实用简洁实现方式。

    原题还是老赵的:

    http://blog.zhaojie.me/2010/06/code-for-fun-iterator-generator-yield-in-javascript.html

    原以为是一个蛮复杂的题目,想了许久没思路,当然要实现绝对能实现,但如果分析JavaScript脚本或是动态产生代码,都太复杂了。

    刚才忽然灵机一动,迭代器我们很少会真的直接傻乎乎的next去遍历的,那为什么一定要实现这个傻乎乎的next呢?直接实现each,这样,这样反过来,Yeah,一通百通,不一会儿就写出了第一个超简洁版本:

    代码
    function yieldHost(yieldFunction)
    {
    return function (processer)
    {
    var yield = function (result)
    {
    processer(result)
    };

    yieldFunction(yield);

    };
    }

    思路一换,代码真简洁。

    先附上例子,然后来谈原理。

    首先我们需要一个函数来进行枚举,像这样:

    function fun(yield)
    {

    for (var i = 0; i < 100; i++)
    yield(i);

    }

    或是这样:

    function fun(yield)
    {

    yield(
    1);
    yield(
    2);
    yield(
    3);

    }

    由于实现方式与C#的不同,所以在循环体内也不用什么yield break或是yield continue这样的语法,直接break或是continue就好了。

    然后是实际的运用,yieldHost函数可以将上面的符合要求的fun函数转换为一个枚举器:

    var enumerator = yieldHost(fun);

    这个枚举器其实也是一个函数,像jQuery的each函数一样,接收一个处理函数来处理枚举:

    enumerator(function (item)
    {
    window.alert(item);
    });

    接下来谈谈原理。

    对于传统的枚举器来说,我们会认为枚举器应该在每次调用返回一个值,这就是next方法,但就像陈子瀚说的,这需要在yield的时候把函数停住,虽然可以实现,但真的很麻烦。

    但!事实上我发现,大多数时候,我们都是用foreach这样的语法来访问枚举器的。这样一来就给了我一个非常讨巧的办法,不实现next方法,而是实现each方法。

    each方法和next的方法的区别在哪里呢?熟悉jQuery的朋友就会知道,each方法其实可以视为将next倒过来,不是返回枚举值,而是接收一个函数,把枚举值当作参数传进去。

    正是这一倒,所有问题都迎刃而解了。我们没有必要去暂停一个函数的执行,只需要将处理枚举值的逻辑注到这个函数里面去就完了。所以事实上这里的yieldHost就是完成了一个倒装的工作,把enumerator接收的那个函数(也就是window.alert( item ),注到了枚举函数中(即fun)。最终执行的效果就像是这样:

    function fun(yield)
    {

    window.alert(
    1);
    window.alert(
    2);
    window.alert(
    3);

    }

    所以就诞生了这个超简洁的实现。

    有了这个超简洁的实现,下一步就是实现像jQuery的each方法一样的return true代表break和return false代表continue的功能了,只有具备了这样的功能,才能处理无穷集,或是实现TakeWhile之类的功能。

    老实说我对JavaScript的研究并不透彻,只想到了一个使用异常打断的办法,这就是第二个版本的yieldHost:

    代码
    function yieldHost(yieldFunction)
    {

    var exception = Math.random();

    return function (processer)
    {
    try
    {
    yieldFunction(
    function (result)
    {
    if (processer(result))
    throw exception;
    });
    }
    catch (e)
    {
    if (e !== exception)
    throw e;
    }

    };
    }

    显然这并不完美,但我实在想不出更好的办法。

    接下来在这个基础上实现Select、Where什么,其实是非常简单的事情,给出一个我的Select的实现:

    代码
    function Select(enumerator, selector)
    {
    return function (fun)
    {
    enumerator(
    function (item)
    {
    return fun(selector(item));
    });
    }
    }

    至于,这个Select怎么修改成连写的版本,即:

    enumerator.Select( selector )( processor );

    我觉得这对JavaScript而言真不是一件很难的事情啊。。。。

    只是,过早的引入语法友好,会把JavaScript变得很复杂难看。所以,这个留给大家去玩吧。

    公司招聘,应老大要求在文章末尾附上顺风车广告一条,不喜勿入:http://job.cnblogs.com/offer/7490/

  • 相关阅读:
    推荐阅读20100603
    [ASP.NET4之旅]Circular file references are not allowed
    满园尽是503,记曾经的一次IIS 7性能考验
    VS2010小Bug:找不到System.Web.Extensions.dll引用
    VS2010奇异Bug:三个中文符号在CSS文件中轻松让VS2010崩溃
    推荐阅读20100528
    Windows平台网站文件同步备份解决方案——cwRsyn
    推荐阅读20100803
    在IE8中使用建行企业网银的解决方法
    博客园已经用上NorthScale Memcached Server
  • 原文地址:https://www.cnblogs.com/Ivony/p/1754390.html
Copyright © 2011-2022 走看看