diff options
author | Rick Waldron <waldron.rick@gmail.com> | 2013-11-11 13:13:22 -0500 |
---|---|---|
committer | Timmy Willison <timmywillisn@gmail.com> | 2015-03-04 17:26:47 -0500 |
commit | d702b7637a61e1973e08c27b8d8de2ed24a543e2 (patch) | |
tree | 5facc46a8a1fd855112e851219c6efc1b2206eb3 /src | |
parent | 95fb798980d7e404c413e29e20016db9052e2bf2 (diff) | |
download | jquery-d702b7637a61e1973e08c27b8d8de2ed24a543e2.tar.gz jquery-d702b7637a61e1973e08c27b8d8de2ed24a543e2.zip |
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
Diffstat (limited to 'src')
-rw-r--r-- | src/data/Data.js | 94 | ||||
-rw-r--r-- | src/event.js | 6 | ||||
-rw-r--r-- | src/manipulation.js | 31 |
3 files changed, 64 insertions, 67 deletions
diff --git a/src/data/Data.js b/src/data/Data.js index 7b9ec5b54..ec108e062 100644 --- a/src/data/Data.js +++ b/src/data/Data.js @@ -5,15 +5,6 @@ define([ ], function( jQuery, rnotwhite ) { function Data() { - // Support: Android<4, - // Old WebKit does not have Object.preventExtensions/freeze method, - // return new empty object instead with no [[set]] accessor - Object.defineProperty( this.cache = {}, 0, { - get: function() { - return {}; - } - }); - this.expando = jQuery.expando + Data.uid++; } @@ -21,45 +12,60 @@ Data.uid = 1; Data.accepts = jQuery.acceptData; Data.prototype = { - key: function( owner ) { - // We can accept data for non-element nodes in modern browsers, - // but we should not, see #8335. - // Always return the key for a frozen object. - if ( !Data.accepts( owner ) ) { - return 0; - } - - // Check if the owner object already has a cache key - var unlock = owner[ this.expando ]; - // If not, create one - if ( !unlock ) { - unlock = Data.uid++; + register: function( owner, initial ) { + var descriptor = {}, + value = initial || {}; + try { // If it is a node unlikely to be stringify-ed or looped over // use plain assignment if ( owner.nodeType ) { - owner[ this.expando ] = unlock; + owner[ this.expando ] = value; + // Otherwise secure it in a non-enumerable, non-writable property + // configurability must be true to allow the property to be + // deleted with the delete operator } else { - Object.defineProperty( owner, this.expando, { value: unlock } ); + descriptor[ this.expando ] = { + value: value, + writable: true, + configurable: true + }; + Object.defineProperties( owner, descriptor ); } + + // Support: Android < 4 + // Fallback to a less secure definition + } catch ( e ) { + descriptor[ this.expando ] = value; + jQuery.extend( owner, descriptor ); } - // Ensure the cache object - if ( !this.cache[ unlock ] ) { - this.cache[ unlock ] = {}; + return owner[ this.expando ]; + }, + cache: function( owner, initial ) { + // We can accept data for non-element nodes in modern browsers, + // but we should not, see #8335. + // Always return an empty object. + if ( !Data.accepts( owner ) ) { + return {}; } - return unlock; + // Check if the owner object already has a cache + var cache = owner[ this.expando ]; + + // If so, return it + if ( cache ) { + return cache; + } + + // If not, register one + return this.register( owner, initial ); }, set: function( owner, data, value ) { var prop, - // There may be an unlock assigned to this node, - // if there is no entry for this "owner", create one inline - // and set the unlock as though an owner entry had always existed - unlock = this.key( owner ), - cache = this.cache[ unlock ]; + cache = this.cache( owner ); // Handle: [ owner, key, value ] args if ( typeof data === "string" ) { @@ -69,7 +75,8 @@ Data.prototype = { } else { // Fresh assignments by object are shallow copied if ( jQuery.isEmptyObject( cache ) ) { - jQuery.extend( this.cache[ unlock ], data ); + + jQuery.extend( cache, data ); // Otherwise, copy the properties one-by-one to the cache object } else { for ( prop in data ) { @@ -80,11 +87,7 @@ Data.prototype = { return cache; }, get: function( owner, key ) { - // Either a valid cache is found, or will be created. - // New caches will be created and the unlock returned, - // allowing direct access to the newly created - // empty data object. A valid owner object must be provided. - var cache = this.cache[ this.key( owner ) ]; + var cache = this.cache( owner ); return key === undefined ? cache : cache[ key ]; @@ -125,11 +128,10 @@ Data.prototype = { }, remove: function( owner, key ) { var i, name, camel, - unlock = this.key( owner ), - cache = this.cache[ unlock ]; + cache = this.cache( owner ); if ( key === undefined ) { - this.cache[ unlock ] = {}; + this.register( owner ); } else { // Support array or space separated string of keys @@ -156,19 +158,19 @@ Data.prototype = { } i = name.length; + while ( i-- ) { delete cache[ name[ i ] ]; } } }, hasData: function( owner ) { - return !jQuery.isEmptyObject( - this.cache[ owner[ this.expando ] ] || {} - ); + var cache = owner[ this.expando ]; + return cache !== undefined && !jQuery.isEmptyObject( cache ); }, discard: function( owner ) { if ( owner[ this.expando ] ) { - delete this.cache[ owner[ this.expando ] ]; + delete owner[ this.expando ]; } } }; diff --git a/src/event.js b/src/event.js index ae0539bb9..4849ef3f0 100644 --- a/src/event.js +++ b/src/event.js @@ -216,8 +216,12 @@ jQuery.event = { // Remove the expando if it's no longer used if ( jQuery.isEmptyObject( events ) ) { + // Normally this should go through the data api + // but since event.js owns these properties, + // this exception is made for the sake of optimizing + // the operation. delete elemData.handle; - dataPriv.remove( elem, "events" ); + delete elemData.events; } }, diff --git a/src/manipulation.js b/src/manipulation.js index 7c9f5049a..be982dc2d 100644 --- a/src/manipulation.js +++ b/src/manipulation.js @@ -288,34 +288,25 @@ jQuery.extend({ }, cleanData: function( elems ) { - var data, elem, type, key, + var data, elem, type, special = jQuery.event.special, i = 0; for ( ; (elem = elems[ i ]) !== undefined; i++ ) { - if ( jQuery.acceptData( elem ) ) { - key = elem[ dataPriv.expando ]; - - if ( key && (data = dataPriv.cache[ key ]) ) { - if ( data.events ) { - for ( type in data.events ) { - if ( special[ type ] ) { - jQuery.event.remove( elem, type ); - - // This is a shortcut to avoid jQuery.event.remove's overhead - } else { - jQuery.removeEvent( elem, type, data.handle ); - } + if ( jQuery.acceptData( elem ) && (data = elem[ dataPriv.expando ])) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); } } - if ( dataPriv.cache[ key ] ) { - // Discard any remaining `private` data - delete dataPriv.cache[ key ]; - } } + delete data.events; } - // Discard any remaining `user` data - delete dataUser.cache[ elem[ dataUser.expando ] ]; } } }); |