JavaScript本地存储(5) : 实际运用及总结

任何的技术如果不能运用到最终产品中那么只能永远是理论上的东西,前面我分了4篇介绍了JavaScript本地存储,下面应该介绍一下实际运用及代码整合了,为什么要整合呢,看过我前面文章的朋友应该知道有些功能是新版本浏览器才有的比如localStroage,有的功能是某些浏览器特有的比如IE专享的userData等,还有一些是功能上比较弱的,比如Cookie,它支持的存储空间就有限,如何才能统筹这些存储功能的资源,以及如何在一个功能不支持的情况下能另寻蹊径从而实现优雅的降级是我们这章要讨论的内容,我们要利用现有的本地存储的模式,做到尽可能的全兼容,最优兼容。

对于浏览器支持的存储模式我们可以采用一种线性探测,即以localStorage > globalStorage > userData > Cookie的优先级探测可支持的本地存储的模式,如何判断浏览器是否支持一种存储模式,我们可以修改一下先前提供的IStorage接口,接口改变如下:

1
2
3
4
5
6
var IStorage = function() {
  this.setItem = function(key, value) {}
  this.getItem = function(key) {}
  this.remove = function(key) {}
  this.isAvailable = function() {}
}

由此可见,我们增加了this.isAvailable的接口函数,顾名思义,这个接口函数的功能就是判断存储模式是否被支持的,那么先前讲解的Cookie、UserData、DOMStorage类就要实现这个接口了。具体实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var Cookie = function() {
  // 其余代码略
  this.isAvailable = function() {
     return !!navigator.cookieEnabled;
  }
}
 
var UserData = function() {
  // 其余代码略
  this.isAvailable = function() {
    return !!document.all;
  }
}
 
var DOMStorage = function() {
  // 其余代码略
  this.isAvailable = function() {
    return !!this.domStorage;
  }
}

对于判断Cookie是否开启,我们可以通过navigator.cookieEnabled这个属性,前面两个感叹号是将非0值、0值、NULL等强制转换为布尔值,因为我们规定isAvailable返回的是布尔值,即true or false。对于UserData的判断,由于是IE专有的属性,那么只要判断是不是IE即可,我们用到判断IE专用的document.all属性进行IE识别,DOMStorage由于初始化会进行自动赋值,如果localStorage或者globalStorage不存在的话this.domStorage将为null,所以我们只需要判断this.domStorage即可。

到这里我们接口也改良过了,下面到核心部分,就是灵活判断并选择最优的存储方案。当然我们需要用到上一章讲解的StorageFactory类工厂,算法实现如下:

1
2
3
4
5
6
7
8
9
var createStorage = function() {
  var storage,namelist = ['DOMStorage', 'UserData', 'Cookie'];
  for (var i=0; i<namelist.length; i++) {
    storage = StorageFactory.create(namelist[i]);
        if (storage.isAvailable())
          return storage;
  }
  return null;
}

然后我们就可以通过createStorage来实现创建本地存储,然后调用接口定义的setItem、getItem、remove等方法了,怎么样是不是很方便。

最后整合好的js文件在这里JStorage.js

JavaScript本地存储(3) : DOM Storage

上回我们在《JavaScript本地存储(2) : IE Only的userData》中提到了userData,当然这个存储方式只能支持IE系列的浏览器,从长远来看这种方法应该是被抛弃的,目前新技术发展很快,比如最新的HTML5技术,随之而来的是更为标准的JavaScript本地存储模式,那就是DOM Storage。更多的可以参考W3C的标准化文档《Web Storage》,其为我们提供了两种模式的本地存储,一个是sessionStorage,另一个是localStorage,今天我们先重点介绍localStorage。
同样的这两种存储模式都是基于标准的Storage接口的,其接口规范如下:

1
2
3
4
5
6
7
8
interface Storage {
  readonly attribute unsigned long length;
  DOMString key(in unsigned long index);
  getter any getItem(in DOMString key);
  setter creator void setItem(in DOMString key, in any value);
  deleter void removeItem(in DOMString key);
  void clear();
};

