[javascript] 배열 상태는 iOS 12 Safari에서 캐시됩니다. 버그입니까 아니면 기능입니까?

2018.10.31에 업데이트

이 버그는 iOS 12.1에서 수정되었습니다. 좋은 하루 되세요 ~

새로 출시 된 iOS 12 Safari에서 Array의 값 상태에 문제가 있음을 발견했습니다 (예 : 다음과 같은 코드).

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
    <title>iOS 12 Safari bugs</title>
    <script type="text/javascript">
    window.addEventListener("load", function ()
    {
        let arr = [1, 2, 3, 4, 5];
        alert(arr.join());

        document.querySelector("button").addEventListener("click", function ()
        {
            arr.reverse();
        });
    });
    </script>
</head>
<body>
    <button>Array.reverse()</button>
    <p style="color:red;">test: click button and refresh page, code:</p>
</body>
</html>

페이지를 새로 고친 후에도 여전히 배열 값이 반대로 바뀝니다. 이것은 새로운 Safari의 버그입니까?


여기 데모 페이지가 있습니다. iOS 12 Safari에서 사용하십시오 :
https://abelyao.github.io/others/ios12-safari-bug.html



답변

확실히 버그입니다! 그리고 그것은 매우 심각한 버그입니다.

버그는 모든 값이 기본 리터럴 인 배열 이니셜 라이저의 최적화 때문입니다. 예를 들어 다음과 같은 기능이 주어집니다.

function buildArray() {
    return [1, null, 'x'];
}

호출에서 반환 된 모든 배열 참조 buildArray()는 동일한 메모리에 연결되며 toString()결과 와 같은 일부 메서드 는 캐시됩니다. 일반적으로 일관성을 유지하기 위해 최적화 된 어레이에서 변경 가능한 조작은 데이터를 별도의 메모리 공간에 복사하여 링크합니다. 이 패턴이라고 기록 중 복사 짧게, 또는 소.

reverse()메소드는 배열을 변경하므로 기록 중 복사를 트리거해야합니다. 그러나 원래의 구현 자 (Apple의 Keith Miller)는 reverse()많은 테스트 사례를 작성했지만 사례를 놓 쳤기 때문에 그렇지 않습니다 .

이 버그는 8 월 21 일 Apple에보고되었습니다 .이 수정 프로그램 8 월 27 일 WebKit 저장소 에 제공되어 2018 년 10 월 30 일 Safari 12.0.1 및 iOS 12.1에 제공되었습니다.


답변

버그를 해결하기 위해 lib를 작성했습니다.
https://www.npmjs.com/package/array-reverse-polyfill

이것은 코드입니다 :

(function() {
  function buggy() {
    var a = [1, 2];
    return String(a) === String(a.reverse());
  }
  if(!buggy()) return;
  var r = Array.prototype.reverse;
  Array.prototype.reverse = function reverse() {
    if (Array.isArray(this)) this.length = this.length;
    return r.call(this);
  }
})();

a

답변

이것은 웹킷 의 버그입니다 . 이것은 최종적으로 해결되었지만 아직 iOS GM 릴리스와 함께 제공되지는 않았습니다. 이 문제에 대한 해결책 중 하나 :

(function() {
  function getReverseStr() {
    return [1, 2].reverse();
  }

  var n1 = getReverseStr()[0];
  var n2 = getReverseStr()[0];
  // check if there is an issue
  if(n1 != n2) {
    var origReverseFunction = Array.prototype.reverse;
    Array.prototype.reverse = function() {
      var newArr = this.slice();
      // use original reverse function so that edge cases are taken care of
      origReverseFunction.apply(newArr, arguments);
      var that = this;
      // copy reversed array
      newArr.forEach(function(value, index) {
        that[index] = value;
      });
      return this;
    }
  }
})();


답변

요소 수가 변경되면 캐시되지 않는 것 같습니다.
나는 이것을 이렇게 피할 수 있었다.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
    <title>iOS 12 Safari bugs</title>
    <script type="text/javascript">
    window.addEventListener("load", function ()
    {
        let arr = [1, 2, 3, 4, 5];
        arr.push('');
        arr.pop();
        alert(arr.join());

        document.querySelector("button").addEventListener("click", function ()
        {
            arr.reverse();
        });
    });
    </script>
</head>
<body>
    <button>Array.reverse()</button>
    <p style="color:red;">test: click button and refresh page, code:</p>
</body>
</html>


답변


댓글 달기

이메일 주소를 발행하지 않을 것입니다. 필수 항목은 *(으)로 표시합니다