├── .npmignore
├── .gitignore
├── index.js
├── tests
├── index.html
└── tests.js
├── package.json
├── origin.html
├── LICENSE
├── fixed.html
└── README.md
/.npmignore:
--------------------------------------------------------------------------------
1 | tests/*
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 |
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | function buggy() {
3 | var a = [1, 2];
4 | return String(a) === String(a.reverse());
5 | }
6 | if(!buggy()) return;
7 | var r = Array.prototype.reverse;
8 | Array.prototype.reverse = function reverse() {
9 | if (Array.isArray(this)) this.length = this.length;
10 | return r.call(this);
11 | }
12 | })();
13 |
--------------------------------------------------------------------------------
/tests/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Tests
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "array-reverse-polyfill",
3 | "version": "1.0.9",
4 | "description": "Fix IOS 12 Array Reverse Bug.",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/fanmingfei/array-reverse-ios12.git"
12 | },
13 | "keywords": [
14 | "ios12",
15 | "array",
16 | "reverse",
17 | "bug"
18 | ],
19 | "author": "fanmingfei",
20 | "license": "MIT",
21 | "bugs": {
22 | "url": "https://github.com/fanmingfei/array-reverse-ios12/issues"
23 | },
24 | "homepage": "https://github.com/fanmingfei/array-reverse-ios12#readme"
25 | }
26 |
--------------------------------------------------------------------------------
/origin.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Origin
7 |
8 |
9 |
10 | Use iOS12 test.
11 | Click Test
12 |
19 |
20 |
21 |
22 | Use iOS12 test. <br/>
23 | <button onclick="test()" style="border: 1px solid #ccc;">Click Refresh</button>
24 | <script>
25 | function test() {
26 | var arr = [1, 2, 3, 4]
27 | document.body.innerHTML += '<br/>'+arr.join(',')
28 | arr.reverse()
29 | }
30 | </script>
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 明非
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/fixed.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Fixed
8 |
9 |
10 |
11 |
12 | Use iOS12 test.
13 | Click Test
14 |
21 |
22 |
23 |
24 | Use iOS12 test. <br/>
25 | <button onclick="test()" style="border: 1px solid #ccc;">Click Refresh</button>
26 | <script>
27 | function test() {
28 | var arr = [1, 2, 3, 4]
29 | document.body.innerHTML += '<br/>'+arr.join(',')
30 | arr.reverse()
31 | }
32 | </script>
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/tests/tests.js:
--------------------------------------------------------------------------------
1 | // reference to the V8's test cases
2 | // also see https://github.com/v8/v8/blob/master/test/mjsunit/array-reverse.js
3 |
4 | QUnit.test('test primitive literal', function(assert) {
5 | assert.deepEqual([], [].reverse(), 'empty array');
6 | assert.deepEqual([8, 6, 4, 2], [2, 4, 6, 8].reverse(), 'array of int');
7 | assert.deepEqual(
8 | [0.8, 0.6, 0.4],
9 | [0.4, 0.6, 0.8].reverse(),
10 | 'array of double'
11 | );
12 | assert.deepEqual(
13 | ['str4', 'str3', 'str2'],
14 | ['str2', 'str3', 'str4'].reverse(),
15 | 'array of string'
16 | );
17 | });
18 |
19 | QUnit.test('test with undefined', function(assert) {
20 | assert.deepEqual(
21 | [undefined, undefined, null, 1],
22 | [1, null, undefined, undefined].reverse()
23 | );
24 | });
25 |
26 | QUnit.test('test with array of Objects', function(assert) {
27 | assert.deepEqual(
28 | new Array(new Array(1), new Array(2), new Array()),
29 | new Array(new Array(), new Array(2), new Array(1)).reverse()
30 | );
31 | assert.deepEqual(
32 | new Array(new Object(), new Object(), new Object()),
33 | new Array(new Object(), new Object(), new Object()).reverse()
34 | );
35 | assert.deepEqual(
36 | new Array(new Object(1), new Object(0), new Object()),
37 | new Array(new Object(), new Object(0), new Object(1)).reverse()
38 | );
39 | assert.deepEqual(
40 | new Array(new Object('1'), new Object(0), Object.create(null)),
41 | new Array(Object.create(null), new Object(0), new Object('1')).reverse()
42 | );
43 | assert.deepEqual(
44 | new Array(new Number(1), new Number(2), new Number(3)),
45 | new Array(new Number(3), new Number(2), new Number(1)).reverse()
46 | );
47 | assert.deepEqual(
48 | new Array(new Boolean(false), new Boolean(true)),
49 | new Array(new Boolean(true), new Boolean(false)).reverse()
50 | );
51 | });
52 |
53 | QUnit.test('test with Object', function(assert) {
54 | let obj = { length: 5 };
55 | obj[0] = 'foo';
56 | obj[3] = 'bar';
57 | Array.prototype.reverse.call(obj);
58 |
59 | assert.deepEqual({ '1': 'bar', '4': 'foo', length: 5 }, obj);
60 | });
61 |
62 | QUnit.test('test with prototype chain', function(assert) {
63 | let proto = { 0: 'foo', 19: 'bar' };
64 | let obj = { length: 20, 5: 'foobar', __proto__: proto };
65 | Array.prototype.reverse.call(obj);
66 |
67 | assert.equal('bar', obj[0]);
68 | assert.equal('foobar', obj[14]);
69 | assert.equal('foo', obj[19]);
70 | });
71 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # array-reverse-polyfill
4 | Fixed Array `reverse()` Bug of Safari 12.0 on iOS 12 and MacOS.
5 |
6 | ## About the Bug
7 |
8 | https://bugs.webkit.org/show_bug.cgi?id=188794
9 |
10 | https://stackoverflow.com/q/52390368/7606327
11 |
12 | https://www.v2ex.com/t/490590 (in Chinese)
13 |
14 | https://zhuanlan.zhihu.com/p/44869607 (in Chinese)
15 |
16 | A simple visualization of this bug:
17 |
18 | 
19 |
20 | (Thank @yisibl for his magical realism style drawing / 感谢灵魂画手一丝姐姐)
21 |
22 | ## Usage
23 |
24 | ### CDN
25 |
26 | jsDelivr
27 | ```html
28 |
29 | ```
30 |
31 | unpkg
32 | ```html
33 |
34 | ```
35 |
36 | Alibaba
37 | ```html
38 |
39 | ```
40 |
41 | ### npm package
42 | [array-reverse-polyfill](https://www.npmjs.com/package/array-reverse-polyfill)
43 |
44 | ### Download releases
45 | [fanmingfei/array-reverse-ios12](https://github.com/fanmingfei/array-reverse-ios12/releases)
46 |
47 |
48 | ## UA sniffing
49 |
50 | This polyfill use *[feature (bug 🙃) detection](https://en.wikipedia.org/wiki/Feature_detection_(web_development))* instead of *[UA sniffing](https://en.wikipedia.org/wiki/User_agent#User_agent_sniffing)*.
51 |
52 | Some may want to load the polyfill in client-side or add the polyfill script in server-side only for Safari 12.0, and save the request to the polyfill for all other browsers. We assume the next version of iOS/Safari (12.1) will fix the bug, so we suggest you search "Version/12.0" and "Safari/" substring in user-agent string. You could also use regexp `/Version\/12\.0.*Safari\//`.
53 |
54 | ### Client on-demand load
55 | ```html
56 |
57 |
62 | ...
63 | ```
64 | **NOTE**: The polyfill should be loaded and executed before all other scripts. So be careful about the `async` `defer` or `type=module`attributes and make sure your `loadScript()` implementation will keep the order.
65 |
66 | ### Server-side adaption (use PHP as sample)
67 | ```php
68 |
69 | if (preg_match('#Version/12\.0.*Safari/#', $_SERVER['HTTP_USER_AGENT'])): ?>
70 |
71 | endif; ?>
72 | ...
73 | ```
74 |
75 | ## Test
76 |
77 | Test page: [https://fanmingfei.github.io/array-reverse-ios12/origin.html](https://fanmingfei.github.io/array-reverse-ios12/origin.html)
78 |
79 |
80 | Test page with this polyfill: [https://fanmingfei.github.io/array-reverse-ios12/fixed.html](https://fanmingfei.github.io/array-reverse-ios12/fixed.html)
81 |
82 | Clicking the button to test.
83 |
84 |
85 | For Unit Tests: [https://fanmingfei.github.io/array-reverse-ios12/tests/](https://fanmingfei.github.io/array-reverse-ios12/tests/)
86 |
87 |
88 | --------------------------------
89 |
90 | ## Hope you guys luck
91 |
92 |
--------------------------------------------------------------------------------