分享

Javascript – Arraylike的7种实现

 haosunzhe 2015-03-27
(点击上方蓝字,可快速关注我们)

jQuery的崛起让ArrayLike(类数组)在javascript中大放异彩,它的出现为一组数据的行为(函数)扩展提供了基础。


类数组和数组相似,具有数组的某些行为,但是它相比数组可以更加自由的扩展,它的存在让一组数据的表现不再受限于数组,也无需去污染数组本身的原型——它来自javascript对象的挖掘和扩展,而并非javascript本身就存在的。简单的说,它来自数组,比数组更加适合扩展。


这篇文章主要分为以下知识


  • 锋芒毕露的ArrayLike

  • ArrayLike的实现

  • 其他


锋芒毕露的ArrayLike


如果你已经了解了ArrayLike,这一节可以略过。


ArrayLike(类数组/伪数组)即拥有数组的一部分行为,在DOM中早已表现出来,而jQuery的崛起让ArrayLike在javascript中大放异彩。正如它的翻译一样:它类似于数组。


ArrayLike对象的精妙在于它和javascript原生的Array类似,但是它是自由构建的,它来自开发者对javascript对象的扩展,也就是说:对于它的原型(prototype)我们可以自由定义,而不会污染到javascript原生的Array。


过去针对一组数据的扩展是下面这个样子的:


//污染Array实现扩展

Array.prototype.demo = function () {

//check

};

var test = [];

test.demo();


上面代码你们懂的,污染了Array,在协同式开发中这简直就是作孽啊——ArrayLike应此诞生。


ArrayLike让你对一组数据的扩展不再受限于Array本身,同时也不会影响到Array,说白了就是:一组数据,肯定是有数组来存,但是如果要对这组数据进行扩展,会影响到数组原型,ArrayLike的出现则提供了一个中间数据桥梁,ArrayLike有数组的特性, 但是对ArrayLike的扩展并不会影响到原生的数组。举个栗子:


爸爸妈妈对你期望很高,你要好好学习,但是舍友基佬教会了你打dota,整天拉你打dota让你没时间看书学习,结果呢,就是打得一手好dota学习掉下去了——但是如果,你开了分身斧,让你的分身去打dota,你自己仍然好好学习,dota学习两不误,而且你的分身不仅仅可以打dota,也可以去打wow,把妹,做你做不到的事情,是不是觉得这样不就碉堡了么!!!


没错,ArrayLike就是要干这么碉堡的事情。


常见的ArrayLike有下面这几个,详见:其他。


  • Arguments

  • NodeList

  • StyleSheetList

  • HTMLCollection

  • HTMLFormControlsCollection (继承HTMLCollection)

  • HTMLOptionsCollection(继承HTMLCollection)

  • HTMLAllCollection

  • DOMTokenList


ArrayLike的实现


第一种 – 通过闭包实现:


通过闭包实现,内部采用一个Array作为基础,API是针对数组进行操作,在API的实现上较差。并且不支持直接通过索引(array[0])来访问元素,通过闭包实现上会丢失instanceof的判定,优点是够轻。


!function () {

//通过闭包实现

var List = function () {

var list = [],

self = {

constructor: List,

//如果希望更像原生一点,将length定义为属性,那么length则需要自己维护

length: function () { return list.length; },

add: function (item) {

list.push(item);

},

eq: function (index) {

return list[index];

}

};

return self;

};

//测试

console.group('第一种 - 通过闭包实现');

var demo = new List();

demo.add('List - add()');

console.log('demo instanceof List : %c' + (demo instanceof List), 'color:red;');

console.log('demo.constructor === List :%c' + (demo.constructor === List), 'color:blue');

//无法通过索引demo[0]这种方式访问

console.log('成员:[ ' + demo.eq(0) + ' , ' + demo.eq(1) + ' ]');

console.log('length:' + demo.length());

//注意看demo对象

console.log(demo);

console.groupEnd();

}();


