Storage and Data Management Polyfills

1. localStorage and sessionStorage Polyfills

API Method Description Persistence
localStorage localStorage.setItem(key, value) Store data persistently Until explicitly deleted
sessionStorage sessionStorage.setItem(key, value) Store data for session Until tab/window closed
getItem storage.getItem(key) Retrieve stored value Returns string or null
removeItem storage.removeItem(key) Delete specific item -
clear storage.clear() Delete all items -
key storage.key(index) Get key by index Returns key name or null
length storage.length Number of stored items Read-only property

Example: localStorage polyfill using cookies

// localStorage polyfill for older browsers (IE7 and below)
(function() {
    if ('localStorage' in window && window.localStorage !== null) {
        return;
    }
    
    var Storage = function() {
        this.length = 0;
        this._data = {};
    };
    
    Storage.prototype.setItem = function(key, value) {
        if (!(key in this._data)) {
            this.length++;
        }
        this._data[key] = String(value);
        this._save();
    };
    
    Storage.prototype.getItem = function(key) {
        return this._data.hasOwnProperty(key) ? this._data[key] : null;
    };
    
    Storage.prototype.removeItem = function(key) {
        if (key in this._data) {
            delete this._data[key];
            this.length--;
            this._save();
        }
    };
    
    Storage.prototype.clear = function() {
        this._data = {};
        this.length = 0;
        this._save();
    };
    
    Storage.prototype.key = function(index) {
        var keys = Object.keys(this._data);
        return index >= 0 && index < keys.length ? keys[index] : null;
    };
    
    Storage.prototype._save = function() {
        // Save to cookie (with size limits)
        var serialized = JSON.stringify(this._data);
        document.cookie = this._cookieName + '=' + 
                         encodeURIComponent(serialized) + 
                         '; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/';
    };
    
    Storage.prototype._load = function() {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = cookies[i].trim();
            if (cookie.indexOf(this._cookieName + '=') === 0) {
                var value = cookie.substring(this._cookieName.length + 1);
                try {
                    this._data = JSON.parse(decodeURIComponent(value));
                    this.length = Object.keys(this._data).length;
                } catch(e) {}
                break;
            }
        }
    };
    
    // Create localStorage
    var localStorage = new Storage();
    localStorage._cookieName = '__localStorage__';
    localStorage._load();
    
    // Create sessionStorage
    var sessionStorage = new Storage();
    sessionStorage._cookieName = '__sessionStorage__';
    sessionStorage._load();
    
    // Make read-only
    try {
        Object.defineProperty(window, 'localStorage', {
            value: localStorage,
            writable: false
        });
        Object.defineProperty(window, 'sessionStorage', {
            value: sessionStorage,
            writable: false
        });
    } catch(e) {
        window.localStorage = localStorage;
        window.sessionStorage = sessionStorage;
    }
})();

Example: Safe storage access with try-catch

// Safe storage wrapper (handles private mode)
var SafeStorage = {
    isAvailable: function(storageType) {
        try {
            var storage = window[storageType];
            var test = '__storage_test__';
            storage.setItem(test, test);
            storage.removeItem(test);
            return true;
        } catch(e) {
            return false;
        }
    },
    
    setItem: function(key, value, storageType) {
        storageType = storageType || 'localStorage';
        try {
            window[storageType].setItem(key, value);
            return true;
        } catch(e) {
            console.warn('Storage unavailable:', e);
            return false;
        }
    },
    
    getItem: function(key, storageType) {
        storageType = storageType || 'localStorage';
        try {
            return window[storageType].getItem(key);
        } catch(e) {
            console.warn('Storage unavailable:', e);
            return null;
        }
    },
    
    removeItem: function(key, storageType) {
        storageType = storageType || 'localStorage';
        try {
            window[storageType].removeItem(key);
            return true;
        } catch(e) {
            console.warn('Storage unavailable:', e);
            return false;
        }
    }
};

