代码改变世界

码农干货系列【5】--lambda in js:lambda.js 1.0.0发布

2012-12-27 21:55  【当耐特】  阅读(6565)  评论(3编辑  收藏  举报

lambda.js的由来

作为一个当耐特程序员,对lambda一定不陌生。随着当耐特版本的更新迭代,C#也由委托==〉匿名委托==〉lambda表达式。由于javascript语言的约束,没有提供相应的lambda的机制,所以就有了lambda.js,让广大jser也可以 (a,b)=>a.xx==”yyy”&&b>11 一把!

Query Operator

而lambda最常用的地方就是Query Operator。

比如下面一些C# code:

int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
int oddNumbers = numbers.Count(n => n % 2 == 1);

对应的javascript code(不使用lambda.js)

var numbers = [ 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 ];
var oddNumbers = _(numbers).count(function (item) { return item % 2 === 1 });

使用lambda.js之后

var numbers = [5, 4, 1, 3, 9, 8, 6, 7, 2, 0];
var oddNumbers = _(numbers).count(_("a=>a%2===1"));

仅此而已?

lambda.js当然提供了underscore.js和JSLINQ都包含的功能!当然lambda.js编程风格更接近jquery style(连缀,大部分方法返回lambda对象)。

        
        var numbers = [5, 4, 1, 3, 9, 8, 6, 7, 2, 0];
        var oddNumbers = _(numbers).count(_("a=>a%2===1"));

        console.log(oddNumbers===5)
        //数组
        _(["aa", "bb", "cc"]).each(function (index, item) {
            if (index === 0) console.log("arrry each:"+("aa" === item));
            if (index === 1) console.log("arrry each:" +( "bb" === item));
            if (index === 2) console.log("arrry each:" +( "cc" === item));
        })

        //支持对象
        _({ x: 100, y: 200, name: "zhanglei" }).each(function (key, value) {

            if (key == "x") console.log("obj each:" +( value === 100));
            if (key == "y") console.log("obj each:"+(value === 200));
            if (key == "name") console.log("obj each:"+(value === "zhanglei"));
         
        })

        ////支持map
        var aa = _([1, 2, 3]).map(function (item) {
            return item * 3;
        }).items

        console.log("map:"+(aa[0] === 3))
        console.log("map:"+(aa[1] === 6))
        console.log("map:"+(aa[2] === 9))


        //支持对象的数组
        var stooges1 = [{ name: 'zhanglei', age: 17 }, { name: 'curly', age: 25 }, { name: 'moe', age: 21 }, { name: 'larry', age: 23 }];

        var youngest = _(stooges1).map(function (item) { return item.name + ' is ' + item.age; }).items
        console.log(youngest)



        //支持查找
        var even = _([7, 2, 3, 4, 5, 6, 7, 8, 9]).find(function (item) { return item % 2 == 0; }).items;
        console.log(even) 

这些都不是关键,关键是可以lambda化,比如这个查询:

    var stooges3 = [{ name: 'zhanglei', age: 17 }, { name: 'curly', age: 25 }, { name: 'moe', age: 21 }, { name: 'larry', age: 23 }];
        var over18 = _(stooges3)
            .find(function (item, index) { return item.age >= 18 && index>0})
            .sortBy(function (item) { return item.age })
            .first(function (item) { return item.age > 21 })
            .map(function (stooge) { return stooge.name + ' is ' + stooge.age; })
            .items
        ;
      
        console.log(over18);

lambda化之后:

        var stooges3 = [{ name: 'zhanglei', age: 17 }, { name: 'curly', age: 25 }, { name: 'moe', age: 21 }, { name: 'larry', age: 23 }];

        var over18 = _(stooges3)
           .find(_("(a,b)=> a.age >= 18 && b > 0 "))
           .sortBy(_("b=>b.age"))
           .first(_("item=>item.age>21"))
           .map(_("i=>i.name + ' is ' + i.age;"))
           .items
        ;



        console.log(over18);

lambda.js预览


(function (window) {

    var  lambda = function (items) {
        if (lambda.type(items) === "string") return lambda._compile(items);
        return new lambda.prototype.init(items);
    },

   
    _lambda = window.lambda,

 
    __ = window._;

    lambda.prototype.init = function (items) {

        this.items = items;
        return this;
    }


    lambda.prototype.each = function (fn) {

        var name, i = 0, length = this.items.length, isObj = length === undefined || lambda.type(this.items) === "function";
        var its = this.items;
        if (isObj) {
            for (name in its) {
                fn.call(its[name], name, its[name]);
            }
        } else {
            for (; i < its.length;) {
                fn.call(its[i], i, its[i++]);
            }
        }
    }
    lambda.prototype.count= function(fn) {
        if (fn == null)
            return this.items.length;
        else
            return this.find(fn).items.length;
    }
    lambda.prototype.map = function (fn) {

        var result = [];
        this.each(function (index,item) {

            result[index]=fn(item);
        })
        return lambda(result);
    }

    lambda.prototype.first = function (fn) {

        if (fn != null) {
            return this.find(fn).first();
        }
        else {
            // If no clause was specified, then return the First element in the Array
            if (this.items.length > 0)
                return lambda([this.items[0]]);
            else
                return null;
        }
    }

    lambda.prototype.find = function (fn) {
        
        var newArr=[], self = this, i = 0;
       
        this.each(function (index, item) {

            if (fn(item,index)) newArr[i++] = item;
          
        })

        return lambda(newArr);
        
    }

    lambda.prototype.sortBy = function (clause) {
            var tempArray = [];
            for (var i = 0; i < this.items.length; i++) {
                tempArray[tempArray.length] = this.items[i];
            }
            return  lambda(
            tempArray.sort(function (a, b) {
                var x = clause(a);
                var y = clause(b);
                return ((x < y) ? -1 : ((x > y) ? 1 : 0));
            })
        );
    }

    lambda.type = function (obj) {
        return obj == null ?
         String(obj) :
         {
             "[object Array]": "array",
             "[object Boolean]": "boolean",
             "[object Date]": "date",
             "[object Function]": "function",
             "[object Number]": "number",
             "[object Object]": "object",
             "[object RegExp]": "regexp",
             "[object String]": "string"
         }[Object.prototype.toString.call(obj)] || "object";
    }

    lambda._compile = function (condition) {
        var conditionStr = condition.split("=>");
      
        if (conditionStr[0].indexOf("(") === -1) {
            return function (item) {               
                return eval(conditionStr[1].replace(new RegExp("\\b" + conditionStr[0] + "(?![A-Za-z0-9_])", "g"), "item"));
            }
        } else {
            var tempStr = conditionStr[0].replace(/\(/g, "").replace(/\)/g, "").split(",");
            var tempItem = lambda.trim(tempStr[0]);
            var tempIndex = lambda.trim(tempStr[1]);
            return function (item,index) {
                return eval(conditionStr[1].replace(new RegExp("\\b" + tempItem + "(?![A-Za-z0-9_])", "g"), "item").replace(new RegExp("\\b" + tempIndex + "(?![A-Za-z0-9_])", "g"), "index"));
            }
        }
    }



    var trimLeft = /^\s+/,
    trimRight = /\s+$/,
    rnotwhite = /\S/,
    trim = String.prototype.trim;
    
    // IE doesn't match non-breaking spaces with \s
    if (rnotwhite.test("\xA0")) {
        trimLeft = /^[\s\xA0]+/;
        trimRight = /[\s\xA0]+$/;
    }

    lambda.trim = trim ?
        function (text) {
            return text == null ?
                "" :
                trim.call(text);
        } :

    // Otherwise use our own trimming functionality
        function (text) {
            return text == null ?
                "" :
                text.toString().replace(trimLeft, "").replace(trimRight, "");
        };

    lambda.prototype.init.prototype = lambda.prototype;


    
    
    lambda.noConflict = function (deep) {
        if (window._ === lambda) {
            window._ = __;
        }

        if (deep && window.lambda === lambda) {
            window.lambda = _lambda;
        }

        return lambda;
    }


    var root = this;

    if (typeof exports !== 'undefined') {
        if (typeof module !== 'undefined' && module.exports) {
            exports = module.exports = lambda = _;
        }
        exports.lambda = exports._ = lambda;
    } else {
        root.lambda = root._ = lambda;
    }


 
}(window))

完整demo下载

欢迎任何问题或者建议。