运行结果和demo对象结构:



第二种 – 通过继承实现:


主要亮点(应用)在保留Array的API,在Array上二次封装,可以通过索引来访问。


!function () {

//通过继承数组实现,数组原生方法会被继承过来

var List = function () { };

List.prototype = [];

List.prototype.constructor = List;

List.prototype.add = function (item) {

this.push(item);

};

//测试

console.group('第二种 - 通过继承实现');

var demo = new List();

//源于继承

demo.push('Array - push()');

demo.add('List - add()');

console.log('demo instanceof List : %c' + (demo instanceof List), 'color:blue;');

console.log('demo.constructor === List :%c' + (demo.constructor === List), 'color:blue');

console.log('[ ' + demo[0] + ' , ' + demo[1] + ' ]');

console.log('length:' + demo.length);

//注意看demo对象

console.log(demo);

console.groupEnd();

}();


运行结果和demo对象结构:



第三种 – 通过自我维护实现:


在增删改上需要自我维护length,相比下来很是折腾和繁琐,只是提供一种代码思路,并不提倡,可以通过索引访问,


!function () {

//通过自动维护length实现

var List = function () {

this.length = 0;

};

List.prototype.add = function (item) {

//让对象模拟Array的行为

this[this.length++] = item;

};

console.group('第三种 - 通过自我维护实现');

var demo = new List();

demo.add('List - add()');

console.log('demo instanceof List : %c' + (demo instanceof List), 'color:blue');

console.log('demo.constructor === List :%c' + (demo.constructor === List), 'color:blue');

console.log('[ ' + demo[0] + ' , ' + demo[1] + ' ]');

console.log('length:' + demo.length);

//注意看demo对象

console.log(demo);

console.groupEnd();

}();


运行结果和demo对象结构:



第四种 – 针对第一种优化:


在add中通过Array原生的APIArray.prototype.push来实现,原理是只要调用过Array原生的增删改API操作函数(仅第一次即可),则可以通过索引来访问元素,但是instanceof的判定仍未修复。


!function () {

//第四种Array-Like

var List = function () {

var self = {

constructor: List,

length: 0,

add: function (item) {

//本质在这里,交给Array的自动维护

[].push.call(this, item);

}

};

return self;

};

console.group('第四种 - 针对第一种优化');

var demo = new List();

demo.add('List - add()');

console.log('demo instanceof List : %c' + (demo instanceof List), 'color:red;');

console.log('demo.constructor === List :%c' + (demo.constructor === List), 'color:blue');

console.log('[ ' + demo[0] + ' , ' + demo[1] + ' ]');

console.log('length:' + demo.length);

console.log(demo);

console.groupEnd();

}();


运行结果和demo对象结构:



第五种 – 修复instenceof判定:


这种修复有点勉强,因为在ie下并没有__proto__,所以这里所谓的修复只不过是针对现代浏览器而已,只是提供一种思路,关于instenceof请参考请参考:其他。


!function () {

//第五种,我们看见上面那种instanceOf并不能返回正确的结果,于是我们修正它

var List = function () {

/*

instanceof 检测一个对象A是不是另一个对象B的实例的原理是:

查看对象B的prototype指向的对象是否在对象A的[[prototype]]链上。

如果在,则返回true,如果不在则返回false。

不过有一个特殊的情况,当对象B的prototype为null将会报错(类似于空指针异常)。

reference:http://kb.cnblogs.com/page/77478/

*/

self = {

constructor: List,

length: 0,

//强制引用__proto__,IE并不支持

__proto__: List.prototype,

add: function (item) {

push.call(this, item);

}

},

//cache

push = Array.prototype.push;

return self;

};

console.group('第五种 - 修复instenceOf判定');

var demo = new List();

demo.add('List - add()');

console.log('demo instanceof List : %c' + (demo instanceof List), 'color:blue;');

console.log('demo.constructor === List :%c' + (demo.constructor === List), 'color:blue');

console.log('[ ' + demo[0] + ' , ' + demo[1] + ' ]');

console.log('length:' + demo.length);

console.log(demo);

console.groupEnd();

}();


