Состояние массива будет кэшироваться в iOS 12 Safari.Это ошибка или особенность? - PullRequest
0 голосов
/ 18 сентября 2018

Обновление в 2018.10.31

Эта ошибка была исправлена ​​в iOS 12.1, хорошего дня ~

Я обнаружил проблему с состоянием значения массива во недавно выпущенном iOS 12 Safari, например, такой код:

<!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

Ответы [ 4 ]

0 голосов
/ 20 сентября 2018

Это ошибка в webkit .Хотя это было решено в их конце, но еще не выпущено с выпуском 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;
    }
  }
})();
0 голосов
/ 18 сентября 2018

Это определенно ошибка!И это очень серьезная ошибка.

Ошибка связана с оптимизацией инициализаторов массива, в которых все значения являются примитивными литералами.Например, учитывая функцию:

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

Все возвращенные ссылки на массивы из вызовов к buildArray() будут ссылаться на одну и ту же память, а некоторые методы, такие как toString(), будут кэшировать свои результаты.Обычно для сохранения согласованности любая изменяемая операция с такими оптимизированными массивами копирует данные в отдельное пространство памяти и ссылается на него;этот шаблон называется копирование при записи , или сокращенно CoW.

Метод reverse() изменяет массив, поэтому он должен инициировать копирование при записи.Но это не так, потому что оригинальный разработчик (Кит Миллер из Apple) пропустил случай reverse(), хотя он написал много тестов.

Эта ошибка была сообщена Apple в21 августа. Исправление появилось в хранилище WebKit 27 августа и было выпущено в Safari 12.0.1 и iOS 12.1 30 октября 2018 года.

0 голосов
/ 20 сентября 2018

Похоже, что не кэшируется, если количество элементов меняется.
Мне удалось избежать этого, как это.

<!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>
0 голосов
/ 18 сентября 2018

Я написал библиотеку, чтобы исправить ошибку.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);
  }
})();
...