为了个人挑战,我正在用 JavaScript 实现 LINQ(好吧,一组具有类似 LINQ 功能的函数)。但是,截至目前,这些功能正在立即处理数据;这对于某些函数(例如 Sum 或 Aggregate)是正确的行为,但对于其他函数(例如 Select 或 While)是不正确的。
我很好奇 JavaScript 中是否有一个构造可以让我获得与 .Net 中相同的行为,在 .Net 中,在枚举集合或使用立即执行的函数之前不会发生真正的处理。
注意:我相信这个任务(在 JS 中实现 LINQ)已经完成了。那不是重点。这是我自己对自己的挑战,这很可能会帮助我增加对 LINQ(以及巧合的 JS)的理解。除了个人熏陶之外,我很快就会在工作中使用 LINQ,可能会根据个别项目的需要在工作中使用 JS,并且我会在工作之外使用 JS。
编辑: 看来我吸引了不熟悉 LINQ 的人,所以我想我应该在这方面给出一些解释。 LINQ 是 Language-INtegrated Query,来自 .Net。 LINQ 允许对许多数据源(包括实际的 SQL 关系数据库)进行类似 SQL 的查询,例如 LINQ to Objects,这正是我正在努力实现的目标。
LINQ 的一个特性是延迟执行许多方法。如果我有一个集合 customers
并调用 var query = customers.Where(c => c.Age > 40);
(或者它最终会在 JS 中, var query = customers.Where(function (c) { return c.Age > 40; });
),返回值为接口(interface)类型,实际处理集合(返回子集)仅包含 40 岁以上客户的集合)尚未发生。当我使用其中一种没有延迟执行的方法(例如,query.First()
或 query.ToArray()
)时,然后所有延迟处理发生。这可以是一个链,例如 customers.Where(...).Skip(5).Select(...).OrderBy(...)
(每个“...”一个函数)。
结果是这样的代码:
var collection = [1, 2, 3, 4, 5];
var query = collection.Where(function (n) { return n % 2 == 0; });
collection.push(6);
alert(query.Max());
结果为“6”。
作为附录,我目前正在通过将我的方法原型(prototype)化到 Object 和 Array 上来实现这个项目,迭代
this
的元素,并跳过任何函数元素。像制作 Enumerable 类这样的东西可能更好(事实上,如果需要返回函数或匿名对象之类的东西,我的延迟执行计划可能需要),但这就是我目前所拥有的。我的函数通常表现为以下几行:
Object.prototype.Distinct = Array.prototype.Distinct = function (comparer) {
comparer = comparer || function (a, b) { return a == b; };
var result = [];
for (var idx in this) {
var item = this[idx];
if (typeof item == "function") continue;
if (!result.Contains(item, comparer)) result.push(item);
}
return result;
};
最佳答案
从根本上说,您需要做的是从您的函数中返回对象,而不是执行操作。您返回的对象将包含将来执行操作所需的代码。考虑一个示例用例:
var myCollection = [];
for(var i = 0; i < 100; i++) { myCollection.push(i); }
var query = Iter(myCollection).Where(function(v) { return v % 2 === 0; })
.Skip(5).Select(function(v) { return v*2; });
var v;
while(v = query.Next()) {
console.log(v);
}
我们期望输出:
20
24
28
...
188
192
196
为了做到这一点,我们定义了方法 .Where()、.Skip() 和 .Select() 以返回具有 .Next() 方法的覆盖版本的类的实例。支持此功能的工作代码:(将 trace 设置为 true 以观察执行顺序是惰性的)
var trace = false;
function extend(target, src) {
for(var k in src) {
target[k] = src[k];
}
return target;
}
function Iter(wrapThis) {
if(wrapThis.Next) {
return wrapThis;
} else {
return new ArrayIter(wrapThis);
}
}
Iter.prototype = {
constructor: Iter,
Where: function(fn) { return new WhereIter(this, fn); },
Skip: function(count) { return new SkipIter(this, count); },
Select: function(fn) { return new SelectIter(this, fn); }
};
function ArrayIter(arr) {
this.arr = arr.slice();
this.idx = 0;
}
ArrayIter.prototype = extend(Object.create(Iter.prototype),
{
constructor: ArrayIter,
Next: function() {
if(this.idx >= this.arr.length) {
return null;
} else {
return this.arr[this.idx++];
}
}
});
function WhereIter(src, filter) {
this.src = src; this.filter = filter;
}
WhereIter.prototype = extend(Object.create(Iter.prototype), {
constructor: WhereIter,
Next: function() {
var v;
while(true) {
v = this.src.Next();
trace && console.log('Where processing: ' + v);
if(v === null || this.filter.call(this, v)) { break; }
}
return v;
}
});
function SkipIter(src, count) {
this.src = src; this.count = count;
this.skipped = 0;
}
SkipIter.prototype = extend(Object.create(Iter.prototype), {
constructor: SkipIter,
Next: function() {
var v;
while(this.count > this.skipped++) {
v = this.src.Next();
trace && console.log('Skip processing: ' + v);
if(v === null) { return v; }
}
return this.src.Next();
}
});
function SelectIter(src, fn) {
this.src = src; this.fn = fn;
}
SelectIter.prototype = extend(Object.create(Iter.prototype), {
constructor: SelectIter,
Next: function() {
var v = this.src.Next();
trace && console.log('Select processing: ' + v);
if(v === null) { return null; }
return this.fn.call(this, v);
}
});
var myCollection = [];
for(var i = 0; i < 100; i++) {
myCollection.push(i);
}
var query = Iter(myCollection).Where(function(v) { return v % 2 === 0; })
.Skip(5).Select(function(v) { return v*2; });
var v;
while(v = query.Next()) {
console.log(v);
}
您可能还想查看“字符串 lambdas”以使您的查询更具可读性。这将允许你说 "v*2"
而不是 function(v) { return v*2; }
关于JavaScript 延迟执行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19639262/