惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Cloudbric
Cloudbric
T
Threat Research - Cisco Blogs
Simon Willison's Weblog
Simon Willison's Weblog
AWS News Blog
AWS News Blog
P
Privacy & Cybersecurity Law Blog
H
Help Net Security
云风的 BLOG
云风的 BLOG
G
GRAHAM CLULEY
Spread Privacy
Spread Privacy
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
A
Arctic Wolf
Project Zero
Project Zero
Engineering at Meta
Engineering at Meta
P
Privacy International News Feed
Blog — PlanetScale
Blog — PlanetScale
Stack Overflow Blog
Stack Overflow Blog
M
MIT News - Artificial intelligence
The Register - Security
The Register - Security
Recorded Future
Recorded Future
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
C
Cisco Blogs
PCI Perspectives
PCI Perspectives
Recent Announcements
Recent Announcements
Martin Fowler
Martin Fowler
A
About on SuperTechFans
W
WeLiveSecurity
GbyAI
GbyAI
V
Vulnerabilities – Threatpost
The GitHub Blog
The GitHub Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
C
Check Point Blog
Y
Y Combinator Blog
月光博客
月光博客
Scott Helme
Scott Helme
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google DeepMind News
Google DeepMind News
F
Fortinet All Blogs
U
Unit 42
G
Google Developers Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Threatpost
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Google Online Security Blog
Google Online Security Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Cisco Talos Blog
Cisco Talos Blog
博客园 - 三生石上(FineUI控件)
Hugging Face - Blog
Hugging Face - Blog
MongoDB | Blog
MongoDB | Blog
博客园 - 司徒正美

Mathias Bynens

A horrifying globalThis polyfill in universal JavaScript JavaScript engine fundamentals: optimizing prototypes JavaScript engine fundamentals: Shapes and Inline Caches Asynchronous stack traces: why await beats Promise#then() ECMAScript regular expressions are getting better! Unicode property escapes in JavaScript regular expressions ES2015 const is not about immutability Valid JavaScript variable names in ES2015 Unicode-aware regular expressions in ES2015 Dear Google, please fix plain text emails in Gmail PBKDF2+HMAC hash collisions explained JavaScript has a Unicode problem Processing Content Security Policy violation reports Hiding JSON-formatted data in the DOM with CSP enabled Loading JSON-formatted data with Ajax and xhr.responseType='json' Reserved keywords in JavaScript How to support full Unicode in MySQL databases How to speedrun Dropbox’s Dropquest 2012 Unquoted font family names in CSS Unquoted property names / object keys in JavaScript Valid JavaScript variable names in ES5 CSS character escape sequences JavaScript’s internal character encoding: UCS-2 or UTF-16? The smallest possible valid (X)HTML documents JavaScript character escape sequences JavaScript foo.prototype.bar notation Ambiguous ampersands HTML element + attribute notation Unquoted attribute values in HTML and CSS/JS selectors The end-tag open (ETAGO) delimiter Using the oninput event handler with onkeyup/onkeydown as its fallback Everything you always wanted to know about touch icons In defense of CSS hacks — introducing “safe CSS hacks” AirPlay video support in iOS Safari — a bookmarklet Completing Dropbox’s Dropquest 2011 in 60 seconds Using CSS without HTML How to create simple Mac apps from shell scripts Using setTimeout to speed up window.onload Bulletproof JavaScript benchmarks Thoughts on Safari Reader’s generated HTML How to enable Safari Reader on your site? The XML serialization of HTML5, aka ‘XHTML5’ The id attribute got more classy in HTML5 The three levels of HTML5 usage The HTML5 document.head DOM tree accessor Bulletproof HTML5 <details> fallback using jQuery Displaying hidden elements like <head> using CSS Inline <script> and <style> vs. external .js and .css — what’s the size threshold? Using Showdown/PageDown with and without jQuery
How I detect and use localStorage: a simple JavaScript pattern
Mathias · 2011-07-29 · via Mathias Bynens

