]> source.dussan.org Git - jquery.git/commitdiff
Core: Use Array.prototype.flat where supported
authorAhmed.S.ElAfifi <ahmed.s.elafifi@gmail.com>
Mon, 19 Aug 2019 08:04:01 +0000 (10:04 +0200)
committerMichał Gołębiowski-Owczarek <m.goleb@gmail.com>
Wed, 25 Sep 2019 15:49:32 +0000 (17:49 +0200)
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
src/manipulation.js
src/var/concat.js [deleted file]
src/var/flat.js [new file with mode: 0644]
test/unit/core.js

index dde341cc8c2a0d5f76982506fc460aa4b4b6c40f..0cf986a3c56959c163e80245950db4807fdbd7c0 100644 (file)
@@ -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
index ab19d8b3cd7de9caf188cebbb86c0439ad681019..1026eaf54fed0a409683b5857d99c30260fbe0da 100644 (file)
@@ -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 (file)
index e47c19d..0000000
+++ /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 (file)
index 0000000..86f91a6
--- /dev/null
@@ -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 );
+};
+
+} );
index b8e556b85b0f9d9919d79e25d81a080d09e4b2ec..c3deef1f71c74aa4c2009617f9476fdf0a14fe03 100644 (file)
@@ -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 ) {