大家应该对这个非常熟悉,因为有些地方类似于我们在第一篇介绍的IStorage的接口方法,同样的基于这样的标准我们就可以很轻松的实现我们的IStorage接口了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var DOMStorage = function() {
  this.domStorage = null;
  this.domain = location.hostname;
  if (!this.domStorage) {
    this.domStorage = window.localStorage ||
            window.globalStorage &&
            window.globalStorage[this.domain];
  }
 
  this.setItem = function(key, value) {
    this.domStorage.setItem(key, value);
  }
 
  this.getItem = function(key) {
    return this.domStorage.getItem(key);
  }
 
  this.remove = function(key) {
    this.domStorage.removeItem(key);
  }
}

大家可能奇怪,为什么会出现window.globalStorage这样的东东,原来localStorage只是FireFox 3.5及以上版本支持,对于低于这个版本的FireFox浏览器,我们可以用globalStorage变通一下,当然更详细的信息我们可以参考Mozilla的官方文档《DOM Storage》。下面只列出globalStorage的域功能,globalStorage有个域标识,相当于访问权限控制,具体如下。

  • globalStorage[‘developer.mozilla.org’] 在developer.mozilla.org下面所有的子域都可以通过这个存储对象来进行读和写。
  • globalStorage[‘mozilla.org’] 在mozilla.org域名下面的所有网页都可以通过这个存储对象来进行读和写。
  • globalStorage[‘org’] 在.org域名下面的所有网页都可以通过这个存储对象来进行读和写。
  • globalStorage[”] 在任何域名下的任何网页都可以通过这个存储对象来进行读和写。

肯定会有朋友问,IE支持localStorage么,据我所知,目前也只是IE8及以上版本支持localStorage,那么低于IE8版本的可以用globalStorage?答案是否定的,globalStorage貌似只有火狐支持,那么低于IE8版本的IE浏览器就没有办法了吗?哈,你忘记了我先前介绍的userData的IE专用办法了,关键时刻还是可以请它来帮忙的。

下一篇我将重点介绍如何整合这些存储方式。哦,对了,差点忘了sessionStorage,这个好理解,存储操作方式和localStorage一样,只不过sessionStorage是浏览器对话模式的,也就是说你关了浏览器就什么都没有了。

最后给出篇参考文章:《跨浏览器的本地存储(二):DOM:Storage》

JavaScript本地存储(2) : IE Only的userData

上次我们提到了本地存储的一个方式,那就是Cookie,不过遗憾的是Cookie保存的数据量非常小,更详细的可以参考《在 Internet Explorer 中的 cookie 的数字和大小限制》,而且我们还要冒着用户禁用Cookie的风险,那么有没有变通的方法呢,微软为我们提供了一个类似的功能userData来帮助我们实现本地存储。

浏览器支持 : IE5.0 或以上

  • 基本语法 :
    XML: <Prefix: CustomTag id=sID style="behavior:url('#default#userData')" />
    HTML: <ELEMENT style="behavior:url('#default#userData')" id=sID>
  • Script:
    object.style.behavior = "url('#default#userData')"
    object.addBehavior ("#default#userData")
  • 属性:
    expires 设置或者获取 userData behavior 保存数据的失效日期。
    XMLDocument 获取 XML 的引用。
  • 方法:
    getAttribute() 获取指定的属性值。
    load(object) 从 userData 存储区载入存储的对象数据。
    removeAttribute() 移除对象的指定属性。
    save(object) 将对象数据存储到一个 userData 存储区。
    setAttribute() 设置指定的属性值。

要使用userData存储功能,必须先建立一个HTML标签,然后将behavior:url('#default#userData')样式属性加上去,等于说userData是寄存于HTML标签的,当然不是所有标签都是可以的,仅限于部分标签。要了解更多的信息可以访问MSDN的《userData Behavior》