// Usage
if (SafeStorage.isAvailable('localStorage')) {
    SafeStorage.setItem('user', JSON.stringify({ name: 'John' }));
    var user = JSON.parse(SafeStorage.getItem('user'));
}
Warning: localStorage may throw SecurityError in private browsing mode or when storage quota is exceeded. Always wrap storage operations in try-catch blocks.

2. IndexedDB Compatibility and Polyfills

Feature API Description Support
Database indexedDB.open(name, version) Open/create database IE10+
Object Store db.createObjectStore(name, options) Create table equivalent In onupgradeneeded callback
Transaction db.transaction(stores, mode) Create transaction scope Modes: readonly, readwrite
CRUD Operations store.add/get/put/delete() Data manipulation Returns IDBRequest
Cursors store.openCursor() Iterate over records Event-driven iteration
Vendor Prefixes webkitIndexedDB, mozIndexedDB Legacy browser support Prefix normalization needed

Example: IndexedDB vendor prefix normalization

// Normalize IndexedDB across browsers
window.indexedDB = window.indexedDB || 
                   window.mozIndexedDB || 
                   window.webkitIndexedDB || 
                   window.msIndexedDB;

window.IDBTransaction = window.IDBTransaction || 
                        window.webkitIDBTransaction || 
                        window.msIDBTransaction;

window.IDBKeyRange = window.IDBKeyRange || 
                     window.webkitIDBKeyRange || 
                     window.msIDBKeyRange;

// Check if IndexedDB is available
if (!window.indexedDB) {
    console.warn('IndexedDB not supported');
}

// Promise wrapper for IndexedDB
var IDB = {
    open: function(name, version) {
        return new Promise(function(resolve, reject) {
            var request = indexedDB.open(name, version);
            
            request.onerror = function() {
                reject(request.error);
            };
            
            request.onsuccess = function() {
                resolve(request.result);
            };
            
            request.onupgradeneeded = function(event) {
                var db = event.target.result;
                // Handle upgrades
                if (!db.objectStoreNames.contains('store')) {
                    db.createObjectStore('store', { keyPath: 'id', autoIncrement: true });
                }
            };
        });
    },
    
    add: function(db, storeName, data) {
        return new Promise(function(resolve, reject) {
            var transaction = db.transaction([storeName], 'readwrite');
            var store = transaction.objectStore(storeName);
            var request = store.add(data);
            
            request.onsuccess = function() {
                resolve(request.result);
            };
            
            request.onerror = function() {
                reject(request.error);
            };
        });
    },
    
    get: function(db, storeName, key) {
        return new Promise(function(resolve, reject) {
            var transaction = db.transaction([storeName], 'readonly');
            var store = transaction.objectStore(storeName);
            var request = store.get(key);
            
            request.onsuccess = function() {
                resolve(request.result);
            };
            
            request.onerror = function() {
                reject(request.error);
            };
        });
    }
};

// Usage
IDB.open('myDatabase', 1)
    .then(function(db) {
        return IDB.add(db, 'store', { name: 'John', age: 30 });
    })
    .then(function(id) {
        console.log('Added with ID:', id);
    })
    .catch(function(error) {
        console.error('IndexedDB error:', error);
    });
