动态解析和执行函数

219 阅读2分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

前言

我们平时写前端代码的时候,函数都是提前编写好的。

今天我们就聊聊到到动态解析和执行函数,来点不一样的!!!!

动态解析和执行函数两种方式

  • eval
  • new Function

eval

函数会将传入的字符串当做 JavaScript 代码进行执行

简单计算:

eval(`2+2`) // 4

代码段:

var a = 10;
eval(`if (a >10) {console.log("大于10")}else console.log("小于等于10")`)
// 小于等于10

可以看到,eval是能访问当创建时所在的作用域的变量的。

字符转Json

在JSON.parse之前,我们常用eval把字符串转为对象

eval("({a:1, b:2})") 
// {a: 1, b: 2}

可以简单封装一下:

function parseJSON(str){
    return eval("(" + str + ")") 
}

parseJSON("({a:1, b:2})")
// {a: 1, b: 2}

实际上setTimout的第一个参数,也是可以传入字符串的

setTimeout("console.log('好啊')", 1000)
// 好啊

其背后的工作机制就是 eval, 你可以用CSP禁止eval后再尝试,就知道了。

会爆出错误来!

eval的this

eval的this是和当前执行的上下文挂钩的,我们一起看看

var person = {
    getName(){
        console.log("this", eval("this"))
    }
}

person.getName()
//  {getName: ƒ}

window.eval("this")
// Window {window: Window, self: Window, document: document, name: '', location: Location, …}

所以直接调用eval和调用window.eval的区别是很大的。

new Function

语法

new Function ([arg1[, arg2[, ...argN]],] functionBody)

前面是参数名,最后一个是函数体

参数名和name

var sum = new Function("num1", "num2", "return num1+num2");
sum(1, 2);
// 3
console.log("name", sum.name);
// anonymous

sum.name = "sum"
console.log("name", sum.name);
// anonymous

我们自定义了一个加法函数, 其属性name为anonymous, 这个和空字符串还有区别。 我们一起看看空函数名的函数。


var person = {
  name: "Tom"
};

person.getName = function (){
  return this.name
}

console.log("name:", person.getName.name); 
// name: 

name为anonymous不等于name为""。

this属性

var sum = new Function("num1", "num2", `
    console.log("this",this); 
    return num1+num2
 `);
sum(1,2)
// Window {window: Window, self: Window, document: document, name: '', location: Location, …}

var person = {
    getName: new Function(` console.log("this",this);`)
}
person.name()
// this {getName: ƒ}

var person2 = {
    getName(){
       return (new Function(`console.log("this:", this)`))()        
    }
}
person2.getName()
// this: Window {window: Window, self: Window, document: document, name: '', location: Location, …}

var getName = function() {
    var nameXX = "tom";    
    return new Function("return nameXX")()
}

getName()
// Uncaught ReferenceError: nameXX is not defined

可以看到 new Function全局环境,因此在运行时它们只能访问全局变量和自己的局部变量,不能访问它们被 Function 构造器创建时所在的作用域的变量。

小结

  1. eval是恶魔,尽量不适用
  2. new Function更加安全,性能更加好, 不能访问它们被 Function 构造器创建时所在的作用域的变量。 eval() 通常比其他替代方法更慢,因为它必须调用 JS 解释器,而许多其他结构则可被现代 JS 引擎进行优化。

今天你收获了吗?