【问题标题】:eval(fn) and eval(arrowFn) returns different valueeval(fn) 和 eval(arrowFn) 返回不同的值
【发布时间】:2017-05-05 12:59:33
【问题描述】:

根据Mozilla docs,为了使用eval 执行函数,它必须包含在( ) 中,即如果您不使用它们,则将其视为字符串。

eval 作为字符串定义函数需要“(”和“)”作为前缀和后缀

当我执行普通函数时,它会按预期返回undefined,但对于 ES6 函数则不会。我的问题是 JavaScript 引擎或仅在 eval 函数中对 ES6 函数的处理方式不同。

var fn = "function a(){}";
var es6fn = "()=>{}";

console.log(eval(fn)); // undefined
console.log(eval(es6fn)); // ()=>{}
console.log(typeof eval(es6fn)); // ()=>{} i.e. a function

【问题讨论】:

  • es6 Lamda 表达式的行为类似于匿名函数 - 所以这是 fn 和 es6fn 之间的一个区别
  • 附带说明:这很好玩,但请不要在生产中使用它 - 你未来的自己会感激不尽

标签: javascript ecmascript-6 eval arrow-functions


【解决方案1】:

让我们退后一步,看看这里实际发生了什么。我认为您误解了 MDN 试图提出的观点。在您的示例中执行的唯一函数是 eval。文档中提到的(...) 不是用于在字符串中执行函数,而是用于更改函数定义的评估方式

函数调用会function a(){}(),但文档谈到将函数定义放在括号内:(function(){})


定义函数的方式主要有以下几种:

  1. 函数声明

    function foo() {}
    
  2. 函数表达式

    var foo = function() {}
    
  3. 箭头函数

    var foo = () => {}
    

要了解函数声明和函数表达式之间的区别,我们必须了解语句表达式之间的区别(声明基本上就像一个声明)。

语句是具有副作用并且产生的东西。 ifforswitch等都是语句

表达式是产生值的东西。例如。 5 是一个产生值 5 的数字 literal5 + 3 是一个计算两个字面值之和的表达式,即计算它会返回值 8。

一个函数声明就像一个声明。它本身不会产生值,但作为副作用,定义了一个其值为函数的变量(you can see in the specification that nothing happens when a function declaration is evaluated(它们已经在那时处理过))。

函数表达式非常相似,但不是定义变量,而是对其求值,从而得到函数对象。

这就是原因

eval('function a() {}') // undefined, but a is defined as side effect
eval('(function a() {})') // a function object

产生不同的结果。第一个被解释为函数声明。将创建变量a,但不会创建eval 可以返回的值。在第二种情况下,分组运算符(...))强制将函数定义解释为函数表达式,这意味着由@产生并返回一个值987654338@.


现在关于箭头函数:这里没有歧义。箭头函数定义始终是表达式,即评估它们总是产生一个值。

eval(`() => {}`) // a function object

总结一下

虽然箭头函数和函数声明/表达式之间存在差异,但这种差异并不是您在eval 中看到的结果的原因。这里的区别是因为evaling 一个语句/声明和一个表达式。

【讨论】:

  • 谢谢,它真的很有帮助,不过我确实有一个问题,为什么eval('function (){}') 会产生Syntax error,因为它是一个函数表达式,就像箭头函数一样。
  • 它不是函数表达式,它仍然被认为是函数声明,但函数声明需要名称,因此会出错。
【解决方案2】:

在您的示例中,a 被声明为它是一个命名函数。在第二种情况下,您只需编写一个表达式,它是一个 lambda 函数。请参阅herehere

如果您想为fn 获得相同的效果,请执行此操作

`console.log(eval("var a = function(){}"))`; //`function(){}`.

【讨论】:

    【解决方案3】:

    首先,EcmaScript 是 JavaScript 的“官方”名称。现在 ES2015 已经完成,对于大多数人来说,它实际上只是成为 JavaScript v6。因此,这种差异并非来自不同的引擎。

    不同行为的根源来自于 eval 函数中写入的字符串的结果。第一个 eval 字符串的结果是一个函数定义,没有任何东西要返回。另一方面,第二个 eval 正在评估一个 lambda 函数,所以 eval 的结果就是那个函数。为了清除这个概念,你可以重写代码如下:

    var fn = "function a(){ return 1;}";
    var es6fn = "()=>{}";
    
    console.log(eval(fn)); // undefined
    console.log(eval(es6fn)); // ()=>{}
    console.log(typeof eval(es6fn)); // ()=>{} i.e. a function
    console.log(a()); // call the a() function

    如您所见,a() 被定义为一个函数,您可以在第一个 eval 之后使用该函数。因此,第一个 eval 运行并全部返回 eval 函数的返回值。

    【讨论】:

      【解决方案4】:

      文档没有说明函数执行。这些函数不会被执行,除非它们像(() => {})()这样显式执行。

      报价

      eval作为字符串定义函数需要“(”和“)”作为前缀和后缀

      指将字符串解释为函数表达式而不是函数声明。

      // will throw SyntaxError
      // because function declaration requires a name
      typeof eval('function (){}'); 
      
      typeof eval('function a(){}') === 'undefined';
      typeof eval('(function a(){})') === 'function';
      typeof eval('null, function a(){}') === 'function';
      typeof eval('()=>{}') === 'function';
      

      箭头函数总是一个表达式,它不需要像逗号或括号这样的辅助结构来解释是一个表达式,这里是不同的。

      【讨论】:

      • 你能解释一下这是如何eval('null, function a(){}')返回一个函数的。我不知道这如何被视为一个函数,他们没有提到这个
      • 逗号运算符是一种强制将其解释为表达式的已知方法。见developer.mozilla.org/en/docs/Web/JavaScript/Reference/…null, function (){} 解释左手表达式(它可以是任何东西,为简洁起见,null 或 0 或 1)并返回右手表达式,即一个函数。这是这个 hack 的一个流行用法,stackoverflow.com/questions/9107240/…
      猜你喜欢
      • 1970-01-01
      • 2017-04-01
      • 1970-01-01
      • 2020-02-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-10
      相关资源
      最近更新 更多