Skip to content

Commit d702b76

Browse files
rwaldrontimmywil
authored andcommitted
Data: move element cache to element[expando]
- avoid explicit data.discard() cleanup calls - explicitly remove the data.events property, only when private data exists - reduces code footprint Fixes gh-1734 Close gh-1428
1 parent 95fb798 commit d702b76

File tree

4 files changed

+66
-74
lines changed

4 files changed

+66
-74
lines changed

src/data/Data.js

Lines changed: 48 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -5,61 +5,67 @@ define([
55
], function( jQuery, rnotwhite ) {
66

77
function Data() {
8-
// Support: Android<4,
9-
// Old WebKit does not have Object.preventExtensions/freeze method,
10-
// return new empty object instead with no [[set]] accessor
11-
Object.defineProperty( this.cache = {}, 0, {
12-
get: function() {
13-
return {};
14-
}
15-
});
16-
178
this.expando = jQuery.expando + Data.uid++;
189
}
1910

2011
Data.uid = 1;
2112
Data.accepts = jQuery.acceptData;
2213

2314
Data.prototype = {
24-
key: function( owner ) {
25-
// We can accept data for non-element nodes in modern browsers,
26-
// but we should not, see #8335.
27-
// Always return the key for a frozen object.
28-
if ( !Data.accepts( owner ) ) {
29-
return 0;
30-
}
31-
32-
// Check if the owner object already has a cache key
33-
var unlock = owner[ this.expando ];
3415

35-
// If not, create one
36-
if ( !unlock ) {
37-
unlock = Data.uid++;
16+
register: function( owner, initial ) {
17+
var descriptor = {},
18+
value = initial || {};
3819

20+
try {
3921
// If it is a node unlikely to be stringify-ed or looped over
4022
// use plain assignment
4123
if ( owner.nodeType ) {
42-
owner[ this.expando ] = unlock;
24+
owner[ this.expando ] = value;
25+
4326
// Otherwise secure it in a non-enumerable, non-writable property
27+
// configurability must be true to allow the property to be
28+
// deleted with the delete operator
4429
} else {
45-
Object.defineProperty( owner, this.expando, { value: unlock } );
30+
descriptor[ this.expando ] = {
31+
value: value,
32+
writable: true,
33+
configurable: true
34+
};
35+
Object.defineProperties( owner, descriptor );
4636
}
37+
38+
// Support: Android < 4
39+
// Fallback to a less secure definition
40+
} catch ( e ) {
41+
descriptor[ this.expando ] = value;
42+
jQuery.extend( owner, descriptor );
4743
}
4844

49-
// Ensure the cache object
50-
if ( !this.cache[ unlock ] ) {
51-
this.cache[ unlock ] = {};
45+
return owner[ this.expando ];
46+
},
47+
cache: function( owner, initial ) {
48+
// We can accept data for non-element nodes in modern browsers,
49+
// but we should not, see #8335.
50+
// Always return an empty object.
51+
if ( !Data.accepts( owner ) ) {
52+
return {};
5253
}
5354

54-
return unlock;
55+
// Check if the owner object already has a cache
56+
var cache = owner[ this.expando ];
57+
58+
// If so, return it
59+
if ( cache ) {
60+
return cache;
61+
}
62+
63+
// If not, register one
64+
return this.register( owner, initial );
5565
},
5666
set: function( owner, data, value ) {
5767
var prop,
58-
// There may be an unlock assigned to this node,
59-
// if there is no entry for this "owner", create one inline
60-
// and set the unlock as though an owner entry had always existed
61-
unlock = this.key( owner ),
62-
cache = this.cache[ unlock ];
68+
cache = this.cache( owner );
6369

6470
// Handle: [ owner, key, value ] args
6571
if ( typeof data === "string" ) {
@@ -69,7 +75,8 @@ Data.prototype = {
6975
} else {
7076
// Fresh assignments by object are shallow copied
7177
if ( jQuery.isEmptyObject( cache ) ) {
72-
jQuery.extend( this.cache[ unlock ], data );
78+
79+
jQuery.extend( cache, data );
7380
// Otherwise, copy the properties one-by-one to the cache object
7481
} else {
7582
for ( prop in data ) {
@@ -80,11 +87,7 @@ Data.prototype = {
8087
return cache;
8188
},
8289
get: function( owner, key ) {
83-
// Either a valid cache is found, or will be created.
84-
// New caches will be created and the unlock returned,
85-
// allowing direct access to the newly created
86-
// empty data object. A valid owner object must be provided.
87-
var cache = this.cache[ this.key( owner ) ];
90+
var cache = this.cache( owner );
8891

8992
return key === undefined ?
9093
cache : cache[ key ];
@@ -125,11 +128,10 @@ Data.prototype = {
125128
},
126129
remove: function( owner, key ) {
127130
var i, name, camel,
128-
unlock = this.key( owner ),
129-
cache = this.cache[ unlock ];
131+
cache = this.cache( owner );
130132

131133
if ( key === undefined ) {
132-
this.cache[ unlock ] = {};
134+
this.register( owner );
133135

134136
} else {
135137
// Support array or space separated string of keys
@@ -156,19 +158,19 @@ Data.prototype = {
156158
}
157159

158160
i = name.length;
161+
159162
while ( i-- ) {
160163
delete cache[ name[ i ] ];
161164
}
162165
}
163166
},
164167
hasData: function( owner ) {
165-
return !jQuery.isEmptyObject(
166-
this.cache[ owner[ this.expando ] ] || {}
167-
);
168+
var cache = owner[ this.expando ];
169+
return cache !== undefined && !jQuery.isEmptyObject( cache );
168170
},
169171
discard: function( owner ) {
170172
if ( owner[ this.expando ] ) {
171-
delete this.cache[ owner[ this.expando ] ];
173+
delete owner[ this.expando ];
172174
}
173175
}
174176
};

src/event.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,8 +216,12 @@ jQuery.event = {
216216

217217
// Remove the expando if it's no longer used
218218
if ( jQuery.isEmptyObject( events ) ) {
219+
// Normally this should go through the data api
220+
// but since event.js owns these properties,
221+
// this exception is made for the sake of optimizing
222+
// the operation.
219223
delete elemData.handle;
220-
dataPriv.remove( elem, "events" );
224+
delete elemData.events;
221225
}
222226
},
223227

src/manipulation.js

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -288,34 +288,25 @@ jQuery.extend({
288288
},
289289

290290
cleanData: function( elems ) {
291-
var data, elem, type, key,
291+
var data, elem, type,
292292
special = jQuery.event.special,
293293
i = 0;
294294

295295
for ( ; (elem = elems[ i ]) !== undefined; i++ ) {
296-
if ( jQuery.acceptData( elem ) ) {
297-
key = elem[ dataPriv.expando ];
298-
299-
if ( key && (data = dataPriv.cache[ key ]) ) {
300-
if ( data.events ) {
301-
for ( type in data.events ) {
302-
if ( special[ type ] ) {
303-
jQuery.event.remove( elem, type );
304-
305-
// This is a shortcut to avoid jQuery.event.remove's overhead
306-
} else {
307-
jQuery.removeEvent( elem, type, data.handle );
308-
}
296+
if ( jQuery.acceptData( elem ) && (data = elem[ dataPriv.expando ])) {
297+
if ( data.events ) {
298+
for ( type in data.events ) {
299+
if ( special[ type ] ) {
300+
jQuery.event.remove( elem, type );
301+
302+
// This is a shortcut to avoid jQuery.event.remove's overhead
303+
} else {
304+
jQuery.removeEvent( elem, type, data.handle );
309305
}
310306
}
311-
if ( dataPriv.cache[ key ] ) {
312-
// Discard any remaining `private` data
313-
delete dataPriv.cache[ key ];
314-
}
315307
}
308+
delete data.events;
316309
}
317-
// Discard any remaining `user` data
318-
delete dataUser.cache[ elem[ dataUser.expando ] ];
319310
}
320311
}
321312
});

test/unit/manipulation.js

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ test( "text(undefined)", function() {
6464

6565
function testText( valueObj ) {
6666

67-
expect( 7 );
67+
expect( 6 );
6868

6969
var val, j, expected, $multipleElements, $parentDiv, $childDiv;
7070

@@ -94,8 +94,6 @@ function testText( valueObj ) {
9494
$parentDiv = jQuery( "<div/>" );
9595
$parentDiv.append( $childDiv );
9696
$parentDiv.text("Dry off");
97-
98-
equal( $childDiv.data("leak"), undefined, "Check for leaks (#11809)" );
9997
}
10098

10199
test( "text(String)", function() {
@@ -1814,7 +1812,7 @@ test( "clone()/html() don't expose jQuery/Sizzle expandos (#12858)", function()
18141812

18151813
test( "remove() no filters", function() {
18161814

1817-
expect( 3 );
1815+
expect( 2 );
18181816

18191817
var first = jQuery("#ap").children().first();
18201818

@@ -1823,9 +1821,6 @@ test( "remove() no filters", function() {
18231821
jQuery("#ap").children().remove();
18241822
ok( jQuery("#ap").text().length > 10, "Check text is not removed" );
18251823
equal( jQuery("#ap").children().length, 0, "Check remove" );
1826-
1827-
equal( first.data("foo"), null, "first data" );
1828-
18291824
});
18301825

18311826
test( "remove() with filters", function() {

0 commit comments

Comments
 (0)