本文翻译自:Array state will be cached in iOS 12 Safari. Is it a bug or feature?
Update at 2018.10.31 更新于2018.10.31
This bug has been fixed in iOS 12.1, have a good day~ 这个bug已经在iOS 12.1中得到修复,祝你有个美好的一天〜
I found a problem with Array's value state in the newly released iOS 12 Safari, for example, code like this: 我在新发布的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>
After refreshing the page, the array's value is still reversed. 刷新页面后,数组的值仍然相反。 Is this a bug or a feature of new Safari? 这是新Safari的错误还是功能?
Here is a demo page. 这是一个演示页面。 Try to use it with iOS 12 Safari: https://abelyao.github.io/others/ios12-safari-bug.html 尝试在iOS 12 Safari中使用它: https : //abelyao.github.io/others/ios12-safari-bug.html
#1楼
参考:https://stackoom.com/question/3Xp7w/数组状态将在iOS-Safari中缓存-这是一个错误或功能吗
#2楼
I wrote a lib to fix the bug. 我写了一个lib来修复bug。 https://www.npmjs.com/package/array-reverse-polyfill https://www.npmjs.com/package/array-reverse-polyfill
This is the code : 这是代码 :
(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); } })();
#3楼
It's definitely a BUG! 这肯定是一个BUG! And it's a very serious bug. 这是一个非常严重的错误。
The bug is due to the optimization of array initializers in which all values are primitive literals. 该错误是由于数组初始值设定项的优化,其中所有值都是原始文字。 For example, given the function: 例如,给定功能:
function buildArray() {
return [1, null, 'x'];
}
All returned array references from calls to buildArray()
will link to the same memory, and some methods such as toString()
will have their results cached. 从对buildArray()
调用返回的所有数组都将链接到同一个内存,而某些方法(如toString()
将缓存其结果。 Normally, to preserve consistency, any mutable operation on such optimized arrays will copy the data to a separate memory space and link to it; 通常,为了保持一致性,对这种优化数组的任何可变操作都会将数据复制到一个单独的存储空间并链接到它; this pattern is called copy-on-write , or CoW for short. 这种模式称为写时复制 ,或简称为CoW。
The reverse()
method mutates the array, so it should trigger a copy-on-write. reverse()
方法改变了数组,因此它应该触发copy-on-write。 But it doesn't, because the original implementor (Keith Miller of Apple) missed the reverse()
case, even though he had written many testcases. 但它没有,因为原来的实现者(Apple的Keith Miller)错过了reverse()
案例,尽管他写了很多测试用例。
This bug was reported to Apple on August 21. The fix landed in the WebKit repository on August 27 and shipped in Safari 12.0.1 and iOS 12.1 on October 30, 2018. 该错误于8月21日向Apple报告 。该修复程序于8月27日登陆WebKit存储库 ,并于2018年10月30日在Safari 12.0.1和iOS 12.1中发布。
#4楼
It seems not to be cached if the number of elements changes. 如果元素的数量发生变化,似乎不会被缓存。
I was able to avoid this like 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>
#5楼
This is a bug in webkit . 这是webkit中的一个错误。 Though this has been solved at their end but not yet shipped with iOS GM release. 虽然这已经在他们的终端解决了但尚未随iOS GM发布一起发布。 One of the solutions to this problem: 解决这个问题的方法之一:
(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;
}
}
})();