函数不能被深拷贝,因其包含不可序列化的闭包环境(代码逻辑+词法作用域),Function构造函数和eval均无法复现原始变量引用关系,JSON.stringify直接忽略函数。

深拷贝函数对象时,既不能用 Function 构造函数,也不能用 eval来“复制闭包上下文”——因为闭包本身无法被真正克隆。
为什么函数不能被深拷贝?
函数(尤其是闭包)的本质是:代码逻辑 + 词法环境(即它定义时所捕获的外部变量)。这个环境在 JS 引擎中是私有、不可序列化、不可导出的。任何试图“复制函数”的操作,都只能复制其代码字符串或执行能力,无法复现原始闭包中的变量引用关系。
- Function 构造函数创建的新函数,作用域链只含全局,访问不到原函数闭包里的局部变量
- eval 在当前作用域执行,能读取外层变量,但它不生成新函数对象,也不保留原函数的 this 绑定、原型、属性等元信息
- JSON.stringify 会直接忽略函数,序列化后函数消失
Function 构造函数的适用场景
它适合动态生成纯逻辑、无依赖、无闭包的函数,比如配置驱动的简单计算:
- 用户输入表达式如
"a + b * 2"→new Function("a", "b", "return " + expr) - 低代码平台中预设规则模板,所有变量都通过参数显式传入
- 不依赖外层作用域,也不需要继承原函数的属性或行为
eval 的风险与局限
eval 虽能访问当前作用域变量,但它不是克隆工具,而是执行工具:
- 执行的是字符串代码,不产生函数实例,无法赋值给变量再调用
- 若用于“还原”函数,需手动拼接完整函数体字符串,极易出错且丢失 name、length、prototype 等
- 存在严重 XSS 风险,一旦输入可控(如用户提交的规则),就可能执行恶意脚本
实际可行的替代思路
面对含闭包的函数,应放弃“克隆”,转向设计层面规避:
-
解耦闭包:把依赖的外部状态提取为参数,使函数变成纯函数(例如
(data, config) => {...}) -
用工厂函数替代:不拷贝闭包函数,而是重新调用工厂,生成新闭包(
createHandler(state)) - 序列化可转移状态:深拷贝函数所依赖的数据(如 config 对象),再结合固定函数逻辑重建行为
- 标记不可拷贝:在自定义深拷贝逻辑中,遇到函数直接跳过或抛出提示,避免静默失效
不复杂但容易忽略:函数不是数据,它是运行时行为载体。深拷贝的目标是数据独立性,而函数的语义天然绑定环境——与其强行克隆,不如重构使其可移植。

















