C#'ref'关键字等效或Javascript中的解决方法

时间:2013-02-16 22:07:41

标签: javascript javascript-objects

如果我有一个对象:

var Edge = function() {
  this.a = 0;
  this.b = 0;
  this.prevEdge = null;
  this.nextEdge = null;
  // +20 other members
}

和一个功能:

function ProcessEdge(e)
{
  // some processing and then
  e = e.nextEdge;
  // some processing and then return something
}

以这种方式从其他函数调用:

var e = new Edge();
// some processing and then
var res = ProcessEdge(e);
// e should be now the same object as e.nextEdge
// but it is not, e is still e

e现在应该是e.nextEdge,但它仍然是e,因为e = e.nextEdge;函数中的ProcessEdge()会破坏参考。

第一个想法是通过使用类似ProcessEdge()之类的东西在e.a = e.nextEdge.a; e.b = e.nextEdge.b; e.prevEdge = e.nextEdge.prevEdge; e.nextEdge = e.nextEdge.nextEdge;函数中逐个分配每个成员来实现成员平等,但引用仍然被破坏。当我这样做的时候,我也必须按成员比较,意大利面条代码和它的缓慢来了。

在C#和类似语言中,只需在变量名称之前使用ref关键字并保留引用并且速度,简单性和速度与预期一致,即可实现引用保留。

好的,现在有效的方法(这是我到目前为止使用的方法):

function ProcessEdge(e)
{
  // some processing and then
  e.Value = e.Value.nextEdge; // instead of e = e.nextEdge;
  // some processing and then return something
}

// And the calling part:
var e = new Edge();
// some processing and then
var res = (function ()
{
  e = { Value: e };
  var $res = ProcessEdge(e);
  e = e.Value;
  return $res;
})
.call(this);

此方法工作正常,但问题是内联函数,每次执行行var res = (function() {}...时都会创建(AFAIK)。这个var res...是内部循环,如果有大量数据,则会被调用非常多次,在某些情况下会被调用100,000到500,000次/秒。

那么,是否有更快的方法来实现参考保留?


编辑:

根据下面的评论,我进行了速度测试: http://jsperf.com/closure-vs-compund http://jsperf.com/closure-vs-compund/2

结果是上面提到的闭包方法非常慢,正如我所料。我发现最快的方法是一直放下函数ProcessEdge(),而是内联它。闭合速度的速度是最快速度的26.63%。因此,内联方法比闭包方法快3.7倍。内联方法如下:

function main10(a)
{
  var res=0, res2, e, b;
  for (var i=0;i<10000;i++)
  {
    e = new Edge();
    b = new Edge();
    b.a = i*a;

    e.nextEdge = b;

    // ProcessEdge() as inline version:
    e = e.nextEdge;
    res += e.a-10;

    if (e!=b) res+=1000;
  }
  return res;
}

但通常我们需要使用函数(并为解释器提供自动内联的机会,如果有的话)。下一个最快的是使用&#34; global&#34;参考。速度是最快的98.45%。

global_val是返回值的持有者(因为Javascript中的函数不能返回多个值,所以使用它)。这意味着我们在外部的任何循环中只有一步,在范围内层叠引用对象global_val,这是在创建所有其他函数时创建的。 ProcessEdge()函数会覆盖global_val的值,因为e.nextEdge是对象,这意味着只能快速覆盖引用(没有新副本)。 e = e.nextEdge导致引用破坏,但它无关紧要,因为引用现在位于global_val,我们是安全的。

main11()函数中,我们以这种方式调用ProcessEdge():res += ProcessEdge11(e)并将global_val中的引用存储到e:e = global_val

var global_val = {};
function ProcessEdge11(e)
{
  e = e.nextEdge;
  global_val = e;
  return e.a-10;
}

function main11(a)
{
  var res=0, res2, e, b;
  for (var i=0;i<10000;i++)
  {
    e = new Edge();
    b = new Edge();
    b.a = i*a;
    e.nextEdge = b;

    // some processing and then
    res += ProcessEdge11(e);
    e = global_val;
    if (e!=b) res+=1000;  
  }  
  return res;
}

然后还有一个方法值得一提:使用function inside function。它比上述两种方法更慢(最快的84.42%),但相当简单。它基本上依赖于使用global_val的事实:函数可以以不破坏引用的方式访问它的父作用域变量(对象),因为没有本地变量已创建并且该函数处理一个范围的变量。必要的注意事项是变量没有传入&#34;并且没有这样的本地任务会破坏参考。

function main8(a)
{
  var pseudo_global_ret = 0;

  var res=0, res2, e, b;
  for (var i=0;i<10000;i++)
  {
    e = new Edge();
    b = new Edge();
    b.a = i*a;

     e.nextEdge = b;

    _ProcessEdge();
    res += pseudo_global_ret;
    if (e!=b) res+=1000;
  }
  return res;

  function _ProcessEdge()
  {
    e = e.nextEdge;
    pseudo_global_ret = e.a-10; // instead of return e.a-10
  }
}

还有一点需要注意:交换功能的Javascript版本与C#略有不同,其中ref关键字。在C#中我们可以调用swap(ref a, ref b),但在Javascript中这是不可能的。要走的路是:

function swap()
{
  var tmp=a;
  a=b;
  b=tmp;
}
var a={x:0}
var b={x:1}
swap();

如果尝试某种swap(a,b),则引用会中断。当然,这对于封闭内部封闭以防止全球范围的污染至关重要。

如果有人发现更快的参考保留方式,请回答这个问题。

0 个答案:

没有答案