首页 > 解决方案 > 使用 switch case,case 内部的 break 结束了外部 for...of 循环迭代

问题描述

我定义了以下函数并在其上运行DOMContentLoaded(其中不相关):

function dependentControls() {
  const dependers = [...document.querySelectorAll('[data-depends-on]')]
  for (const depender of dependers) {
    let dependency = document.getElementById(depender.dataset.dependsOn)
    if (dependency) {
      let dependencyDetails = {
        prop: null,
        state: null
      }
      switch (dependency.type) {
        case 'checkbox':
          {
            dependencyDetails.prop = 'checked'
            dependencyDetails.state = false
            break // this break exits the current for loop iteration
          }
        case 'text':
          {
            dependencyDetails.prop = 'value'
            dependencyDetails.state = ''
            break // this break exits the current for loop iteration
          }
        default:
          console.log('default case')
      }
      console.log("switch...end") // this line is never reached
      depender.disabled = !dependency[dependencyDetails.prop]
      dependency.addEventListener('change', () => {
        console.log('dependancy changed')
        depender.disabled = dependency[dependencyDetails.prop] === dependencyDetails.state
      })
    }
  }
  console.log('for...end')
}

document.addEventListener('DOMContentLoaded', dependentControls);
<div>
  <input type="checkbox" id="fooBar" />
  <label for="fooBar">dependency</label>
</div>
<hr />
<div>
  <label for="fooBaz">depender</label>
  <input type="text" id="fooBaz" data-depends-on="fooBar" value="depender" />
</div>

这是转译代码的相关部分:

function _toConsumableArray(arr) {
  if (Array.isArray(arr)) {
    for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) {
      arr2[i] = arr[i];
    }
    return arr2;
  } else {
    return Array.from(arr);
  }
}

var dependentControls = function dependentControls() {
  var dependers = _toConsumableArray(document.querySelectorAll('[data-depends-on]'));
  
  var _iteratorNormalCompletion5 = true;
  var _didIteratorError5 = false;
  var _iteratorError5 = undefined;

  try {
    var _loop5 = function _loop5() {
      var depender = _step5.value;

      var dependency = document.getElementById(depender.dataset.dependsOn);

      if (dependency) {
        var dependencyDetails = {
          prop: 'checked',
          state: false
        };

        switch (dependency.type) {
          case 'checkbox':
            {
              dependencyDetails.prop = 'checked';
              dependencyDetails.state = false;
              return "break";
            }

          case 'text':
            {
              dependencyDetails.prop = 'value';
              dependencyDetails.state = '';
              return "break";
            }

          default:
        }

        console.log("switch...end"); // this line is never reached

        depender.disabled = !dependency[dependencyDetails.prop];
        dependency.addEventListener('change', function() {
          depender.disabled = dependency[dependencyDetails.prop] === dependencyDetails.state;
        });
      }
    };

    _loop4: for (var _iterator5 = dependers[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
      var _ret2 = _loop5();

      switch (_ret2) {
        case "break":
          break _loop4;

        default:
          if (_typeof(_ret2) === "object") return _ret2.v;
      }
    }
  } catch (err) {
    _didIteratorError5 = true;
    _iteratorError5 = err;
  } finally {
    try {
      if (!_iteratorNormalCompletion5 && _iterator5.return != null) {
        _iterator5.return();
      }
    } finally {
      if (_didIteratorError5) {
        throw _iteratorError5;
      }
    }
  }

  console.log("for...end");
}

document.addEventListener('DOMContentLoaded', dependentControls);
<div>
  <input type="checkbox" id="fooBar" />
  <label for="fooBar">dependency</label>
</div>
<hr />
<div>
  <label for="fooBaz">depender</label>
  <input type="text" id="fooBaz" data-depends-on="fooBar" value="depender" />
</div>

这是我的.babelrc

{
  "presets": [
    [ "@babel/preset-env", {
      "targets": {
        "browsers": [ "last 2 versions", "ie >= 11" ]
      }
    }]
  ]
}

我正在使用 Babel 7 来编译代码并使用最新的 webpack 来捆绑它。当我将此代码粘贴到控制台并调用该函数时,问题不会发生。当我将此代码粘贴到http://babeljs.io/repl并将生成的代码粘贴到控制台时,它也可以工作。

从表面上看,这已经在 4 月份被报道了,到目前为止还没有人评论这个问题:

https://github.com/babel/babel/issues/7765

尽管如此,我也提交了它:

https://github.com/babel/babel/issues/8709

标签: javascriptbabeljs

解决方案


事实证明,这确实是 Babel 7 中引入的一个错误。

它在 Babel REPL 中无法重现的原因是官方的 REPL 仍在 Babel 6.2.6 上。7.0 REPL 目前可在此处获得:

https://babeljs.io/repl/build/master

Babel 7 转换breakreturn "break";如果break发生在case用花括号括起来的块中:

这与 Babel 7 的预期一致:

switch (a) {
  case 1:
    /** some code **/
    break
  default:
}

这就产生了问题:

switch (a) {
  case 1: { // <-- wrapping the case ...
    /** some code **/
    break
  } // <-- ... makes Babel go wrong
  default: {}
}

将箱子包裹起来 {}会使 Babel 转换breakreturn "break";- 这显然是 Babel 方面的一个错误。

临时解决方案:

如果不需要的话,暂时不要将case指令包装在新{}的块上下文中(我的原始代码中的新块上下文只是为了可读性,所以无论如何都没有必要)。将在 Babel 修复该问题后更新答案。


推荐阅读