CD's blog CD's blog
首页
  • HTMLCSS
  • JavaScript
  • Vue
  • TypeScript
  • React
  • Node
  • Webpack
  • Git
  • Nestjs
  • 小程序
  • 浏览器网络
  • 学习笔记

    • 《TypeScript 从零实现 axios》
    • Webpack笔记
  • JS/TS教程

    • 《现代JavaScript》教程
🔧工具方法
  • 网站
  • 资源
  • Vue资源
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

CD_wOw

内卷的行情,到不了的梦
首页
  • HTMLCSS
  • JavaScript
  • Vue
  • TypeScript
  • React
  • Node
  • Webpack
  • Git
  • Nestjs
  • 小程序
  • 浏览器网络
  • 学习笔记

    • 《TypeScript 从零实现 axios》
    • Webpack笔记
  • JS/TS教程

    • 《现代JavaScript》教程
🔧工具方法
  • 网站
  • 资源
  • Vue资源
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 简介

  • JavaScript 基础知识

  • 数据类型

  • 函数进阶内容

    • 构造函数
    • this原理
    • 递归和堆栈
    • 全局对象
    • 装饰器模式和转发,call/apply
    • Rest 参数与 Spread 语法
    • 函数对象,NFE
    • 函数绑定
    • 变量作用域,闭包
    • new Function 语法
      • 语法
      • 闭包
      • 总结
    • 深入理解箭头函数
    • 旧时的 "var"
    • 调度:setTimeout 和 setInterval
  • 对象属性配置

  • 原型,继承

  • 类

  • 错误处理

  • Promise,asyncawait

  • Generator,高级 iteration

  • 模块

  • 杂项

  • 《现代JavaScript》教程
  • 函数进阶内容
CD_wOw
2020-12-13
目录

new Function 语法

还有一种创建函数的方法。它很少被使用,但有些时候只能选择它

# 语法

创建函数的语法:

let func = new Function ([arg1, arg2, ...argN], functionBody);
1

该函数是通过使用参数 arg1...argN 和给定的 functionBody 创建的。

下面这个例子可以帮助你理解创建语法。这是一个带有两个参数的函数:

let sum = new Function('a', 'b', 'return a + b');

alert( sum(1, 2) ); // 3
1
2
3

这里有一个没有参数的函数,只有函数体:

let sayHi = new Function('alert("Hello")');

sayHi(); // Hello
1
2
3

与我们已知的其他方法相比,这种方法最大的不同在于,它实际上是通过运行时通过参数传递过来的字符串创建的。

以前的所有声明方法都需要我们 —— 程序员,在脚本中编写函数的代码。

但是 new Function 允许我们将任意字符串变为函数。例如,我们可以从服务器接收一个新的函数并执行它:

let str = ... 动态地接收来自服务器的代码 ...

let func = new Function(str);
func();
1
2
3
4

使用 new Function 创建函数的应用场景非常特殊,比如在复杂的 Web 应用程序中,我们需要从服务器获取代码或者动态地从模板编译函数时才会使用。

# 闭包

通常,闭包是指使用一个特殊的属性 [[Environment]] 来记录函数自身的创建时的环境的函数。它具体指向了函数创建时的词法环境。

但是如果我们使用 new Function 创建一个函数,那么该函数的 [[Environment]] 并不指向当前的词法环境,而是指向全局环境。

因此,此类函数无法访问外部(outer)变量,只能访问全局变量

function getFunc() {
  let value = "test";

  let func = new Function('alert(value)');

  return func;
}

getFunc()(); // error: value is not defined
1
2
3
4
5
6
7
8
9

将其与常规行为进行比较:

function getFunc() {
  let value = "test";

  let func = function() { alert(value); };

  return func;
}

getFunc()(); // "test",从 getFunc 的词法环境中获取的
1
2
3
4
5
6
7
8
9

new Function 的这种特性看起来有点奇怪,不过在实际中却非常实用。

想象以下我们必须通过一个字符串来创建一个函数。在编写脚本时我们不会知道该函数的代码(这也就是为什么我们不用常规方法创建函数),但在执行过程中会知道了。我们可能会从服务器或其他来源获取它。

我们的新函数需要和主脚本进行交互。

如果这个函数能够访问外部(outer)变量会怎么样?

问题在于,在将 JavaScript 发布到生产环境之前,需要使用 压缩程序(minifier) 对其进行压缩 —— 一个特殊的程序,通过删除多余的注释和空格等压缩代码 —— 更重要的是,将局部变量命名为较短的变量。

例如,如果一个函数有 let userName,压缩程序会把它替换为 let a(如果 a 已被占用了,那就使用其他字符),剩余的局部变量也会被进行类似的替换。一般来说这样的替换是安全的,毕竟这些变量是函数内的局部变量,函数外的任何东西都无法访问它。在函数内部,压缩程序会替换所有使用了使用了这些变量的代码。压缩程序很聪明,它会分析代码的结构,而不是呆板地查找然后替换,因此它不会“破坏”你的程序。

但是在这种情况下,如果使 new Function 可以访问自身函数以外的变量,它也很有可能无法找到重命名的 userName,这是因为新函数的创建发生在代码压缩以后,变量名已经被替换了。

即使我们可以在 new Function 中访问外部词法环境,我们也会受挫于压缩程序。

此外,这样的代码在架构上很差并且容易出错。

当我们需要向 new Function 创建出的新函数传递数据时,我们必须显式地通过参数进行传递。

# 总结

语法:

let func = new Function ([arg1, arg2, ...argN], functionBody);
1

由于历史原因,参数也可以按逗号分隔符的形式给出。

以下三种声明的含义相同:

new Function('a', 'b', 'return a + b'); // 基础语法
new Function('a,b', 'return a + b'); // 逗号分隔
new Function('a , b', 'return a + b'); // 逗号和空格分隔
1
2
3

使用 new Function 创建的函数,它的 [[Environment]] 指向全局词法环境,而不是函数所在的外部词法环境。因此,我们不能在 new Function 中直接使用外部变量。不过这样是好事,这有助于降低我们代码出错的可能。并且,从代码架构上讲,显式地使用参数传值是一种更好的方法,并且避免了与使用压缩程序而产生冲突的问题。

编辑 (opens new window)
#现代JavaScript
上次更新: 2021/08/22, 01:09:59
变量作用域,闭包
深入理解箭头函数

← 变量作用域,闭包 深入理解箭头函数→

最近更新
01
gsap动画库学习笔记 - 持续~
06-05
02
远程组件加载方案笔记
05-03
03
小程序使用笔记
03-29
更多文章>
Theme by Vdoing | Copyright © 2020-2023 CD | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式