【问题标题】:javascript should we share functions whenever possible or create new ones?javascript 我们应该尽可能共享函数还是创建新函数?
【发布时间】:2011-05-27 07:04:29
【问题描述】:

我想知道哪种情况的“开销”更大:

1) 案例 1:500 万个对象共享 30 个功能。每次调用函数时,都会产生开销,因为需要执行 f.call(instance, arg1, arg2, etc)

 //example code
function makeObject()
{
  return { method1:func1,
           method2:func2,
           ...
           method30:func30 };
}

2) 案例 2:500 万个对象,每个对象有 30 个函数(= 1.5 亿个单独的函数实例)。每次调用函数时,都没有“路由开销”,但当然会牺牲更多实例

//example code
function makeObject()
{
  return { method1:func1.bind(asd),
           method2:func2.bind(asd),
           ...
           method30:func30.bind(asd) };
}

500 万只是我的手指打出的一个数字,而我的大脑仍在计算一个很好的数字作为示例。

基本上我想知道我们应该尽可能共享函数还是创建新函数?

(你可以假设我永远不会在整个页面的任何地方使用 eval 函数)

【问题讨论】:

  • 我假设这 5m 个对象是动态创建的?
  • 运行测试。这是唯一知道或确定的方法。 (为什么不直接使用“原型链”,让引擎“共享”功能?)
  • 500 万将是不现实的,可能仅用于基准测试目的

标签: javascript web-services


【解决方案1】:

由于几乎所有现代浏览器都优化了prototype-scopeschain 查找,因此您绝对应该去共享方法。

用简单的话描述的优化技术,是一种hash lookup table,javascript引擎使用它来访问out of scope变量的属性/方法。因此,与经典的 scope chain lookup 相比,引擎必须爬过每个父作用域变量/激活对象的开销很小。

如果有某种直接的eval'ed 代码,这种优化的lookup 只会失败。由于eval 可以从上下文更改属性,因此引擎必须回退到经典的查找算法(这有点慢)。

但是,5m 对象对于 Javascript 引擎来说有点不真实,我希望这些数字只是示例。在现实世界的场景中,5m 个对象调用 n 数量的函数会在各处造成堆栈溢出和“运行脚本过长”错误。

单独parsing time 拥有 1.5 亿个函数将是可耻的。

【讨论】:

  • 仅供参考 Chrome (V8) 和 FF (TraceMonkey) 都缓存函数声明,并且只真正创建一个对象字面量 ({}) 和一个用于具有相同源的新函数的指针,它们不会重新-解析并编译它们。
  • @cwolves:很有趣。有没有详细描述该技术的资源?我想知道限制在哪里(即超出范围的方法和评估代码)。
  • @cwolves 你的意思是说创建附加函数的开销最小(与范围链查找开销相比)
  • 而这个“仅1.5亿个函数的解析时间将是可耻的”。来自?每个函数编译一次。创建 5m 个对象,每个对象具有 30 个属性是需要时间的。
  • @Pacerier, @c-smile -- 我的意思是说较新的浏览器缓存功能 compilation 而旧浏览器没有。这意味着此代码:for(var i=0; i<10; i++){ foo.func = function(){ // stuff }; } 在旧版浏览器中效率极低。在较新的浏览器中,中间部分(迭代 2--10)在速度方面与 foo.func = {} 几乎相同
【解决方案2】:

如果假设情况 2 是关于创建类似这样的对象:

function makeObject()
{
  return { method1:func1,
           method2:func2,
           ...
           method30:func30 };
}

那么您将拥有 5m 个对象,每个对象具有 30 个属性。不是“1.5 亿个单独的函数实例”。函数实例数仍为 30。

创建 5m 这样的对象会花费你一些东西。添加属性的成本大约是读取属性的 3-10 倍。这意味着创建此类集合所花费的时间可能比您的程序使用__proto__ 使用一级间接寻址访问方法所花费的时间更多。

简而言之:对于 5m/30,案例 1 在大多数情况下更为理想。但是在某些情况下,将方法引用放入对象本身是值得考虑的。例如。频繁访问的对象/方法的数量有限(例如单例)。

【讨论】:

  • 您的代码适用于案例 1。案例 2 不是这样(我已经用示例更新了问题)
  • [quote] 但是在某些情况下,将方法引用放入对象本身是值得考虑的。例如。经常访问的对象/方法的数量有限(例如单例)。[/quote] 抱歉,您介意详细说明吗? (我真的不明白)
  • @Pacerier:正如我所说,如果你有单例对象(例如,JS 中命名空间的模拟是由单例对象进行的),那么将函数直接放入这样的单例对象中是有意义的。如果您有许多实例共享同一个类(也称为原型)的经典对象/类情况,那么您应该使用 .prototype.foo = function(){} 选项。
  • 好的(顺便说一句,我正在测试这是否会通知您,请告诉我是否会通知您。谢谢)
【解决方案3】:

为什么不这样呢? :

function myObject() { }

myObject.prototype.method1 = func1
myObject.prototype.method2 = func2,
           ...
myObject.prototype.method30 = func30

因此,如果您将对象创建为

var obj = new myObject();

那么你将调用它的方法:

obj.method1(); 
obj.method2(); 

不需要call() 和其他魔法......

在这种情况下,您不需要使用相同的 30 个属性来填充对象的每个实例。

【讨论】:

  • 我的问题是针对多重继承问题。所以基本上我想知道B类是否扩展了A类,那么B类的实例是否应该与A类的实例使用相同的函数实例?或者我们应该为每个实例创建新的函数实例(因为这样更容易)
猜你喜欢
  • 2016-08-21
  • 1970-01-01
  • 2011-09-30
  • 1970-01-01
  • 2012-10-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-29
相关资源
最近更新 更多