带有块作用域的 Javascript 默认参数仅在 iOS 上失败

Leo*_*ang 9 javascript scope ios ecmascript-6

try {
  const val = 'correct value';
  (() => {
    ((arg = val) => {
      const val = 'ignored value';
      alert(arg);
    })();
  })();
} catch (err) {
  alert(err.message || 'Unknown error');
}
Run Code Online (Sandbox Code Playgroud)

在 OS X Chrome、OS X Safari、Android Chrome、Windows Chrome、Windows Firefox甚至Windows Edge 上,它会提醒“正确值”。在 iOS Safari 和 iOS Chrome 上,它会警告“找不到变量:val”。

以下代码段均适用于 iOS:

不使用默认参数(代码段 2):

try {
  const val = 'correct value';
  (() => {
    alert(val);
    (() => {
      const val = 'wrong value';
    })();
  })();
} catch (err) {
  alert(err.message || 'Unknown error');
}
Run Code Online (Sandbox Code Playgroud)

无嵌套函数(代码段 3):

try {
  const val = 'correct value';
  ((arg = val) => {
    const val = 'ignored value';
    alert(val || 'wrong value');
  })();
} catch (err) {
  alert(err.message || 'Unknown error');
}
Run Code Online (Sandbox Code Playgroud)

不覆盖变量(代码段 4):

try {
  const val = 'correct value';
  (() => {
    ((arg = val) => {
      alert(arg);
    })();
  })();
} catch (err) {
  alert(err.message || 'Unknown error');
}
Run Code Online (Sandbox Code Playgroud)

块作用域而不是函数(代码段 5):

try {
  const val = 'correct value';
  {
    ((arg = val) => {
      const val = 'ignored value';
      alert(arg);
    })();
  }
} catch (err) {
  alert(err.message || 'Unknown error');
}
Run Code Online (Sandbox Code Playgroud)

基于代码段 3,很明显valinarg = val应该来自父作用域,而不是内部函数的作用域。

在第一个片段中,浏览器val在当前作用域中找不到,而是使用子作用域而不是检查祖先作用域,这导致了时间死区。

这是 iOS 错误还是我误解了正确的 JS 行为?

这个 bug 发生在我们的 Webpack+Babel+Terser 输出中,所以我们不能只是重写代码来避免这个 bug。

Joe*_*ook 3

我认为这是参数默认值及其 TDZ的错误实现带来的不良后果。我怀疑 iOS Safari 认为您正在尝试分配给尚未初始化的内容。

仅供参考——错误位置:

在此输入图像描述


解决方法 1 不要初始化与默认参数和外部作用域同名的内部作用域常量

try {
    const val = 'correct value';
    (() => {
        ((arg = val) => {
            const val_ = 'ignored value';       // <----
            alert(arg);
        })();
    })();
} catch (err) {
    console.error(err);
    console.error('msg', err.message || 'Unknown error');
}
Run Code Online (Sandbox Code Playgroud)

解决方法2

强制:constlet

try {
    let val = 'correct value';                 // <----
    (() => {
        ((arg = val) => {
            const val = 'ignored value';
            alert(arg);
        })();
    })();
} catch (err) {
    console.error(err);
    console.error('msg', err.message || 'Unknown error');
}
Run Code Online (Sandbox Code Playgroud)

解决方法 3根本 不要const val在最里面的闭包中重新初始化:

try {
    const val = 'correct value';
    (() => {
        ((arg = val) => {
            // const val = 'ignored value';      // <--
            alert(arg);
        })();
    })();
} catch (err) {
    console.error(err);
    console.error('msg', err.message || 'Unknown error');
}
Run Code Online (Sandbox Code Playgroud)