(请原谅我的英语)
下面是一个非常简化的示例代码。
function test(limit) {
let count = 0, undone = true;
function inc() {
// do something
count++;
if(count == limit)
undone = false;
}
while(undone) {
// do something
inc();
}
}
我经常使用闭包样式函数,例如上例中的inc()
。因为共享变量非常方便。
但是,有人告诉我,我的编码风格非常有害。因为每次调用test()
(在上面的示例中),inc()
都会在内存中重复分配。他建议我改用ES6类样式代码。
我不同意他的看法,但不确定。闭包样式代码真的比ES6类样式有害吗?
修改
我对这个问题做了一个简单的基准测试,如前所述 在下面。
https://stackoverflow.com/a/51530670/7339376
结果令我有些惊讶。我错了。我的 顾问是正确的。
答案 0 :(得分:2)
对于上面的示例,使用闭包来维护私有变量和使用类来处理同一件事在理论上没有显着差异。
每次调用/usr/lib
函数时,使用闭包都会分配一个新的函数对象。
每次调用test
函数时,使用类都会分配一个新对象。
理论上,两者都分配内存,并且都花时间调用构造函数。
有些人可能会反对这样的事实,即每次只将类中的方法编译一次时,就需要重新编译该函数。请注意,闭包也可以优化为仅编译一次。因此,这只是一个微观优化问题。
从学术上讲,闭包和类是等效的。我认为,当Lisp在不对语言进行任何修改的情况下使用闭包将对象和类实现为库时,我们首先理解了这一点,但是从那以后,有研究论文发表,证明闭包和类在计算上是等效的-只有它们的功能集略有不同。 / p>
关于优化,您唯一要做的就是对代码进行基准测试。如果足够好,则请勿更改您的代码。与此相反的任何观点仅是宗教/政治观点,应予以忽略。如果您需要更好的性能,则对代码进行概要分析以找到真正的瓶颈。这可能是瓶颈,也可能不是。不要优化不会减慢速度的代码。
一天结束时,请写出最清晰,最易读的代码。
答案 1 :(得分:0)
这是一个非常普遍的论点,反对将功能直接作为属性添加到对象,或者通过在其他对象内部定义它们来添加。但是您不需要ES6来更改此设置。普通的老式函数原型已经为此使用了很长时间。
例如,在此代码中,inc
函数仅创建一次,而不管创建了多少测试对象。</ p>
function test(limit) {
this.limit = limit
this.count = 0
this.undone = true;
}
test.prototype.inc = function() {
console.log("something", this.count)
this.count++;
if (this.count == this.limit)
this.undone = false;
}
let t = new test(10)
while (t.undone) {
t.inc()
}
您还可以使用简单的旧对象原型来执行此操作,该原型使您可以与多个对象共享同一功能:
const test = {
limit: undefined,
count: 0,
undone: true,
inc() {
console.log("something", this.count)
this.count++;
if(this.count == this.limit)
this.undone = false;
}
}
let t = Object.create(test)
t.limit = 5
while(t.undone) {
t.inc()
}
let p = Object.create(test)
p.limit = 5
while(p.undone) {
p.inc() // <-- same inc() as t.inc()
}
答案 2 :(得分:0)
(请原谅我的英语)
我刚刚使用perf
命令在Ubuntu 16.04计算机上进行了基准测试,结果让我感到非常惊讶。我错了。我的顾问是对的。
我简单的ES6类样式代码的效率是闭包样式等效代码的2到3倍。
下面是class.js
,ES类样式,简单的循环代码。
class test {
constructor(limit) {
this.limit = limit;
this.count = 0;
this.undone = true;
while(this.undone) {
this.inc();
}
}
inc() {
this.count++;
if(this.count == this.limit)
this.undone = false;
}
}
for(let i=0; i<5000000; i++) {
new test(100)
}
perf stat node class
的结果(执行3次)。
Performance counter stats for 'node class':
1055.290800 task-clock (msec) # 1.002 CPUs utilized
28 context-switches # 0.027 K/sec
6 cpu-migrations # 0.006 K/sec
2,554 page-faults # 0.002 M/sec
2,858,088,136 cycles # 2.708 GHz
892,221,452 stalled-cycles-frontend # 31.22% frontend cycles idle
<not supported> stalled-cycles-backend
5,815,629,017 instructions # 2.03 insns per cycle
# 0.15 stalled cycles per insn
2,561,547,866 branches # 2427.338 M/sec
6,776,303 branch-misses # 0.26% of all branches
1.053429789 seconds time elapsed
Performance counter stats for 'node class':
1057.919398 task-clock (msec) # 1.002 CPUs utilized
28 context-switches # 0.026 K/sec
6 cpu-migrations # 0.006 K/sec
2,555 page-faults # 0.002 M/sec
2,856,736,277 cycles # 2.700 GHz
890,790,850 stalled-cycles-frontend # 31.18% frontend cycles idle
<not supported> stalled-cycles-backend
5,815,147,889 instructions # 2.04 insns per cycle
# 0.15 stalled cycles per insn
2,561,451,165 branches # 2421.216 M/sec
6,778,756 branch-misses # 0.26% of all branches
1.056058390 seconds time elapsed
Performance counter stats for 'node class':
1054.245840 task-clock (msec) # 1.002 CPUs utilized
27 context-switches # 0.026 K/sec
9 cpu-migrations # 0.009 K/sec
2,553 page-faults # 0.002 M/sec
2,856,022,458 cycles # 2.709 GHz
890,300,670 stalled-cycles-frontend # 31.17% frontend cycles idle
<not supported> stalled-cycles-backend
5,815,241,984 instructions # 2.04 insns per cycle
# 0.15 stalled cycles per insn
2,561,469,424 branches # 2429.670 M/sec
6,768,183 branch-misses # 0.26% of all branches
1.052295061 seconds time elapsed
下面是closure.js
(关闭样式等效于class.js)
function test(limit) {
let count = 0, undone = true;
function inc() {
count++;
if(count == limit)
undone = false;
}
while(undone) {
inc();
}
}
for(let i=0; i<5000000; i++) {
test(100)
}
还有perf stat node closure
的结果(也执行了3次)。
Performance counter stats for 'node closure':
2377.214932 task-clock (msec) # 1.002 CPUs utilized
389 context-switches # 0.164 K/sec
9 cpu-migrations # 0.004 K/sec
3,141 page-faults # 0.001 M/sec
6,560,657,082 cycles # 2.760 GHz
1,946,461,285 stalled-cycles-frontend # 29.67% frontend cycles idle
<not supported> stalled-cycles-backend
16,046,574,530 instructions # 2.45 insns per cycle
# 0.12 stalled cycles per insn
6,110,865,652 branches # 2570.599 M/sec
6,953,209 branch-misses # 0.11% of all branches
2.371426270 seconds time elapsed
Performance counter stats for 'node closure':
2381.292759 task-clock (msec) # 1.002 CPUs utilized
391 context-switches # 0.164 K/sec
8 cpu-migrations # 0.003 K/sec
3,142 page-faults # 0.001 M/sec
6,558,376,504 cycles # 2.754 GHz
1,943,542,624 stalled-cycles-frontend # 29.63% frontend cycles idle
<not supported> stalled-cycles-backend
16,046,969,491 instructions # 2.45 insns per cycle
# 0.12 stalled cycles per insn
6,110,955,583 branches # 2566.234 M/sec
6,967,852 branch-misses # 0.11% of all branches
2.375578434 seconds time elapsed
Performance counter stats for 'node closure':
2376.579401 task-clock (msec) # 1.003 CPUs utilized
388 context-switches # 0.163 K/sec
7 cpu-migrations # 0.003 K/sec
3,125 page-faults # 0.001 M/sec
6,562,861,447 cycles # 2.761 GHz
1,947,165,390 stalled-cycles-frontend # 29.67% frontend cycles idle
<not supported> stalled-cycles-backend
16,051,805,939 instructions # 2.45 insns per cycle
# 0.12 stalled cycles per insn
6,111,984,155 branches # 2571.757 M/sec
6,962,744 branch-misses # 0.11% of all branches
2.369557403 seconds time elapsed