【问题标题】:Loss of scope in setTimeout with a string带有字符串的 setTimeout 范围丢失
【发布时间】:2020-06-03 10:54:43
【问题描述】:

为什么会报错?

!function() {
    var a = 3;
    setTimeout('console.log(a)');
}()

如果使用函数而不是字符串,它会起作用。

它也适用于 eval:

!function() {
    var a = 3;
    eval('console.log(a)');
}()

这个问题是关于理论的。无需修复。

问题在于范围。我不明白为什么范围会丢失。更好 - 来自解释它的来源(例如 Mozilla 网站)的一些句子。

【问题讨论】:

  • 因为不能执行字符串?
  • 可以,但由于某种原因不在本地范围内。
  • @Argee 字符串是eval-ed
  • 好吧,如果它是eval-ed,那么也许这会有所帮助:stackoverflow.com/questions/9781285/… ... 显然 eval 通常使用全局范围,但您在不同的范围内执行它

标签: javascript


【解决方案1】:

因为您可以将setTimeout 函数想象成类似的东西

function setTimeout(callbackOrString, time) {
    if( typeof callbackOrString === 'string' ) {
        callbackOrString = eval(callbackOrString)
    }
    // pass callbackOrString to event queue 
}

字符串的评估发生在setTimeout 函数的内部,而不是在您将其传递给函数时。因此,在评估字符串时,该范围内没有 a

eval不是常规函数而是对引擎的更多指令,其行为在这里定义ECMAScript: 18.2.1 eval ( x )

setTimeout 不是语言规范的一部分,而是 API 的一部分,只是一个“常规”函数 Living Standard: timer initialization steps(参见 1. 和 3.):

1. Let method context proxy be method context if that is a WorkerGlobalScope object, or else the WindowProxy that corresponds to method context.  
…  
7. Let task be a task that runs the following substeps:
   …
   2. Run the appropriate set of steps from the following list:
      - If the first method argument is a Function
        …
      - Otherwise
        …
        3. Let settings object be method context's environment settings object.

这就像您编写自己的函数一样,这会导致与 setTimeout 相同的错误,原因完全相同:

function myFunc(code) {
  if (typeof code === 'string') {
    eval(code)
  } else if (typeof code === 'function') {
    code();
  }
}


!function() {
  var a = 3;

  // arrow function is passed to myFunc, closure is create so "a" is available
  myFunc(() => console.log(a)); 
  
  // function is passed to myFunc, closure is create so "a" is available
  myFunc(function() {
    console.log(a)
  });

  // the string "() => console.log(a)" is evaluated in the scope of "a" and the resulting arrow function is passed to myFunc, closure is create so "a" is available
  myFunc(eval('() => console.log(a)'));

  try {
    // does not work because only the string is passed to myFunc which is evaluated within myFunc
    myFunc('console.log(a)');
  } catch (err) {
    console.error(err);
  }
}()

【讨论】:

  • 但它适用于 eval。查看问题更新。例如,我需要在 Mozilla 网站上对此进行解释。
  • @oleedd 但eval 不在同一范围内调用。由于没有捕获值a 的机制,因此无法“转移”该绑定。
  • 之所以调用它是因为它在我的第二个示例中有效。
  • @oleedd 它在你的第二个示例中起作用,因为 eval 在同一范围内调用! setTimeout 不在同一范围内运行,它采用字符串evals 稍后
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-12-20
  • 2019-12-30
  • 1970-01-01
  • 2014-10-15
  • 1970-01-01
  • 2017-04-04
  • 1970-01-01
相关资源
最近更新 更多