下面我们基于先前讲解的IStorage接口,实现UserData这个类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
var UserData = function() {
  this.userData = null;
  this.name = location.hostname;
  //this.name = "wangye.org";
 
  if (!this.userData) {
    try {
      this.userData = document.createElement('INPUT');
      this.userData.type = "hidden";
      this.userData.style.display = "none";
      this.userData.addBehavior ("#default#userData");
      document.body.appendChild(this.userData);
      var expires = new Date();
      expires.setDate(expires.getDate()+365);
      this.userData.expires = expires.toUTCString();
    } catch(e) {
    }
  }
 
  this.setItem = function(key, value) {
    this.userData.load(this.name);
    this.userData.setAttribute(key, value);
    this.userData.save(this.name);
  }
 
  this.getItem = function(key) {
    this.userData.load(this.name);
    return this.userData.getAttribute(key);
  }
 
  this.remove = function(key) {
    this.userData.load(this.name);
    this.userData.removeAttribute(key);
    this.userData.save(this.name);
  }
}

在这里我有必要说明一下,this.name这里指定了userData存储文件的文件名,这里我们指定为location.hostname,不过大家在本地测试时可能会遇到location.hostname为空的情况,这样会导致下面的脚本执行出错,因为不能指定一个空文件名,这时我们可以硬编码个文件名供我们测试。接下来我们通过createElement动态创建一个input元素作为我们userData的宿主,然后设置过期时间,这点类似于Cookie的过期时间,我们设置为当前时间+365天。接下来就可以通过load加载,然后对键值进行相关操作,然后再save了。

关于UserData这个类的具体操作和先前讲的Cookie一样,这里就不举例了。

JavaScript本地存储(1) : 使用Cookie

在网页开发中,为了某些特殊的效果比如像购物车、页面换肤、记住密码等等就可能需要用到JavaScript的本地存储功能,说到底JavaScript的本地存储就是让网站能够“记住”你,等到下次再访问的时候能够识别出来,当然记住的方式无外乎就是通过在浏览者的电脑上保存一个记录的文件。本系列文章主要讲解这些技术,当然我们实现约定一个类似于下面的接口:

1
2
3
4
5
var IStorage = function() {
  this.setItem = function(key, value) {}
  this.getItem = function(key) {}
  this.remove = function(key) {}
}

下面的编程实现将围绕在这个接口展开,关于为什么定义这个接口,我将在下面的文章中阐述。

说到Cookie(也可以叫做Cookies)大家可能比较熟悉,这可以是最早实现本地存储的一个方法之一了,实现也非常简单。基于IStorage我们实现了下述关于Cookie类的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
var Cookie = function() {
  this.setItem = function(key, value) {
    var expires = new Date();
    expires.setTime(expires.getTime() + 
            1000 * 60 * 
            60 * 24 * 365);
 
    document.cookie = key + "=" + 
            escape(value) + 
            ";path=/" + 
            "; expires=" + 
            expires.toGMTString();
  }
 
  this.getItem = function(key) {
    var key_equal = key + "=";
    if (document.cookie.length <= 0)
      return "";
    var pos_start = document.cookie.
            indexOf(key_equal);
    if (pos_start == -1)
      return "";
    pos_start += key_equal.length;
    pos_end = document.cookie.
          indexOf(";", pos_start);
    if (pos_end == -1)
      pos_end = document.cookie.length;
 
    return unescape(document.cookie.
            substring(pos_start, pos_end));
  }
 
  this.remove = function(key) {
    this.setItem(key, "");
  }
}

可以看出每个Cookie由key和value这样的键值对组成,而且在这之间以分号相隔,形如key1=value1;key2=value2;等等,当然操作cookie可以通过document.cookie进行,然后我们所做的其实是文本分析,比如如何根据key获得等于号后面的value。

好了,下面的一篇文章我将会给大家展示一个例子,讲解Cookie的使用方法。