运行结果和demo对象结构:



第六种 – jQuery的实现:


jQuery构造函数繁琐的实现不仅仅只是为了去new化,同时也修复了针对jQuery对象的判定,巧妙的将原型重新指向,让instenceof可以在原型链中查找到jQuery构造函数,使得instenceOf判定有效,让jQuery直逼真正的javascript对象。


!function () {

//jQuery Array-Like实现

var jQuery = function () {

return new jQuery.fn.init();

}, push = Array.prototype.push;

jQuery.fn = jQuery.prototype = {

constructor: jQuery,

length: 0,

add: function (item) {

//使用Array.prototype.push添加元素,会自动维护length

push.call(this, item);

}

};

jQuery.fn.init = function () {

return this;

};

//漂亮的重置prototype

jQuery.fn.init.prototype = jQuery.fn;

console.group('第六种 - jQuery的实现');

var demo = new jQuery();

demo.add('List - add()');

console.log('demo instanceof jQuery : %c' + (demo instanceof jQuery), 'color:blue');

console.log('demo.constructor === jQuery : %c' + (demo.constructor === jQuery), 'color:blue');

console.log('[ ' + demo[0] + ' , ' + demo[1] + ' ]');

console.log('length:' + demo.length);

console.log(demo);

console.groupEnd();

}();


运行结果和demo对象结构:



第七种 – 最简单的实现:


并没有采用闭包,而是通过定义原型实现,实现方法类似第四种,但是原型指向正确,instenceof判定有效。


//最简单的类数组实现

!function () {

var List = function () { }, push = Array.prototype.push;

List.prototype = {

constructor: List,

length: 0,

add: function (item) {

push.call(this, item);

}

};

console.group('第七种 - 最简单的实现');

var demo = new List();//只是需要new

demo.add('List - add()');

console.log('demo instanceof List : %c' + (demo instanceof List), 'color:blue;');

console.log('demo.constructor === List :%c' + (demo.constructor === List), 'color:blue');

console.log('[ ' + demo[0] + ' , ' + demo[1] + ' ]');

console.log('length:' + demo.length);

console.log(demo);

console.groupEnd();

}();


运行结果和demo对象结构:



第八种 – jQuery拆解版:


为了更好的理解jQuery的构造函数实现,所以给出了这种,jQuery.fn.init就是本例中的ArrayLike对象,jQuery只是把init挂载到jQuery.prototype上了而已。


(function () {

var List = function () {

return new ArrayLike();

}, ArrayLike = function () {//这个array-like就是jQuery拆解版的实现

};

List.prototype = {

constructor: List,

length: 0,

add: function (item) {

Array.prototype.push.call(this, item);

}

};

//就是jQuery的jQuery.fn.init.prototype = jQuery.fn;

ArrayLike.prototype = List.prototype;

//测试

console.group('第八种 - jQuery拆解版');

var demo = List(); //这样就不用new了

demo.add('List - add()');

console.log('demo instanceof List : %c' + (demo instanceof List), 'color:blue;');

console.log('demo.constructor === List :%c' + (demo.constructor === List), 'color:blue');

console.log('[ ' + demo[0] + ' , ' + demo[1] + ' ]');

console.log('length:' + demo.length);

console.log(demo);

console.groupEnd();

})();


运行结果和demo对象结构:



其实应该叫做类数组对象的7次实现…有点标题党的意思…..不要打脸…


其他


  • 完整源码:https://github.com/linkFly6/linkfly.so/blob/master/LinkFLy/LinkFly/ArrayLike.js

  • 参考文章:JavaScript类数组对象参考

  • 参考文章:理解instanceof实现原理



来自:linkFly

链接:http://www.cnblogs.com/silin6/p/4309925.html



    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多