Note: IndexedDB is asynchronous and event-driven. Use Promise wrappers or libraries like idb or localForage for easier API.
Operation Method Options Notes
Set Cookie document.cookie = 'key=value' expires, path, domain, secure, samesite Append, not replace
Get Cookie Parse document.cookie - Returns all cookies as string
Delete Cookie Set with past expiration expires=Thu, 01 Jan 1970 Must match path/domain
Size Limit ~4KB per cookie ~20-50 cookies per domain Browser dependent
HttpOnly Server-side only flag Cannot access via JavaScript Security feature
SameSite SameSite=Strict|Lax|None CSRF protection Modern browsers
// Cookie utility functions
var Cookies = {
    set: function(name, value, options) {
        options = options || {};
        
        var cookieString = encodeURIComponent(name) + '=' + encodeURIComponent(value);
        
        // Expires
        if (options.expires) {
            var expires;
            if (typeof options.expires === 'number') {
                expires = new Date();
                expires.setTime(expires.getTime() + options.expires * 24 * 60 * 60 * 1000);
            } else {
                expires = options.expires;
            }
            cookieString += '; expires=' + expires.toUTCString();
        }
        
        // Path
        cookieString += '; path=' + (options.path || '/');
        
        // Domain
        if (options.domain) {
            cookieString += '; domain=' + options.domain;
        }
        
        // Secure
        if (options.secure) {
            cookieString += '; secure';
        }
        
        // SameSite
        if (options.sameSite) {
            cookieString += '; samesite=' + options.sameSite;
        }
        
        document.cookie = cookieString;
    },
    
    get: function(name) {
        var nameEQ = encodeURIComponent(name) + '=';
        var cookies = document.cookie.split(';');
        
        for (var i = 0; i < cookies.length; i++) {
            var cookie = cookies[i].trim();
            if (cookie.indexOf(nameEQ) === 0) {
                return decodeURIComponent(cookie.substring(nameEQ.length));
            }
        }
        return null;
    },
    
    remove: function(name, options) {
        options = options || {};
        options.expires = -1;
        this.set(name, '', options);
    },
    
    getAll: function() {
        var cookies = {};
        var cookieArray = document.cookie.split(';');
        
        for (var i = 0; i < cookieArray.length; i++) {
            var cookie = cookieArray[i].trim();
            var parts = cookie.split('=');
            if (parts.length === 2) {
                cookies[decodeURIComponent(parts[0])] = decodeURIComponent(parts[1]);
            }
        }
        return cookies;
    },
    
    has: function(name) {
        return this.get(name) !== null;
    }
};

// Usage
Cookies.set('username', 'john_doe', { 
    expires: 7,  // 7 days
    path: '/',
    secure: true,
    sameSite: 'Lax'
});

var username = Cookies.get('username');
console.log(username);  // 'john_doe'

Cookies.remove('username');

var allCookies = Cookies.getAll();
console.log(allCookies);

Example: Storage wrapper using cookies as fallback

// Universal storage with cookie fallback
var UniversalStorage = {
    _useLocalStorage: (function() {
        try {
            var test = '__test__';
            localStorage.setItem(test, test);
            localStorage.removeItem(test);
            return true;
        } catch(e) {
            return false;
        }
    })(),
    
    setItem: function(key, value, days) {
        if (this._useLocalStorage) {
            try {
                localStorage.setItem(key, value);
                return;
            } catch(e) {}
        }
        
        // Fallback to cookies
        Cookies.set(key, value, { expires: days || 365 });
    },
    
    getItem: function(key) {
        if (this._useLocalStorage) {
            try {
                return localStorage.getItem(key);
            } catch(e) {}
        }
        
        // Fallback to cookies
        return Cookies.get(key);
    },
    
    removeItem: function(key) {
        if (this._useLocalStorage) {
            try {
                localStorage.removeItem(key);
                return;
            } catch(e) {}
        }
        
        // Fallback to cookies
        Cookies.remove(key);
    }
};

// Usage - automatically uses localStorage or cookies
UniversalStorage.setItem('token', 'abc123');
var token = UniversalStorage.getItem('token');
Warning: Cookies have ~4KB size limit per cookie and are sent with every HTTP request. Use localStorage for larger data. Never store sensitive data in cookies without Secure and HttpOnly flags.

4. WebSQL to IndexedDB Migration Polyfills

Feature WebSQL IndexedDB Status
Standard W3C Working Draft W3C Recommendation WEBSQL DEPRECATED
API Style SQL queries NoSQL key-value Different paradigms
Browser Support Chrome, Safari (removed in new versions) All modern browsers Migrate to IndexedDB
Storage Limit ~5-10MB default ~50MB+ (quota API) IndexedDB more flexible
Transactions SQL transactions Object store transactions Both support ACID