Disclaimer: This is not a localStorage tutorial — see Chapter 7 of Dive Into HTML5 if that’s what you’re looking for. Read on if you’re still interested.

The conventional, old-school method

Here’s how you can detect localStorage support:

// Feature test
var hasStorage = (function() {
	try {
		localStorage.setItem(mod, mod);
		localStorage.removeItem(mod);
		return true;
	} catch (exception) {
		return false;
	}
}());

Note that accessing the global localStorage object may throw an exception, so it needs to be done inside a try-catch block for this test.

Modernizr does it this way (and documents the technique extensively in the source). It could be simplified a bit depending on the browsers and edge cases you wish to support, but for now this is the most robust feature detect out there.

With the above snippet, you can make use of localStorage in your code as follows:

if (hasStorage) {
	// Use `localStorage` here, e.g.
	localStorage.setItem('foo', 'bar');
	localStorage.lol = 'wat';
	localStorage.removeItem('foo');
}

Of course, this is nothing new. So why am I writing this? Well, I’ve been using a slightly different technique that has a few advantages, and I’d like to share it.

The new pattern

Let’s see what happens if we tweak the above script just a little bit:

// Feature detect + local reference
var storage = (function() {
	var uid = new Date;
	var result;
	try {
		localStorage.setItem(uid, uid);
		result = localStorage.getItem(uid) == uid;
		localStorage.removeItem(uid);
		return result && localStorage;
	} catch (exception) {}
}());

Compared to the previous snippet, not much has changed. There are three differences:

  • This snippet actually detects if localStorage works correctly, by checking if the value that is returned by getItem() is the same one that was set using setItem().
  • If the localStorage feature detect is successful, its result will be truthy. So we append && localStorage, which causes the storage variable to become a reference to the global localStorage object if the test was successful. In other words, if localStorage isn’t supported, storage === undefined, which is falsy. If it is supported, storage === window.localStorage, which is truthy.
  • Instead of returning false from the catch block, we return nothing at all. If an error is caught, storage will be undefined, which is falsy.

We could take it one step further and avoid the repeated scope lookups for localStorage (if it’s available) as follows:

// Feature detect + local reference
var storage = (function() {
	var uid = new Date;
	var storage;
	var result;
	try {
		(storage = window.localStorage).setItem(uid, uid);
		result = storage.getItem(uid) == uid;
		storage.removeItem(uid);
		return result && storage;
	} catch (exception) {}
}());

As Juriy “kangax” Zaytsev noted, it’s possible to avoid the anonymous function entirely:

// Feature detect + local reference
var storage;
var fail;
var uid;
try {
	uid = new Date;
	(storage = window.localStorage).setItem(uid, uid);
	fail = storage.getItem(uid) != uid;
	storage.removeItem(uid);
	fail && (storage = false);
} catch (exception) {}

It works just as well, and is even more efficient than the previous snippet because of the reduced number of function calls.

Anyhow, you can use any of these snippets like this:

if (storage) {
	// Use `storage` here, e.g.
	storage.setItem('foo', 'bar');
	storage.lol = 'wat';
	storage.removeItem('foo');
}

It couldn’t be simpler!

Useful?

I mostly like this pattern because of its elegance, but — assuming your code is inside its own scope to prevent leaking variables to the global scope — there are some (minor) performance benefits as well:

  • There’s only a single variable (storage) — and it can be used both to check if localStorage is supported and to manipulate it.
  • By using the local storage variable instead of the global localStorage object directly, scope lookups are kept to a minimum.
  • Consistently using a local variable that’s referencing localStorage results in a smaller file size after compression/minification.
  • It’s an edge case, but if you ever need to switch from localStorage to sessionStorage for your entire web app, you won’t even have to change your variable names. Just replace the one occurence of localStorage with sessionStorage and you’re done. (These APIs are identical, and even the feature detect can be done the same way.)

Other use cases

I’ve been using localStorage (and sessionStorage) in this example, but of course the exact same pattern could be applied in other cases as well. Can you think of other examples?