From 2f666c1daba43d26d77d9500db09d528bf66fac8 Mon Sep 17 00:00:00 2001 From: "Ahmed.S.ElAfifi" Date: Mon, 19 Aug 2019 10:04:01 +0200 Subject: [PATCH] Core: Use Array.prototype.flat where supported Calling `Array.prototype.concat.apply( [], inputArray )` to flatten `inputArray` crashes for large arrays; using `Array.prototype.flat` avoids these issues in browsers that support it. In case it's necessary to support these large arrays even in older browsers, a polyfill for `Array.prototype.flat` can be loaded. This is already being done by many applications. (cherry picked from 9df4f1de12728b44a4b0f91748f12421008d9079) Fixes gh-4320 Closes gh-4459 --- src/core.js | 7 +++---- src/manipulation.js | 6 +++--- src/var/concat.js | 7 ------- src/var/flat.js | 16 ++++++++++++++++ test/unit/core.js | 20 +++++++++++++++++++- 5 files changed, 41 insertions(+), 15 deletions(-) delete mode 100644 src/var/concat.js create mode 100644 src/var/flat.js diff --git a/src/core.js b/src/core.js index dde341cc8..0cf986a3c 100644 --- a/src/core.js +++ b/src/core.js @@ -4,10 +4,9 @@ define( [ "./var/arr", - "./var/document", "./var/getProto", "./var/slice", - "./var/concat", + "./var/flat", "./var/push", "./var/indexOf", "./var/class2type", @@ -20,7 +19,7 @@ define( [ "./var/isWindow", "./core/DOMEval", "./core/toType" -], function( arr, document, getProto, slice, concat, push, indexOf, +], function( arr, getProto, slice, flat, push, indexOf, class2type, toString, hasOwn, fnToString, ObjectFunctionString, support, isFunction, isWindow, DOMEval, toType ) { @@ -369,7 +368,7 @@ jQuery.extend( { } // Flatten any nested arrays - return concat.apply( [], ret ); + return flat( ret ); }, // A global GUID counter for objects diff --git a/src/manipulation.js b/src/manipulation.js index ab19d8b3c..1026eaf54 100644 --- a/src/manipulation.js +++ b/src/manipulation.js @@ -1,7 +1,7 @@ define( [ "./core", "./core/isAttached", - "./var/concat", + "./var/flat", "./var/isFunction", "./var/push", "./var/rcheckableType", @@ -24,7 +24,7 @@ define( [ "./traversing", "./selector", "./event" -], function( jQuery, isAttached, concat, isFunction, push, rcheckableType, +], function( jQuery, isAttached, flat, isFunction, push, rcheckableType, access, rtagName, rscriptType, wrapMap, getAll, setGlobalEval, buildFragment, support, dataPriv, dataUser, acceptData, DOMEval, nodeName ) { @@ -126,7 +126,7 @@ function fixInput( src, dest ) { function domManip( collection, args, callback, ignored ) { // Flatten any nested arrays - args = concat.apply( [], args ); + args = flat( args ); var fragment, first, scripts, hasScripts, node, doc, i = 0, diff --git a/src/var/concat.js b/src/var/concat.js deleted file mode 100644 index e47c19d75..000000000 --- a/src/var/concat.js +++ /dev/null @@ -1,7 +0,0 @@ -define( [ - "./arr" -], function( arr ) { - "use strict"; - - return arr.concat; -} ); diff --git a/src/var/flat.js b/src/var/flat.js new file mode 100644 index 000000000..86f91a673 --- /dev/null +++ b/src/var/flat.js @@ -0,0 +1,16 @@ +define( [ + "./arr" +], function( arr ) { + +"use strict"; + +// Support: IE 9 - 11+, Edge 18+, Android Browser 4.0 - 4.3 only, iOS 7 - 11 only, Safari 11 only, +// Firefox <= 61 only +// Provide fallback for browsers without Array#flat. +return arr.flat ? function( array ) { + return arr.flat.call( array ); +} : function( array ) { + return arr.concat.apply( [], array ); +}; + +} ); diff --git a/test/unit/core.js b/test/unit/core.js index b8e556b85..c3deef1f7 100644 --- a/test/unit/core.js +++ b/test/unit/core.js @@ -703,7 +703,7 @@ QUnit.test( "map()", function( assert ) { } ); QUnit.test( "jQuery.map", function( assert ) { - assert.expect( 25 ); + assert.expect( 28 ); var i, label, result, callback; @@ -803,6 +803,24 @@ QUnit.test( "jQuery.map", function( assert ) { return k % 2 ? k : [ k, k, k ]; } ); assert.equal( result.join( "" ), "00012223", "Array results flattened (#2616)" ); + + result = jQuery.map( [ [ [ 1, 2 ], 3 ], 4 ], function( v, k ) { + return v; + } ); + assert.equal( result.length, 3, "Array flatten only one level down" ); + assert.ok( Array.isArray( result[ 0 ] ), "Array flatten only one level down" ); + + // Support: IE 9 - 11+, Edge 18+, Android Browser 4.0 - 4.3 only, iOS 7 - 11 only, + // Safari 11 only, Firefox <= 61 only + // Skip the test in browsers without Array#flat. + if ( Array.prototype.flat ) { + result = jQuery.map( Array( 300000 ), function( v, k ) { + return k; + } ); + assert.equal( result.length, 300000, "Able to map 300000 records without any problems (#4320)" ); + } else { + assert.ok( "skip", "Array#flat doesn't supported on all browsers" ); + } } ); QUnit.test( "jQuery.merge()", function( assert ) { -- 2.39.5