t - Arraylike的7种实现

王朝学院·作者佚名  2016-05-23
窄屏简体版  字體: 小  |  中  |  大  |  超大  

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

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

本文原创于linkFly,原文地址。

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

锋芒毕露的ArrayLikeArrayLike的实现其他锋芒毕露的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有下面这几个,详见:其他。

ArgumentsNodeListStyleSheetListHTMLCollectionHTMLFormControlsCollection (继承HTMLCollection)HTMLOptionsCollection(继承HTMLCollection)HTMLAllCollectionDOMTokenListArrayLike的实现第一种 - 通过闭包实现:通过闭包实现,内部采用一个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对象的判定,巧妙的将原型重新指向,让instenc

 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
 
© 2005- 王朝網路 版權所有 導航