Example: WebSQL migration helper

// WebSQL migration utility
var WebSQLMigration = {
    hasWebSQL: function() {
        return 'openDatabase' in window;
    },
    
    hasIndexedDB: function() {
        return 'indexedDB' in window;
    },
    
    migrateFromWebSQL: function(webSQLDB, indexedDBName, tableName, storeName) {
        return new Promise(function(resolve, reject) {
            if (!this.hasWebSQL() || !this.hasIndexedDB()) {
                reject(new Error('Required APIs not available'));
                return;
            }
            
            // Read from WebSQL
            webSQLDB.transaction(function(tx) {
                tx.executeSql('SELECT * FROM ' + tableName, [], function(tx, results) {
                    var rows = [];
                    for (var i = 0; i < results.rows.length; i++) {
                        rows.push(results.rows.item(i));
                    }
                    
                    // Write to IndexedDB
                    var request = indexedDB.open(indexedDBName, 1);
                    
                    request.onupgradeneeded = function(e) {
                        var db = e.target.result;
                        if (!db.objectStoreNames.contains(storeName)) {
                            db.createObjectStore(storeName, { keyPath: 'id' });
                        }
                    };
                    
                    request.onsuccess = function(e) {
                        var db = e.target.result;
                        var transaction = db.transaction([storeName], 'readwrite');
                        var store = transaction.objectStore(storeName);
                        
                        rows.forEach(function(row) {
                            store.add(row);
                        });
                        
                        transaction.oncomplete = function() {
                            resolve(rows.length);
                        };
                        
                        transaction.onerror = function() {
                            reject(transaction.error);
                        };
                    };
                    
                    request.onerror = function() {
                        reject(request.error);
                    };
                });
            });
        }.bind(this));
    }
};

// Usage
var db = openDatabase('mydb', '1.0', 'My Database', 2 * 1024 * 1024);
WebSQLMigration.migrateFromWebSQL(db, 'myIndexedDB', 'users', 'users')
    .then(function(count) {
        console.log('Migrated ' + count + ' records');
    })
    .catch(function(error) {
        console.error('Migration failed:', error);
    });
Note: WebSQL is deprecated and removed from modern browsers. Migrate existing WebSQL data to IndexedDB or use libraries like localForage that abstract storage.

5. Memory Storage Fallback Implementations

Strategy Persistence Use Case Limitations
In-Memory Object Session only (cleared on reload) Last resort fallback No persistence, lost on refresh
sessionStorage Tab/window session Temporary data for session Lost when tab closed
localStorage Persistent across sessions Primary storage method May fail in private mode
Cookie Persistent (with expiration) Fallback for localStorage 4KB limit, sent with requests
IndexedDB Persistent, large storage Complex data, large datasets Async API complexity

Example: Complete storage fallback implementation

