JavaScript延迟执行

JavaScript延迟执行,javascript,linq,Javascript,Linq,对于个人挑战,我正在JavaScript中实现LINQ(一组具有类似LINQ功能的函数)。但是,到目前为止,这些函数正在立即处理数据;对于某些函数(如Sum或Aggregate),这是正确的行为,但对于其他函数(如Select或While)则是错误的 我很好奇JavaScript中是否有一个构造可以让我获得与.Net中相同的行为,即在枚举集合或使用立即执行的函数之前,不会发生真正的处理 注意:我相信这个任务(在JS中实现LINQ)已经完成了。这不是重点。这对我自己来说是一个挑战,这可能会帮助我提

对于个人挑战,我正在JavaScript中实现LINQ(一组具有类似LINQ功能的函数)。但是,到目前为止,这些函数正在立即处理数据;对于某些函数(如Sum或Aggregate),这是正确的行为,但对于其他函数(如Select或While)则是错误的

我很好奇JavaScript中是否有一个构造可以让我获得与.Net中相同的行为,即在枚举集合或使用立即执行的函数之前,不会发生真正的处理

注意:我相信这个任务(在JS中实现LINQ)已经完成了。这不是重点。这对我自己来说是一个挑战,这可能会帮助我提高对LINQ(以及,巧合的是,JS)的理解。除了个人的熏陶,我将很快在工作中使用LINQ,根据个人项目的需要,我可能会在工作中使用JS,而我在工作之外的一些事情上使用JS

编辑:似乎我吸引了不熟悉LINQ的人,所以我想我应该在这方面给出一些解释。LINQ是语言集成查询,来自.Net。LINQ允许在许多数据源(包括实际的SQL关系数据库)上进行类似SQL的查询,如LINQtoObjects,这正是我试图实现的

LINQ的一个特性是在许多方法上延迟执行。如果我有一个集合
customers
并调用
var query=customers.Where(c=>c.Age>40)
(或者它在JS中的最终结果,
var query=customers.Where(函数(c){return c.Age>40;});
),返回值是一种接口类型,并且集合的实际处理(返回仅包含40岁以上客户的集合子集)尚未发生。当我使用其中一个没有延迟执行的方法(例如,
query.First()
query.ToArray()
)时,所有延迟处理都会发生。这可能是一个链,例如
customers.Where(…).Skip(5)。选择(…).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”。
作为补充,我目前正在通过在对象和数组上原型化我的方法来实现这个项目,迭代
this
的元素,并跳过任何属于函数的元素。像创建一个可枚举类这样的东西可能更优越(事实上,如果需要返回函数或匿名对象之类的东西,那么延迟执行计划可能需要它),但这就是我目前得到的。我的功能通常表现为以下几点:

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;
};

我不完全清楚你到底想做什么,但我认为你应该研究的是方法。然后,您可能希望重新定义属性,并仅在读取代码后执行代码。或者,如果只想在读取属性本身后执行此操作,请在该点执行此操作。不知道LINQ是如何工作的,甚至不知道它是什么,所以我有点含糊不清。不管是哪种方式,你都可以做

Object.defineProperty(o, "a", { get : function(){return 1;});

允许您仅在访问属性后执行操作(您还可以执行更多操作)

基本上,您需要做的是从函数返回对象,而不是执行操作。返回的对象将包含将来执行操作所需的代码。考虑一个用例:

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);
}
为此,我们定义了.Where()、.Skip()和.Select()方法,以返回具有.Next()方法重写版本的类的实例。支持此功能的工作代码:(将trace设置为true以观察执行顺序是否延迟)

var trace=false;
函数扩展(目标,src){
for(src中的var k){
目标[k]=src[k];
}
回报目标;
}
功能Iter(wrapThis){
if(wrapThis.Next){
回卷纸;
}否则{
返回新的ArrayIter(wrapThis);
}
}
Iter.prototype={
建造商:国际热核实验堆,
其中:函数(fn){返回新的WhereIter(this,fn);},
Skip:function(count){返回新SkipIter(this,count);},
选择:函数(fn){返回新的选择器(this,fn);}
};
函数ArrayIter(arr){
this.arr=arr.slice();
这个值为0.idx=0;
}
ArrayIter.prototype=extend(Object.create(Iter.prototype)),
{
建造商:ArrayIter,
下一步:函数(){
如果(this.idx>=this.arr.length){
返回null;
}否则{
返回this.arr[this.idx++];
}
}
});
ITER(src,过滤器)的功能{
this.src=src;this.filter=filter;
}
其中Iter.prototype=extend(Object.create(Iter.prototype)){
建造商:WhereIter,
下一步:函数(){
var v;
while(true){
v=this.src.Next();
trace&&console.log('其中处理:'+v);
如果(v==null | | this.filter.call(this,v)){break;}
}
返回v;
}
});
函数skipitor(src,count){
this.src=src;this.count=count;
此参数=0;
}
SkipIter.prototype=extend(Object.create(Iter.prototype)){
建造师:斯基皮特,
下一步:函数(){
var v;
而(this.count>this.skipped++){
v=this.src.Next();
trace&&console.log('跳过处理:'+v);
如果(v==null){返回v;}
}
返回这个.src.Next();
}
});
功能选择器(src,fn){
this.src=src;this.fn=fn;
}
SelectIter.prototype=extend(Object.create(Iter.prototype)){
构造函数:SelectIter,
下一步:函数(){
var v=this.src.Next();
trace&&console.log('Select processing:'+v);
如果(v==null){返回null;}
返回此.fn.call(this,v);
}
});
var myCollection=[];
对于(变量i=0;i<100;i++){
推(i);
}
var query=Iter(myCollection)。
20
24
28
...
188
192
196
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);

}