深入理解Js里new Function语法

简介: 深入理解Js里new Function语法

一般我们不会使用用 new Function 构造函数的,因为没必要,直接使用 function 或者 箭头函数写法更简单。但并不是说new Function 构造函数无用。在一些特别的场景,比如函数体的数据格式是字符串的时候,new Function 构造函数的作用就显示出来了。之前也是仅仅知道此方法,但是没有具体的研究搞懂,但是最近一两年一直在倒腾低代码的项目,原理上来说,低代码都是一堆字符串,为了解析字符串就使用了new Function 构造函数(eval方法也是可以的),在此在记录一下,加深理解。详情参见:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions

1、语法

备注: 不推荐使用 Function 构造函数创建函数,因为它需要的函数体作为字符串可能会阻止一些 JS 引擎优化,也会引起其他问题。把 Function 的构造函数当作函数一样调用 (不使用 new 操作符) 的效果与作为 Function 的构造函数调用一样。

let func = new Function ([arg1, arg2, ...argN], functionBody);
//等价于 let func = Function ([arg1, arg2, ...argN], functionBody);

<font color="red">Function构造函数所有的参数都是字符串类型。除了最后一个参数, 其余的参数都作为生成函数的参数即形参。这里可以没有参数。最后一个参数, 表示的是要创建函数的函数体。</font>

//传入参数
let sum = new Function('a', 'b', 'return a + b');
console.log( sum(1, 2) ); // 3
//不传入参数
let sum = new Function('console.log(1)');
console.log( sum() ); // 1

由于历史原因,new Function参数也可以以逗号分隔的列表形式给出。下边这三个声明的含义相同:

new Function('a', 'b', 'return a + b'); 
new Function('a,b', 'return a + b'); 
new Function('a , b', 'return a + b');

2、作用域

Function()构造函数和函数有一点就是:使用构造函数Function()创建的函数不使用当前的词法作用域,相反的,它们总是被顶级函数来编译,因此在运行时它们只能访问全局变量和自己的局部变量.

let a = 1
let fn = function(){
  let a = 2
  let result1 = new Function('console.log(a)')
  let result2 = function(){
    console.log(a)
  }
  result1() //打印出1,访问的是全局变量a
  result2() //打印出2
}
fn()
// new Function这样的函数不能访问外部变量,只能访问全局变量
// 虽然这段代码可以在浏览器中正常运行,但在 Node.js 中,result1() 执行会报错,因为找不到变量 a。
// 这是因为,在 Node 中,顶级作用域不是全局作用域,而 a 其实是在模块的作用域之中。

想象一下,我们必须从一个字符串创建一个函数。该函数的代码在编写脚本时是未知的,但是会在执行过程中知道。我们可能会从服务器或其他地方接收到它。此时我们的新函数需要与主脚本交互,如果它此时它可以访问外部变量,那么就可以操作外部变量,改变外部变量,这样就会引发不可预估的风险。

3、使用

假如有一个非合法 JSON 对象字符串,如下:

let str = "{ name: '小坦克', code: 100 }" 
  JSON.parse(str) // 会报错,因为str字段是不符合规范的对象字符串(key,value都必须是"",双引号包裹)

可以使用new Function

let str = "{ name: '小坦克', code: 100 }" 
  let result =  JSON.parse(new Function('return ' + str)()) // result = { name: '小坦克', code: 100 }

4、new Function和eval的区别

eval中的代码执行时的作用域为当前作用域。它可以访问到函数中的局部变量。

let a = 1
let fn = function(){
  let a = 2
  let result1 = new Function('console.log(a)')
  let result2 = eval('console.log(a)') //打印出2
  result1() //打印出1,访问的是全局变量a
}
fn()

永远不要使用 eval !!!

eval() 是一个危险的函数, 它使用与调用者相同的权限执行代码。如果你用 eval() 运行的字符串代码被恶意方(不怀好意的人)修改,您最终可能会在您的网页/扩展程序的权限下,在用户计算机上运行恶意代码。更重要的是,第三方代码可以看到某一个 eval() 被调用时的作用域,这也有可能导致一些不同方式的攻击。相似的 Function 就不容易被攻击

eval() 通常比其他替代方法更慢,因为它必须调用 JS 解释器,而许多其他结构则可被现代 JS 引擎进行优化。

此外,现代 JavaScript 解释器将 JavaScript 转换为机器代码。这意味着任何变量命名的概念都会被删除。因此,任意一个 eval 的使用都会强制浏览器进行冗长的变量名称查找,以确定变量在机器代码中的位置并设置其值。另外,新内容将会通过 eval() 引进给变量,比如更改该变量的类型,因此会强制浏览器重新执行所有已经生成的机器代码以进行补偿。但是(谢天谢地)存在一个非常好的 eval 替代方法:只需使用 window.Function。这有个例子方便你了解如何将eval()的使用转变为Function()。

相关文章
|
1月前
|
JavaScript
ES6学习(9)js中的new实现
ES6学习(9)js中的new实现
|
2月前
|
存储 JavaScript 前端开发
Node.js的基本语法
【8月更文挑战第12天】Node.js的基本语法
93 1
|
25天前
|
JavaScript 前端开发
一个js里可以有多少个async function,如何用最少的async function实现多个异步操作
在 JavaScript 中,可以通过多种方法实现多个异步操作并减少 `async` 函数的数量。
|
2月前
|
JavaScript 前端开发
JavaScript基础&实战(1)js的基本语法、标识符、数据类型
这篇文章是JavaScript基础与实战教程的第一部分,涵盖了JavaScript的基本语法、标识符、数据类型以及如何进行强制类型转换,通过代码示例介绍了JS的输出语句、编写位置和数据类型转换方法。
JavaScript基础&实战(1)js的基本语法、标识符、数据类型
|
2月前
|
SQL JavaScript 前端开发
【Azure 应用服务】Azure JS Function 异步方法中执行SQL查询后,Callback函数中日志无法输出问题
【Azure 应用服务】Azure JS Function 异步方法中执行SQL查询后,Callback函数中日志无法输出问题
|
2月前
|
前端开发 JavaScript 程序员
前端 JavaScript 的 _ 语法是个什么鬼?
前端 JavaScript 的 _ 语法是个什么鬼?
|
3月前
|
缓存 JavaScript 前端开发
前端框架与库 - Vue.js基础:模板语法、数据绑定
【7月更文挑战第14天】Vue.js 是渐进式框架,以简洁API和高效数据绑定知名。本文聚焦模板语法与数据绑定,解释常见问题和易错点,助力初学者避坑。模板语法中,{{ expression }} 用于渲染值,v-bind/: 用于动态绑定属性。数据绑定涉及文本、属性和事件,注意v-model适用于表单元素,计算属性有缓存。理解正确用法,借助文档和IDE,可提升开发质量和效率。善用Vue.js,打造响应式UI。
87 4
|
4月前
|
存储 JavaScript 前端开发
JavaScript 语法
JavaScript 语法
31 5
|
3月前
|
存储 JavaScript 前端开发
|
4月前
|
JavaScript 前端开发
JavaScript函数是代码复用的关键。使用`function`创建函数
【6月更文挑战第22天】JavaScript函数是代码复用的关键。使用`function`创建函数,如`function sayHello() {...}`或`function addNumbers(num1, num2) {...}`。调用函数如`sayHello()`执行其代码,传递参数按值进行。函数可通过`return`返回值,无返回值默认为`undefined`。理解函数对于模块化编程至关重要。
36 4
下一篇
无影云桌面