关于javascript里面数组的sort方法

介绍

Array.sort([compareFunction])

对Array里面的元素进行升序排序

关于排序的规则

默认的排序

如果compareFunction没有提供的时候,默认把所有Array里面的元素转换为字符串,然后取第一个字符,比较它们的Unicode值,进行正序排序。

这个转为字符串再比较第一个字符的Unicode值,很关键,很多数字的比较,经常就是这样出错的。


[1,2,3].sort();  // ==> [1,2,3]  正确

[9, 80].sort();  // ==> [9, 80]  错误

[9, 80].sort();  // ==> [80, 9]  正确

上面例子中,数组[9, 80]进行sort的时候,先转为为字符串 “9”, “80”, 再比较第一个字符, “9”和”8″

根据这个比较结果进行排序,所以最后80会在9前面,而不是按照它们的数值大小进行排序的。

想自行测试的,可以看看这个demo(http://www.icondownloader.com/demo/sort-stable.html]

compareFunction的排序

进行sort的时候,如果compareFunction提供了,会往compareFunction传两个数组的元素(类似compareFunction(a, b))

取compareFunction的返回值,如果小于0,则表明a小于b;大于0,则a大于b

最后对结果进行升序排序,如果a小于b,则[a, b]; 如果a大于b 则[b, a]

这里有个技巧,在排序的时候,如果想实现降序排序,可以在compareFuction里面实现类似:return !(a-b);

sort stable

关于排序,有个stable的说法,也就是,排序完成之后,对于值相同的元素,在排序结束之后,能否保留其在排序之前的顺序。

sort stable

具体的概念可以参考维基百科https://en.wikipedia.org/wiki/Sorting_algorithm#Stability

目前的主流浏览器对stable的sort的支持情况如下:

Browser Sort is stable Sort is unstable
Firefox 8 all lengths
Safari 5.1.1 all lengths
Opera 11.52 all lengths
Internet Explorer 6, 7, 8 all lengths
Internet Explorer 9 all lengths
Android 2.3 Browser all lengths
Chrome 15 .. 17.0.942.0 length <= 10 length > 10

可以参考此网站,检测浏览器的sort情况http://ofb.net/~sethml/is-sort-stable.html

sort的效率

可以尝试着对compare function在执行的时候,进行一个计数。这样可以知道每次sort,总共执行了多少次compare function。

我这边写了个demo,有兴趣的可以在自己本机的各个浏览器上测试看看。http://www.icondownloader.com/demo/sort-compare-function-call-times.html

这边我贴出我自己Mac OSX Yosemite 10.10.4下的浏览器的运行结果

## chrome 版本 42.0.2311.135 (64-bit)
Array length is: 5 and sort call compare 7 times;
Array length is: 10 and sort call compare 23 times;
Array length is: 11 and sort call compare 22 times;
Array length is: 12 and sort call compare 19 times;
Array length is: 13 and sort call compare 27 times;
Array length is: 14 and sort call compare 25 times;
Array length is: 15 and sort call compare 24 times;
Array length is: 20 and sort call compare 29 times;
Array length is: 50 and sort call compare 80 times;
Array length is: 100 and sort call compare 206 times;
Array length is: 1000 and sort call compare 1996 times;
Array length is: 10000 and sort call compare 20206 times;

## Safari 版本 8.0.7 (10600.7.7)
Array length is: 5 and sort call compare 8 times;
Array length is: 10 and sort call compare 23 times;
Array length is: 11 and sort call compare 28 times;
Array length is: 12 and sort call compare 31 times;
Array length is: 13 and sort call compare 35 times;
Array length is: 14 and sort call compare 40 times;
Array length is: 15 and sort call compare 44 times;
Array length is: 20 and sort call compare 65 times;
Array length is: 50 and sort call compare 232 times;
Array length is: 100 and sort call compare 568 times;
Array length is: 1000 and sort call compare 8996 times;
Array length is: 10000 and sort call compare 123583 times;

## Firefox (Developer Edition) 40.0a2 (2015-05-29)
Array length is: 5 and sort call compare 6 times;
Array length is: 10 and sort call compare 26 times;
Array length is: 11 and sort call compare 24 times;
Array length is: 12 and sort call compare 33 times;
Array length is: 13 and sort call compare 48 times;
Array length is: 14 and sort call compare 48 times;
Array length is: 15 and sort call compare 37 times;
Array length is: 20 and sort call compare 70 times;
Array length is: 50 and sort call compare 245 times;
Array length is: 100 and sort call compare 574 times;
Array length is: 1000 and sort call compare 8600 times;
Array length is: 10000 and sort call compare 114046 times;

从上面的数据可以看出两个结论:

  1. chrome的执行效率明显高于Safari和Firefox。但是chrome的sort是unstable的,而Safari和Firefox是stable的。是不是可以认为,因为chrome不需要考虑stable,所以提高了执行效率。

  2. 可以看出随着数组长度的增加,比较的次数是指数增加的

用map来提高提高sort的性能

从上面的结论2可以知道,在数组长度很长的时候,compareFunction的调用次数是很多的,这个时候,提高compareFunction的效率就很有必要性了。

现在咱们构建一个数组

var arr = [],
    arrLen = 1000,
    i = 0;

function makeWord(){
    var word = [],
        words = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'],
        wordsLength = words.length,
        arrMinLen = 3,
        arrLen = arrMinLen + Math.floor( Math.random()*9 ),

        i = 0;

    for (i = 0; i < arrLen; i++){
        word.push( words[Math.floor( Math.random()*wordsLength )] );
    }

    return word.join('');
}

for (i = 0; i < arrLen; i++) {
    arr.push( makeWord() );
}

然后比较一下它们小写字母状态下的排序

// 未优化
arr.sort(function(a, b){
    return +(a.toLowerCase() > b.toLowerCase()) || +(a.toLowerCase() === b.toLowerCase())-1;
});

// 优化后

// 用一个临时数组来保存位置和计算后的数值
var mapped = list.map(function(el, i) {
  return { index: i, value: el.toLowerCase() };
})

// 排序这个已经计算后的临时数组
mapped.sort(function(a, b) {
  return +(a.value > b.value) || +(a.value === b.value) - 1;
});

// 根据位置信息 对应映射生成一个排序后的数组
var result = mapped.map(function(el){
  return list[el.index];
});
// 不支持map的时候的兼容方法

var tmpArr = [],
    result = [],
    i = 0,
    len = arr.length;

for (i = 0; i < len; i++) {
    tmpArr[i] = {index: i, value: arr[i].toLowerCase()};
}

tmpArr.sort(function(a, b) {
  return +(a.value > b.value) || +(a.value === b.value) - 1;
});

for (i = 0; i < len; i++) {
    result[i] = arr[tmpArr[i].index];
}

具体的执行效果可以看看jsperf里面的这个地址http://jsperf.com/arraysortperform

参考文章:
+ https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
+ https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Unicode
+ https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#String_literals
+ http://ofb.net/~sethml/is-sort-stable.html
+ http://stackoverflow.com/questions/3026281/array-sort-sorting-stability-in-different-browsers

React入门实例教程读书笔记

这份读书笔记是看了阮一峰的《React 入门实例教程》之后撰写的,留作记录。需要看原文的可以点击前面的书名访问对应链接。

这篇文章的目的

  • 介绍react
  • 如何下载react
  • 如何安装react
  • 如何使用react

文章的组织架构

从易到难的开始介绍。

react在页面的部署

  1. 依赖的两个js
  2. jsx 模板的标签规范
  3. 模板的编译应该放到服务器完成

模板语法

  1. React.render() 转换模板为HTML并插入节点
  2. 允许HTML和javascript混写。
    1. 规则是:遇到HTML标签(以<开头)则视为HTML,遇到代码块(以{)开头,视为javascript
    2. HTML内容可以直接写在javascript变量里
    3. javascript变量可以直接插入到模板。如果变量是数组,则自动展开这个数组的所有成员

组件

组件介绍

  1. 用React.createClass来生成组件类
  2. createClass的传参对象,需要一个render方法,用来描述组件的渲染
  3. 组件类以大驼峰方式书写
  4. 组件类以模板的方式调用,写法可以有单标签和双标签两种
  5. 组件类以模板的方式调用,实际是实例化了产生了一个对象,以下简称为组件
  6. 组件的调用和HTML标签完全一致,能加入属性。属性的调用,可以通过组件类的实例在javascript中,以this.props.xxx的方式调用
  7. 组件的style属性设定的时候,期望的值是一个对象{opacity: 0.5, marginLeft: ‘1px’} 而不是一个字符串,最终的写法是{{opacity: 0.5, marginLeft: ‘1px’}} 外面一对大括号表明是javascript代码块,里面的一对大括号,表明是对象
  8. 冲突的属性名class和for 要对应改为className和htmlFor,因为class和for是javascript的保留字
  9. this.props的属性和组件HTML属性一一对应,但是this.props.children例外,它表示组件的子节点
  10. 在用双标签的方式调用组件的时候,标签之间的HTML即为组件的子节点,完全和原生HTML一致

组件的特性

  1. findDOMNode
    1. virtual DOM
    2. virtual DOM 通过 DOM diff算法映射到真实DOM上
    3. findDOMNode用于获取跟组件关联的真实DOM的节点
    4. 只有在virtual DOM插入文档之后才能起作用
  2. this.state
    1. 类似状态机的概念
    2. getInitialState方法来设定初始状态
    3. 通过用户互动修改状态,一般是对应的event function来setState
    4. render里面,根据不同的状态渲染出不同的UI
    5. setState自动调用render重绘UI
    6. 区分this.props和this.state: this.props类似常量,设定之后不改变;this.state是随着用户互动而变化的特性
    7. 表单内容的变化也是通过state来进行更新的

组件生命周期

  1. 三个状态
    1. Mounting 已插入真实DOM
    2. Updating 正在被重新渲染
    3. Unmounting 已移出真实DOM
  2. 每个状态提供两个回调函数,will在进入状态之前调用,did在进入状态之后调用
  3. 三个状态共计五种回调函数,书写方式component是前缀,will/did是行为,Mount/Update/Unmount是状态
    1. componentWillMount()
    2. componentDidMount()
    3. componentWillUpdate(object nextProps, object nextState)
    4. componentDidUpdate(object prevProps, object prevState)
    5. componentWillUnmount()   为什么没有componentDidUnmount() ?
  4. 特殊状态的回调
    1. componentWillReceiveProps(object nextProps) 已加载组件收到新参数
    2. shouldComponentUpdate(object nextProps, object nextState) 组件判断是否重新渲染时调用 不明白

结合其他库或者框架使用

  1. react没有任何依赖
  2. react只关注表现层
  3. 如果希望对组件的DOM进行操作,尽量在componentDidMount中进行,保证真实DOM已经存在

主旨

React是一个关注表现层,以组件来构建内容,以状态机来完成交互的UI改变的,可以在浏览器端和服务器端同时运行的前端框架。

选择器优化

翻译自:http://learn.jquery.com/performance/optimize-selectors/

在越来越多的浏览器支持document.querySelectorAll()之后,使得查找DOM的压力从jQuery转移到浏览器,选择器的优化,已经没有以往那么重要。然而,仍然有一些小技巧要记住。

基于ID的选择器(ID-Based Selectors)

基于ID进行查找永远是最好的方法。

// 快速:
$( "#container div.robotarm" );
 
// 更加快速:
$( "#container" ).find( "div.robotarm" );

使用.find()的方法更快,因为第一个选择器是没法脱离Sizzle选择器引擎的处理 – 而只有一个ID不包含其他类型的选择器,是直接由document.getElementById来处理的,这个是原生方法,超级快

特异性(Specificity)

把更特异的选择器放右侧,更简单的选择器放左侧。(Sizzle选择器在某些情况下,是会从右往左进行查找,这个时候,右侧的特异性,使得查找出来的结果集很小,然后再往上遍历查找符合条件的父级,效率就会很高 — 编者译)

// 没优化的:
$( "div.data .gonzalez" );
 
// 优化了的:
$( ".data td.gonzalez" );

避免过度特异性(Avoid Excessive Specificity)

$( ".data table.attendees td.gonzalez" );
 
// 更好的: 如果可以尽量去除中间的多余的条件
$( ".data td.gonzalez" );

一个轻简的DOM有助于提升选择器的性能(原文是:A “flatter” DOM also helps improve selector performance,不太好翻译),因为这样可以在查找dom的时候,减少需要检索的层级

避免全部选择器(Avoid the Universal Selector)

如果明确的或隐晦的的全部选择器(结果如果是会匹配绝大多数的DOM),这样的选择器会非常的缓慢

$( ".buttons > *" ); // 非常耗性能
$( ".buttons" ).children(); // 更好的方式
 
$( ".category :radio" ); // 隐晦的全部选择器
$( ".category *:radio" ); // 和上面的效果一样,只是更加明确
$( ".category input:radio" ); // 更好的写法

javascript漂移广告效果

今天要帮人实现一个可以在窗口上飘来飘去的广告,虽然对这类漂浮移动的广告很不感冒,但是写还是得写的。

原理知道,但是自己写,总是麻烦,就去网上找了个demo,然后再基于那个demo做了点修改。

话不多说,上代码:




    
    Document
    


效果看这里:

主要的修改有如下几点:

  • requestAnimationFrame — 使得在高级浏览器下的性能呢更好
  • document.documentElement.clientHeight/clientWidth — W3C标准的获取窗口尺寸的方法
  • 数据缓存 把部分固定不变的变量移动到动画主函数之外,避免多余的计算消耗和DOM查找

Bootstrap的Carousel实现垂直滚动效果

介绍了实现bootstrap里面carousel的垂直滚动的方法

Bootstrap的Carousel默认是水平滚动的,下面介绍实现其垂直滚动的方式,看代码:

html结构,相对于bootstrap,新增了vertical的class



css样式,设定.carousel.vertical 对应的一些控制class的效果和动画

.vertical .carousel-inner {
  height: 100%;
}

.carousel.vertical .item {
  -webkit-transition: 0.6s ease-in-out top;
     -moz-transition: 0.6s ease-in-out top;
      -ms-transition: 0.6s ease-in-out top;
       -o-transition: 0.6s ease-in-out top;
          transition: 0.6s ease-in-out top;
}

.carousel.vertical .active {
  top: 0;
}

.carousel.vertical .next {
  top: 100%;
}

.carousel.vertical .prev {
  top: -100%;
}

.carousel.vertical .next.left,
.carousel.vertical .prev.right {
  top: 0;
}

.carousel.vertical .active.left {
  top: -100%;
}

.carousel.vertical .active.right {
  top: 100%;
}

.carousel.vertical .item {
    left: 0;
}​

js代码,设定滚动的间隔

$('.carousel').carousel({
  interval: 3000
})

以上就可以顺利实现一个垂直滚动的Carousel

参考网址:

twitter-bootstrap-carousel-vertical-sliding

Carousel vertical Bootstrap slider

ECMAScript5 中对象属性的新特性

介绍了ECMAScript 5中关于对象属性的新特性,分别给出了在ECMAScript3和ECMAScript5中,设定只读属性的方法

ecmascript5-defineProperty

在ECMAScript3中,对象的属性是很简单的,可读可写可遍历,也不存在公有私有的说法。

为了实现只读变量,很多时候不得不依赖闭包。


var o = (function(){
    var readOnlyVarible = '我是只读的';
        
    return {
        getReadAbleVarible = function(){
            return readOnlyVarible;
        }
    };
});


在ECMAScript5中,针对对象的属性,新增三种描述writable(可写性),enumerable(可枚举性)和configurable(可配置性),还有一个是跟早期的描述等同的value(属性值)

下面是摘自MDN中关于这四个属性描述

configurable
当且仅当这个属性描述符值为 true 时,该属性可能会改变,也可能会被从相应的对象删除。默认为 false
enumerable
true 当且仅当该属性出现在相应的对象枚举属性中。默认为 false
数据描述符同时具有以下可选键值:
value
与属性相关的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined
writable
true 当且仅当可能用 赋值运算符 改变与属性相关的值。默认为 false

上述的默认值是指的用Object.defineProperty()方法创建属性的时候的默认值,而不是通常那种直接量或者.的方式创建的属性的默认值,使用后者创建属性的时候,三个bool型的描述符的值都是true。

有了这新增的三个描述符之后,在创建只读属性的时候就方便多了


var o = {};

Object.defineProperty(o,'readOnlyVarible',{
    writable: false,
    enumberable: true,
    configurable: false, // 防止修改可写性
    value: '我是只读的'
});


或者下面这种方式(把一个已经定义的属性设置为只读的)


var o = {};

o.readOnlyVarible = '我是只读的'

Object.defineProperty(o,'readOnlyVarible',{
    writable: false,
    enumberable: true,
    configurable: false // 防止修改可写性
});


编写可维护的JavaScript-语句和表达式

关于for-in循环的一些使用上应该注意的细节

matainable javascript : For-In 循环

本系列的文章都是在阅读《编写可维护的JavaScript》——Nicbolas C. Zakas 的基础上做的一些个人总结

for-in 循环

### 遍历对象

对对象使用for in循环的时候,一般情况下,都应该使用hasOwnProperty()方法来进行过滤:



var prop,
    obj = {
        a: "aaa",
        b: "bbb"
    };

for (prop in obj) {
    if (obj.hasOwnProperty(prop) {
        console.log("Property name is " + prop);
        console.log("Property value is " + obj[prop]);
    }
}


如果的确需要查询原型链,这个时候应当补充注释

var prop,
    obj = {
        a: "aaa",
        b: "bbb"
    };

for (prop in obj) { // 包含对原型链的遍历
    console.log("Property name is " + prop);
    console.log("Property value is " + obj[prop]);
}

### 不要使用for in 来遍历数组


// 不好的用法
var values = [ 1, 2, 3, 4, 5, 6, 7],
    i;
    
for (i in values) {
    console.log(values[i]);
}


不使用for in来遍历数组,是因为这样会造成一些潜在的错误,同时会有性能上面的影响。

潜在的错误:


var arr = [ 'a', 'b', 'c'],
    i;

arr.test = 'ddd';

for ( i in arr ){
    console.log(i,arr[i]);
}

/** 输出内容
 * 0 a 
 * 1 b 
 * 2 c 
 * test tset 
 * /


可见for in循环会把用户附加在数组上的一些自定义属性输出,这很可能会导致一些潜在的错误,因为这段代码的作者的目的是需要的数组成员的数据

性能问题(摘自JavaScript秘密花园):

由于 for in 循环会枚举原型链上的所有属性,唯一过滤这些属性的方式是使用 hasOwnProperty 函数, 因此会比普通的 for 循环慢上好多倍。

做个测试:


var arr = [],
    i,
    a,
    max = 100000,
    timeStart,
    timeEnd;

// 生成一个长度100000的数组
for (i = 0; i < max; i++) {
    arr[i] = i;
}

/**
 * for 循环
 */
timeStart = new Date().valueOf();
for (i = 0; i < max; i++) {
    // 无意义的一些操作
    a = arr[i];
}
timeEnd = new Date().valueOf();

console.log(timeEnd - timeStart);

/**
 * for in 循环
 */
timeStart = new Date().valueOf();
for (i in arr) {
    // 无意义的一些操作
    a = arr[i];
}
timeEnd = new Date().valueOf();

console.log(timeEnd - timeStart);



在数组长度比较小的时候,两者差距不大
甚至有的时候for in还会快一点
但是随着数组长度越来越大,两者的差距也就越来越明显

下面是一串本机的测试数据(Mac Chrome 37.0.2062.122)

数组长度 for for-in
10 0 0
1000 0 1
10000 1 6
100000 2 46
1000000 24 331
10000000 207 4362

虽然在实际项目中,数组的长度不会那么长,但是能提高一点效率总是好的

各种刷新页面的方法

详细列举了几种刷新页面的方式,解析其中的原理和推荐的方式

很多时候,我们因为一些原因,要对页面进行刷新,这个时候,可以使用的方法有很多



location.reload();

location.href = location.href;

window.location = location;

location.replace( location.href );

location.go(0);


关于浏览器缓存和服务器获取

用户浏览网页的时候,在当前标签页内进行各种页面跳转的时候,浏览器会缓存浏览过的页面。

在进行页面跳转的时候,有两种方式:

一种是在历史记录内进行跳转,这种跳转会由浏览器读取缓存的页面数据进行展示。

还有一种是跳出历史的跳转,直接指向路径的url,这种跳转是会从服务器获取数据展示的。

如何查看是否读取的缓存

以chrome的开发者工具为例,打开之后,查看network面板,清空当前的网络请求列表。

在console里面执行相应的方法,然后查看当前页面的请求(也就是xxx.html),

如果size/content列下显示的是from cache就表明是读的缓存(进行这个实验的时候应该关闭开发者工具里面的禁止缓存选项)

各种跳转的详细情况

location.reload

这个方法有个Boolean值的参数,为true的时候,则刷新就始终是从服务器获取最新的数据。

如果不设定或者设定为false的时候,则是从浏览器的缓存里面获取数据

chrome里面reload的默认参数似乎是true,也就是说会从服务器端获取

所以使用reload的时候,最好明确添加reload的参数

参考地址:https://developer.mozilla.org/en-US/docs/Web/API/Location.reload

location.href = location.href

把location.href指向当前页地址就会触发重新刷新页面。

不过这种方法不太稳定,在chrome和firefox下面,第一次执行的时候会从服务器端载入当前页面,之后再次执行的时候就从浏览器缓存读取,所以最好不要轻易使用

window.location = window.location

window.location的赋值会导致页面的重载把window.location赋值给window.location,使得重载的页面再次指向当前页面,也就变相的实现了刷新页面。

这种方式就目前测试读取的是浏览器缓存里面的内容

location.replace

location.replace会用指定的url里面的内容替换当前页的内容,这个会导致用户无法后退回到之前的页面,回到的是之前页面的前一个历史。

chrome下会始终从缓存中读取。

firefox下,第一次从服务器获取,之后从缓存中读取。

参考地址:https://developer.mozilla.org/en-US/docs/Web/API/Location.replace

history.go(0)

history.go是会跳转向对应的历史,所以很明显是一定会从缓存中读取的。

使用参数0,表示的是跳转的历史就是当前页,也就实现了刷新页面。

参考地址:http://www.w3schools.com/jsref/met_his_go.asp

总结

综上,在刷新页面的时候,如果需要考虑从服务器获取还是读取缓存,比较靠谱的是location.reload()和history.go(0)

参考地址:

http://hi.baidu.com/winterhome/item/73c26920b3faa10a76272c97

http://stackoverflow.com/questions/1536900/force-a-page-refresh-using-javascript-in-firefox

被flash执行的js方法,书写的时候,需要注意的

特别是在IE6下

字符串的拼接

不要用var str = “aaa”+”bbb” 会报错

arr.push arr.join 测试也是会报错

推荐 var str = ”;str += ‘aaa’; str += ‘bbb’;

不要注释

注释会影响解析,可能导致 缺少’}’ 之类的bug

关于返回值

不要类似直接return ‘a’ 这样直接返回一个临时创建的字符而应该是 var str = ‘not’; return not;

这些说法没有严谨的进行验证,只是在最近和flash进行交互的时候,在ie下遇到错误,总是查找不到,最后一点点的回滚代码测试错误的时候,发现的。也没法描述其中的原理是什么。仅供参考

2014年6月5日补充

以上情况只适用于这种情况:使用swfobject.js来加载flash,会以flashvar的形式给flash传递一个js的回调函数。出问题的原因是:swfobject.js会把这个js的回调函数的代码复制然后写入标签的flashvar属性中。这种复制会导致下面的问题。

userData的一些坑

介绍了下在应用userData进行本地存储的实现是,遇到的一些问题,包括document limit和domain limit的区别、按文档路径进行区分的数据隔离方式、必须在document.ready之后执行、缺少销毁全部数据和遍历全部数据的接口。

最近在做兼容多浏览器能跨域访问的本地存储实施方案。一开始打算是用localStorage加userData的方式实现,支持localStorage的使用localStorage,不支持的(IE6,7)使用userData;本域下的直接调用,需要跨域的则加载对应域名下的crossdomain.html作为iframe,然后调用iframe里面的contentWindow来实现。

想法是很好的,也实现了很多功能,但是在实现之后的的评审和准备应用阶段,却发现了很多开发的时候没有注意到的问题。

Document Limit/Doamin Limit

可以看看下面的表格,在每个域名下的存储限制是1M,这个很多很令人满意,但是还有个Document Limit在当初使用的时候却忽略了。userData的实现方式就是加载一个放在用户文件夹下的XML文件,在里面进行I/O操作。这每一个XML文件就是一个Document。而操作的时候,必须明确指定加载哪个XML文件。这就和localStorage的实现方式上有了很大的差别,不利于统一。而且单个Document的大小是128KB,这个很难保证后期能否足够使用。

Security Zone Document Limit (KB) Domain Limit (KB)
Local Machine 128 1024
Intranet 512 10240
Trusted Sites 128 1024
Internet 128 1024
Restricted 64 640

same directory and same protocol to access

上面那个文档的大小限制和明确指定加载的XML文件,还不算很坑的。因为短时间内128KB肯定是足够使用的,也可以在实施方案里面统一各域名下加载同一个名称的XML就好了。也就是看起来像每个域名下只有128kb的存储空间。

可是下面这个限制就很坑了。

对于userData,数据之间的划分,不单单是域名,协议和端口号这三个。它还更加严格的以文档路径进行划分,对的,不用怀疑:http://www.demo-domain.com/test/index.html是没法访问到http://www.demo-domain.com/test2/index.html里面的userData数据的。

当然,这个也能绕开,就是在使用userData的情况下,所有的I/O操作都是走的iframe,哪怕是同域名下。然后给所有域名的根域放置一个crossdomain.html。但是这样的实施方法确实有点复杂

can’t be set/get in <head>

接着还有问题,这个问题是个使用上的限制。在公司的应用环境里,有些情况,需要在加载head的时候,就能读到本地的数据,然后做一个是否跳转的判断。而userData绑定的标签是不包括head里面能放置的那些。所以很不幸userData无法实现

而且通常的使用方式是认为的createElement,然后在这个element上面进行文档的绑定。而在IE6下,向文档插入dom是必须要在document.ready之后进行的,不然会容易出现问题

can’t clear and Traversal(like function key in localStorage)

最后一个问题,在实施方案里面,公开的接口都是仿造localStorage的接口。其中有两个接口clear(清空所有内容)和key(遍历本地数据)。这两个接口使用的场景不多,但是肯定是会有使用到的时候,可惜userData的官方文档里面并没有看到有这相关的接口提供。

所以,因为上面这么多的问题,只好忍痛放弃已经开发完的localStorage和userData的组合,改为使用localStorage和FlashCookie的组合。