在.net平台工作一年有余,最喜歡的應屬Linq特性
在幾個移動端web小項目過程中,前端需要對json對象集合進行比較復雜的操作,為提高開發效率,引入了LINQ to Javascript,該項目地址:http://jslinq.codeplex.com/
LINQ to JavaScript代碼不到兩百行,可讀性很好,今天來對它的源代碼進行下分析
Linq to JavaScript使用示例
var myList = [ {FirstName:"Chris",LastName:"Pearson"}, {FirstName:"Kate",LastName:"Johnson"}, {FirstName:"Josh",LastName:"Sutherland"}, {FirstName:"John",LastName:"Ronald"}, {FirstName:"Steve",LastName:"Pinkerton"} ]; var exampleArray = JSLINQ(myList) .Where(function(item){ return item.FirstName == "Chris"; }) .OrderBy(function(item) { return item.FirstName; }) .Select(function(item){ return item.FirstName; });
Linq to JavaScript整體架構
(function() { JSLINQ = window.JSLINQ = function(dataItems) { return new JSLINQ.fn.init(dataItems); }; JSLINQ.fn = JSLINQ.prototype = { init: function(dataItems) { this.items = dataItems; }, Where: function(clause) { ... }, ...
} JSLINQ.fn.init.prototype = JSLINQ.fn; })();
這個結構的關鍵點有
- 最外層用一個匿名函數(function(){}))()構造塊級作用域
- 不采用new JSLINQ().Where()方式,而是將JSLINQ()作為一個工廠方法,返回init()函數的實例對象
- 為了在init()函數的實例對象上繼續調用JSLINQ原型對象的方法,將JSLINQ.fn.init.prototype指向JSLINQ.fn,也即JSLINQ.prototype
- 通過這一操作,調用new JSLINQ().Where()時,首先可以在init()函數的實例對象中查詢,未找到后在init.prototype中查詢,未找到,繼續沿原型鏈向上查找,在JSLINQ.prototype上找到Where方法
- 如果沒有這一操作,調用new JSLINQ().Where()時,將會提示Where方法未被定義
觀察Linq to JavaScript整體結構會發現,其結構與JQuery的非常相似
(function( window, undefined ) { var jQuery = (function() { // 構建jQuery對象 var jQuery = function( selector, context ) { return new jQuery.fn.init( selector, context, rootjQuery ); } // jQuery對象原型 jQuery.fn = jQuery.prototype = { constructor: jQuery, init: function( selector, context, rootjQuery ) { } }; // Give the init function the jQuery prototype for later instantiation jQuery.fn.init.prototype = jQuery.fn; // 到這里,jQuery對象構造完成,后邊的代碼都是對jQuery或jQuery對象的擴展 return jQuery; })(); window.jQuery = window.$ = jQuery; })(window);
LINQ to JavaScript詳細分析
//構造塊級作用域 (function() { //工廠方法,返回init()的實例對象 JSLINQ = window.JSLINQ = function(dataItems) { return new JSLINQ.fn.init(dataItems); }; JSLINQ.fn = JSLINQ.prototype = { init: function(dataItems) { this.items = dataItems; } //clause是一個函數表達式 Where: function(clause) { var item; //創建本地數組,存儲返回值 var newArray = new Array(); // The clause was passed in as a Method that return a Boolean for (var index = 0; index < this.items.length; index++) { //將元素傳入函數 if (clause(this.items[index], index)) { newArray[newArray.length] = this.items[index]; } } //將結果數組傳入JSLINQ(),創建新的init()對象實例,以便可以鏈式調用 return new JSLINQ(newArray); }, Select: function(clause) { var item; var newArray = new Array(); // The clause was passed in as a Method that returns a Value for (var i = 0; i < this.items.length; i++) { if (clause(this.items[i])) { newArray[newArray.length] = clause(this.items[i]); } } return new JSLINQ(newArray); } ... JSLINQ.fn.init.prototype = JSLINQ.fn; } })();
更多
從LINQ to JavaScript使用示例中可以看到,Where(),Select()等方法中僅支持匿名函數的方式
而在.net3.0之后,LINQ的Where等擴展方法中,已經可以使用便捷的lambda表達式
如果你希望在javascript中也使用lambda表達式語法書寫linq,可以關注LINQ for JavaScript,這是另一個提供linq支持的javascript庫,項目地址http://linqjs.codeplex.com/
LINQ for JavaScript調用示例
var jsonArray = [ { "user": { "id": 100, "screen_name": "d_linq" }, "text": "to objects" }, { "user": { "id": 130, "screen_name": "c_bill" }, "text": "g" }, { "user": { "id": 155, "screen_name": "b_mskk" }, "text": "kabushiki kaisha" }, { "user": { "id": 301, "screen_name": "a_xbox" }, "text": "halo reach" } ] // ["b_mskk:kabushiki kaisha", "c_bill:g", "d_linq:to objects"] var queryResult = Enumerable.From(jsonArray) .Where(function (x) { return x.user.id < 200 }) .OrderBy(function (x) { return x.user.screen_name }) .Select(function (x) { return x.user.screen_name + ':' + x.text }) .ToArray(); // shortcut! string lambda selector var queryResult2 = Enumerable.From(jsonArray) .Where("$.user.id < 200") .OrderBy("$.user.screen_name") .Select("$.user.screen_name + ':' + $.text") .ToArray();