函数嵌套函数,会在内存中存在多个函数吗?
发布于 4 年前 作者 daGaiGuanYu 2165 次浏览 来自 问答

比如:

function main(){
  return function inner(){}
}

每次调用 main 方法,都返回一个新的 inner,但是这个 inner 会重新创建吗?
或者说,调用两次后,内存中有几个 inner 函数被创建出来?

11 回复

每次调用main后,都返回一个新的 inner。 return function … 其实就是 return (new Function(’…’)); 另外,内存中也并不是简单的 “多份 inner 函数”,而是多份包含了inner()上下文的闭包。

@myy 多份上下文闭包肯定是必要的,但是感觉多份 inner 函数有点不舒服(不是对结果的怀疑,还是对设计的不解)
比如,如果“内存有多份 inner 函数”,那

class A{
  inner(){}
}

就会比

function A (){
  return {
    inner(){}
  }
}

省内存了(有多个 A 实例时)。
但第二种写法,不用关心 this 的指向问题,反而比“先进”的 class 关键字更舒服。

@myy 因为像字符串,比如

let a = 'hello'
let b = 'hello'

这种情况,内存中就只有一个 hello 字符串
函数的话,如果有这种优化,就舒服了

class A 多个实例虽然共用了 inner(),但是A本身同样是多份啊。。也没省啥内存。

@myy 多实例,共用一个 __proto__,也就是 A.prototype

class A {
  go(){ console.log(1) }
}
const x = new A()
const y = new A()
x.go() // 1
y.go() // 1
const oldGo = x.go
console.log(oldGo == A.prototype.go)
A.prototype.go = function (){ console.log(2) }
const z = new A()
z.go() // 2
x.go() // 2
y.go() // 2
const newGo = x.go
console.log(newGo == A.prototype.go)
console.log(newGo != oldGo)

会返回多个函数的闭包对象 image.png 测试代码如下:

function main() {
  return function inner() { }
}

const array = [];

for (let i = 0; i >= 0; i++) {
  const fn = main();
  array.push(fn);
}

相比之下,你的第二种方式只会增加引用本身,确实会更节省内存,实际上这也是单例模式的优势之一

image.png

测试代码:

class A {
  go() { console.log(1) }
}
const x = new A()

const array = [];

for (let i = 0; i >= 0; i++) {
  array.push(x.go);
}

@hyj1991 你用的什么工具啊老兄,看起来很棒的样子

@hyj1991 class 的形式虽然没有闭包对象,但有实例啊
打印 go 的话,看不到实例占用的内存,但在上下文方面,两种形式应该差别不大

还有个问题要请教一下(因为上面的没看懂……)
是不是说 function main(){ return function inner(){} } 这种形式,只增加闭包,而不重新创建 inner?

@daGaiGuanYu

工具是这个:https://github.com/hyj1991/easy-monitor

截图是我还在开发中的堆快照本地分析功能,拿来试了下你的这个问题。

是不是说 function main(){ return function inner(){} } 这种形式,只增加闭包,而不重新创建 inner?

是的,你看上面两个测试都是创建了100w次函数,所以两者的引用大小(object elements)是一样的 9.95MB,但是前者会额外多100w个闭包对象,导致内存占用大大超过后者

@hyj1991 感谢~ 但是,new A() 也会创建同样数量的实例的,内存占用应该也很大~

回到顶部