// Storage facade with fallback chain
var StorageFacade = (function() {
    'use strict';
    
    var storageType = null;
    var memoryStorage = {};
    
    // Test storage availability
    function testStorage(type) {
        try {
            var storage = window[type];
            var test = '__storage_test__';
            storage.setItem(test, test);
            storage.removeItem(test);
            return true;
        } catch(e) {
            return false;
        }
    }
    
    // Determine best available storage
    function init() {
        if (testStorage('localStorage')) {
            storageType = 'localStorage';
        } else if (testStorage('sessionStorage')) {
            storageType = 'sessionStorage';
        } else if ('cookie' in document) {
            storageType = 'cookie';
        } else {
            storageType = 'memory';
        }
        console.log('Using storage type:', storageType);
    }
    
    function setItem(key, value) {
        switch(storageType) {
            case 'localStorage':
            case 'sessionStorage':
                try {
                    window[storageType].setItem(key, value);
                } catch(e) {
                    console.warn('Storage quota exceeded, falling back to memory');
                    memoryStorage[key] = value;
                }
                break;
                
            case 'cookie':
                Cookies.set(key, value, { expires: 365 });
                break;
                
            case 'memory':
            default:
                memoryStorage[key] = value;
                break;
        }
    }
    
    function getItem(key) {
        switch(storageType) {
            case 'localStorage':
            case 'sessionStorage':
                try {
                    return window[storageType].getItem(key);
                } catch(e) {
                    return memoryStorage[key] || null;
                }
                
            case 'cookie':
                return Cookies.get(key);
                
            case 'memory':
            default:
                return memoryStorage[key] || null;
        }
    }
    
    function removeItem(key) {
        switch(storageType) {
            case 'localStorage':
            case 'sessionStorage':
                try {
                    window[storageType].removeItem(key);
                } catch(e) {
                    delete memoryStorage[key];
                }
                break;
                
            case 'cookie':
                Cookies.remove(key);
                break;
                
            case 'memory':
            default:
                delete memoryStorage[key];
                break;
        }
    }
    
    function clear() {
        switch(storageType) {
            case 'localStorage':
            case 'sessionStorage':
                try {
                    window[storageType].clear();
                } catch(e) {
                    memoryStorage = {};
                }
                break;
                
            case 'cookie':
                var cookies = Cookies.getAll();
                Object.keys(cookies).forEach(function(key) {
                    Cookies.remove(key);
                });
                break;
                
            case 'memory':
            default:
                memoryStorage = {};
                break;
        }
    }
    
    function getStorageType() {
        return storageType;
    }
    
    // Initialize on load
    init();
    
    // Public API
    return {
        setItem: setItem,
        getItem: getItem,
        removeItem: removeItem,
        clear: clear,
        getStorageType: getStorageType
    };
})();

// Usage - automatically uses best available storage
StorageFacade.setItem('user', JSON.stringify({ name: 'John', age: 30 }));
var user = JSON.parse(StorageFacade.getItem('user'));
console.log('Using storage:', StorageFacade.getStorageType());

Example: Storage with JSON serialization helper

// Enhanced storage with automatic JSON handling
var SmartStorage = {
    set: function(key, value, options) {
        var serialized = typeof value === 'string' ? value : JSON.stringify(value);
        
        try {
            StorageFacade.setItem(key, serialized);
            return true;
        } catch(e) {
            console.error('Storage error:', e);
            return false;
        }
    },
    
    get: function(key, defaultValue) {
        var value = StorageFacade.getItem(key);
        
        if (value === null) {
            return defaultValue !== undefined ? defaultValue : null;
        }
        
        try {
            return JSON.parse(value);
        } catch(e) {
            // Return as string if not JSON
            return value;
        }
    },
    
    remove: function(key) {
        StorageFacade.removeItem(key);
    },
    
    clear: function() {
        StorageFacade.clear();
    },
    
    has: function(key) {
        return StorageFacade.getItem(key) !== null;
    }
};

// Usage with automatic JSON handling
SmartStorage.set('user', { name: 'John', age: 30, roles: ['admin', 'user'] });
var user = SmartStorage.get('user');
console.log(user.name);  // 'John'
console.log(user.roles);  // ['admin', 'user']

SmartStorage.set('counter', 42);
var counter = SmartStorage.get('counter');
console.log(typeof counter);  // 'number'

var nonExistent = SmartStorage.get('missing', { default: 'value' });
console.log(nonExistent);  // { default: 'value' }

Key Takeaways - Storage & Data Management

  • localStorage/sessionStorage: Always wrap in try-catch for private mode handling
  • IndexedDB: Normalize vendor prefixes, use Promise wrappers for easier API
  • Cookies: 4KB limit, use for fallback or when persistence required
  • WebSQL: Deprecated - migrate to IndexedDB immediately
  • Fallback strategy: localStorage → sessionStorage → cookies → memory
  • Libraries: Consider localForage or similar for abstraction