🌐 AI搜索 & 代理 主页
Skip to content

Commit 81d37f7

Browse files
committed
Deprecated: improve .trim for strings containing lots of whitespaces
The old implementation took O(N^2) time to trim the string when multiple adjacent spaces were present. For instance, consider the string "A B" (10 spaces between A and B). Then old regexp /[\s]+$/ would take 10 steps until it realizes the regexp does not match at position 1. Then it would try to match the regexp at position 2, and it would take 9 steps. Then 8 steps for position 3, ... so it would take 10*10/2 steps until it figures out the regexp does not match. The new approach is to require "non-whitespace" char before the whitespace run, so it spends just one step for each space in the string.
1 parent 0f6c3d9 commit 81d37f7

File tree

3 files changed

+78
-19
lines changed

3 files changed

+78
-19
lines changed

src/deprecated.js

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,22 @@ define( [
66
"./var/isFunction",
77
"./var/isWindow",
88
"./var/slice",
9+
"./deprecated/support",
910

1011
"./deprecated/ajax-event-alias",
1112
"./deprecated/event"
12-
], function( jQuery, nodeName, camelCase, toType, isFunction, isWindow, slice ) {
13+
], function( jQuery, nodeName, camelCase, toType, isFunction, isWindow, slice, support ) {
1314

1415
"use strict";
1516

1617
// Support: Android <=4.0 only
1718
// Make sure we trim BOM and NBSP
18-
var rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;
19+
// Require that the "whitespace run" starts from a non-whitespace
20+
// to avoid O(N^2) behavior when the engine would try matching "\s$" at each space position.
21+
// It is important that non-whitespace char is mandatory for regexp performance reasons,
22+
// however, it does not break correctness since whitespace-only string will be trimmed by ltrim above.
23+
var ltrim = /^[\s\uFEFF\xA0]+/,
24+
rtrim = /([^\s\uFEFF\xA0])[\s\uFEFF\xA0]+$/;
1925

2026
// Bind a function to a context, optionally partially applying any
2127
// arguments.
@@ -80,8 +86,11 @@ jQuery.isNumeric = function( obj ) {
8086
};
8187

8288
jQuery.trim = function( text ) {
83-
return text == null ?
84-
"" :
85-
( text + "" ).replace( rtrim, "" );
89+
if ( text == null ) {
90+
return "";
91+
}
92+
return support.trim ?
93+
( text + "" ).trim() :
94+
( text + "" ).replace( ltrim, "" ).replace( rtrim, "$1" );
8695
};
8796
} );

src/deprecated/support.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
define( [
2+
"../var/support"
3+
], function( support ) {
4+
5+
"use strict";
6+
7+
support.trim = "".trim ? "_" === " \uFEFF\xA0_\uFEFF\xA0 ".trim() : false;
8+
9+
return support;
10+
11+
} );

test/unit/support.js

