Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/456.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
JavaScript中无分配循环的模式?_Javascript_Memory Management_Garbage Collection - Fatal编程技术网

JavaScript中无分配循环的模式?

JavaScript中无分配循环的模式?,javascript,memory-management,garbage-collection,Javascript,Memory Management,Garbage Collection,假设我们正在编写一个浏览器应用程序,其中平滑动画至关重要。我们知道垃圾收集会阻塞执行足够长的时间,导致明显的冻结,因此我们需要将创建的垃圾量降至最低。为了最小化垃圾,我们需要在主动画循环运行时避免内存分配 但这条执行路径布满了循环: var i = things.length; while (i--) { /* stuff */ } for (var i = 0, len = things.length; i < len; i++) { /* stuff */ } 优点:实施起来非常简

假设我们正在编写一个浏览器应用程序,其中平滑动画至关重要。我们知道垃圾收集会阻塞执行足够长的时间,导致明显的冻结,因此我们需要将创建的垃圾量降至最低。为了最小化垃圾,我们需要在主动画循环运行时避免内存分配

但这条执行路径布满了循环:

var i = things.length; while (i--) { /* stuff */ }

for (var i = 0, len = things.length; i < len; i++) { /* stuff */ }
优点:实施起来非常简单缺点:取消对属性的引用会影响性能,这可能意味着代价高昂的胜利。可能会意外误用/clobber属性并导致错误

2.)如果数组长度已知,则不要循环--展开 我们可以保证一个数组有一定数量的元素。如果我们事先知道长度,我们可以在程序中手动解开循环:

doSomethingWithThing(things[0]);
doSomethingWithThing(things[1]);
doSomethingWithThing(things[2]);
优点:高效缺点:在实践中几乎不可能。丑陋?你想改变吗

3.)通过工厂模式利用闭合 编写一个返回“looper”的工厂函数,该函数对集合的元素执行操作(一个la
\uuu.each
)。循环器在创建的闭包中保持对索引和长度变量的私有引用。每次调用活套时,活套必须重置
i
length

function buildLooper() {
  var i, length;
  return function(collection, functionToPerformOnEach) { /* implement me */ };
}
app.each = buildLooper();
app.each(things, doSomethingWithThing);
优点:更实用、更地道缺点:函数调用会增加开销。闭包访问要比对象查找慢

它们的var语句allocate memory可以分配垃圾收集器可能删除的内存,这是我们想要避免的

这有点误传。仅使用
var
不会在堆上分配内存。调用函数时,函数中使用的每个变量都会预先在堆栈上分配。当函数完成执行时,将弹出堆栈帧,并立即取消对内存的引用

与垃圾收集相关的内存问题是在堆上分配对象时出现的。这意味着以下任何一项:

  • 闭包
  • 事件侦听器
  • 阵列
  • 物体
在大多数情况下,
typeof foo
返回
“函数”
“对象”
(或任何新的ES6
typeof
返回值)的任何内容都将在堆上生成对象。可能还有更多我现在想不起来的

堆上的对象的问题是它们可以引用堆上的其他对象。例如:

var x = {};
x.y = {};
delete x;
在上面的示例中,浏览器无法取消分配
x
的插槽,因为其中包含的值大小可变。它位于堆上,然后可以指向其他对象(在本例中,是位于
x.y
的对象)。另一种可能性是对同一对象有第二个引用:

var x = {};
window.foo = x;
delete x;
浏览器无法从内存中删除
x
处的对象,因为还有其他对象指向它


长话短说,不要担心删除变量,因为它们工作得非常好,而且性能完全好。在垃圾收集方面,堆分配是真正的敌人,但即使是这里或那里的一些小堆分配也不会伤害大多数应用。

我非常怀疑你的前提。您认为为什么
var
分配GC关心的内存?即使在一个简单而愚蠢的基于堆栈的字节码虚拟机(如CPython)中,局部变量也是frame对象中的本机数组,在函数运行时从未单独分配或添加/删除。由于JavaScript的闭包、
with
eval
,函数的执行上下文(一种帧对象类型)可以在函数返回后继续存在。必须存活的EC将被分配到堆上,但具体何时/如何发生取决于引擎。(像V8这样的智能引擎将进行优化,为本地变量使用堆栈——只有在上下文变量必须比函数调用更有效时才将其复制到堆中)。如果/当这些堆对象变得不可引用/不可访问时,它们将是GC'd。好吧,但这将是一个根本不调用函数的原因(如果这是真的,即使这样,我也会非常怀疑),而不是一个避免
var
的原因。如果没有
eval
/
with
(可以静态确定,也可以在运行时进行假设和检查),在创建EC时只为EC中的所有局部变量分配空间是很简单的,因此单个
var
不需要任何分配。顺便说一句@delman——感谢您对前提的质疑。你促使我做更多的研究。在这之后,在我看来,有足够的因素发挥作用,使这些类型的优化的价值具有争议性。或者,如果一个人注意巧妙地设计功能,这可能是一个没有实际意义的问题。FWIW,我见过像googleclosure这样的库使用
函数(a,b,returnValue)
模式来避免分配。这是一个很好的答案。非常感谢你花时间对我的问题给出了彻底的回答。
var x = {};
window.foo = x;
delete x;