Lines changed: 53 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ testIframe(
7575
"radioValue": true,
7676
"reliableMarginLeft": true,
7777
"reliableTrDimensions": false,
78-
"scrollboxSize": true
78+
"scrollboxSize": true,
79+
"trim": true
7980
},
8081
ie_10_11: {
8182
"ajax": true,
@@ -94,7 +95,8 @@ testIframe(
9495
"radioValue": false,
9596
"reliableMarginLeft": true,
9697
"reliableTrDimensions": false,
97-
"scrollboxSize": true
98+
"scrollboxSize": true,
99+
"trim": true
98100
},
99101
ie_9: {
100102
"ajax": true,
@@ -113,7 +115,8 @@ testIframe(
113115
"radioValue": false,
114116
"reliableMarginLeft": true,
115117
"reliableTrDimensions": false,
116-
"scrollboxSize": false
118+
"scrollboxSize": false,
119+
"trim": false
117120
},
118121
chrome: {
119122
"ajax": true,
@@ -132,7 +135,8 @@ testIframe(
132135
"radioValue": true,
133136
"reliableMarginLeft": true,
134137
"reliableTrDimensions": true,
135-
"scrollboxSize": true
138+
"scrollboxSize": true,
139+
"trim": true
136140
},
137141
safari: {
138142
"ajax": true,
@@ -151,7 +155,8 @@ testIframe(
151155
"radioValue": true,
152156
"reliableMarginLeft": true,
153157
"reliableTrDimensions": true,
154-
"scrollboxSize": true
158+
"scrollboxSize": true,
159+
"trim": true
155160
},
156161
safari_9_10: {
157162
"ajax": true,
@@ -170,7 +175,8 @@ testIframe(
170175
"radioValue": true,
171176
"reliableMarginLeft": true,
172177
"reliableTrDimensions": true,
173-
"scrollboxSize": true
178+
"scrollboxSize": true,
179+
"trim": true
174180
},
175181
firefox: {
176182
"ajax": true,
@@ -189,7 +195,8 @@ testIframe(
189195
"radioValue": true,
190196
"reliableMarginLeft": true,
191197
"reliableTrDimensions": false,
192-
"scrollboxSize": true
198+
"scrollboxSize": true,
199+
"trim": true
193200
},
194201
firefox_60: {
195202
"ajax": true,
@@ -208,7 +215,8 @@ testIframe(
208215
"radioValue": true,
209216
"reliableMarginLeft": false,
210217
"reliableTrDimensions": true,
211-
"scrollboxSize": true
218+
"scrollboxSize": true,
219+
"trim": true
212220
},
213221
ios: {
214222
"ajax": true,
@@ -227,7 +235,8 @@ testIframe(
227235
"radioValue": true,
228236
"reliableMarginLeft": true,
229237
"reliableTrDimensions": true,
230-
"scrollboxSize": true
238+
"scrollboxSize": true,
239+
"trim": true
231240
},
232241
ios_9_10: {
233242
"ajax": true,
@@ -246,7 +255,8 @@ testIframe(
246255
"radioValue": true,
247256
"reliableMarginLeft": true,
248257
"reliableTrDimensions": true,
249-
"scrollboxSize": true
258+
"scrollboxSize": true,
259+
"trim": true
250260
},
251261
ios_8: {
252262
"ajax": true,
@@ -265,7 +275,8 @@ testIframe(
265275
"radioValue": true,
266276
"reliableMarginLeft": true,
267277
"reliableTrDimensions": true,
268-
"scrollboxSize": true
278+
"scrollboxSize": true,
279+
"trim": true
269280
},
270281
ios_7: {
271282
"ajax": true,
@@ -284,7 +295,28 @@ testIframe(
284295
"radioValue": true,
285296
"reliableMarginLeft": true,
286297
"reliableTrDimensions": true,
287-
"scrollboxSize": true
298+
"scrollboxSize": true,
299+
"trim": true
300+
},
301+
android_4_0: {
302+
"ajax": true,
303+
"boxSizingReliable": true,
304+
"checkClone": false,
305+
"checkOn": false,
306+
"clearCloneStyle": true,
307+
"cors": true,
308+
"createHTMLDocument": true,
309+
"focusin": false,
310+
"noCloneChecked": true,
311+
"option": true,
312+
"optSelected": true,
313+
"pixelBoxStyles": false,
314+
"pixelPosition": false,
315+
"radioValue": true,
316+
"reliableMarginLeft": false,
317+
"reliableTrDimensions": true,
318+
"scrollboxSize": true,
319+
"trim": false
288320
},
289321
android: {
290322
"ajax": true,
@@ -303,7 +335,8 @@ testIframe(
303335
"radioValue": true,
304336
"reliableMarginLeft": false,
305337
"reliableTrDimensions": true,
306-
"scrollboxSize": true
338+
"scrollboxSize": true,
339+
"trim": true
307340
}
308341
};
309342

@@ -313,6 +346,10 @@ testIframe(
313346
delete expectedMap[ browserKey ].ajax;
314347
delete expectedMap[ browserKey ].cors;
315348
}
349+
if ( typeof jQuery.trim !== "function" ) {
350+
// If running no-deprecated test, then jQuery.trim does not exist, and we should not verify support for it.
351+
delete expectedMap[ browserKey ].trim;
352+
}
316353
}
317354

318355
if ( /edge\//i.test( userAgent ) ) {
@@ -332,7 +369,9 @@ testIframe(
332369
expected = expectedMap.firefox_60;
333370
} else if ( /firefox/i.test( userAgent ) ) {
334371
expected = expectedMap.firefox;
335-
} else if ( /android 4\.[0-3]/i.test( userAgent ) ) {
372+
} else if ( /android 4\.0/i.test( userAgent ) ) {
373+
expected = expectedMap.android_4_0;
374+
} else if ( /android 4\.[1-3]/i.test( userAgent ) ) {
336375
expected = expectedMap.android;
337376
} else if ( /iphone os (?:9|10)_/i.test( userAgent ) ) {
338377
expected = expectedMap.ios_9_10;

0 commit comments

Comments
 (0)