Javascript - Arraylike的7种实现

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

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

本文原创于linkFly原文地址

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

  • 锋芒毕露的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次实现...有点标题党的意思.....不要打脸...

其他

作者:linkFly

原文:http://www.cnblogs.com/silin6/p/ArrayLike.html

出处:www.cnblogs.com/silin6/

声明:嘿!你都拷走上面那么一大段了,我觉得你应该也不介意顺便拷走这一小段,希望你能够在每一次的引用中都保留这一段声明,尊重作者的辛勤劳动成果,本文与博客园共享。

时间: 2024-11-06 15:46:07

Javascript - Arraylike的7种实现的相关文章

实现一个函数clone,使JavaScript中的5种主要的数据类型(包括Number、String、Object、Array、Boolean)进行值复制

实现一个函数clone,可以对JavaScript中的5种主要的数据类型(包括Number.String.Object.Array.Boolean)进行值复制. 1 /** 对象克隆 2 * 支持基本数据类型及对象 3 * 递归方法 */ 4 function clone(obj) { 5 var o; 6 switch (typeof obj) { 7 case "undefined": 8 break; 9 case "string": o = obj + &q

JavaScript中的三种弹出对话框

JavaScript中的三种弹出对话框 *****本文来自互联网****** 学习过js的小伙伴会发现,我们在一些实例中用到了alert()方法.prompt()方法.prompt()方法,他们都是在屏幕上弹出一个对话框,并且在上面显示括号内的内容,使用这种方法使得页面的交互性更精彩,实际上我们经常会在进行网页浏览时简单这种类型的对话框,在用户与应用程序进行双向交流时,经常要用到对话框.avascript的三种对话框是通过调用window对象的三个方法alert(),confirm()和prom

面向面试编程——javascript继承的6种方法

javascript继承的6种方法 1,原型链继承 2,借用构造函数继承 3,组合继承(原型+借用构造) 4,原型式继承 5,寄生式继承 6,寄生组合式继承 1.原型链继承. <script type="text/javascript"> function Person(name,sex) { this.name=name; this.sex=sex; this.friends=['李四']; this.getName=function(){ alert(this.name

探究JavaScript中的五种事件处理程序

我们知道JavaScript与HTML之间的交互是通过事件实现的.事件最早是在IE3和Netscape Navigator 2中出现的,当时是作为分担服务器运算负载的一种手段. 通俗地理解,事件就是用户或浏览器自身执行的某种操作.而事件处理程序即为响应某个事件的函数.抽出主干,即事件处理程序为函数.  我们又把事件处理程序称为事件侦听器.  事件处理程序是以"on"开头的,因此对于事件on的时间处理程序即为onclick.时间处理程序在JavaScript中大致有五种,下面会根据这五种

[转帖] javascript中括号的几种含义

小括号       JavaScript中小括号有五种语义       语义1,函数声明时参数表              function func(arg1,arg2){                // ...              }         语义2,和一些语句联合使用以达到某些限定作用              // 和for in一起使用                      for(var a in obj){                // ...    

javascript继承的两种方式

javascript继承的两种方式 1,原型链 1 <script type="text/javascript"> 2 function A() 3 { 4 this.name='a'; 5 } 6 function B() 7 { 8 9 } 10 11 12 B.prototype=new A(); 13 var obj=new B(); 14 15 alert(obj.name); 16 </script> 2,对象冒充 1 <script type

javascript中对象两种创建方式

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <script type="text/javascript"> // 创建对象的两种方式: // 1 对象字面量 // 2 new Obj

JavaScript中的三种弹出框

1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title>JavaScript中的三种弹出框</title> 5 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> 6 <script type="text/JavaScript"> 7 funct

JavaScript中的两种事件流

JavaScript中的两种事件流 事件流描述的是从页面中接收事件的顺序.提出事件流概念的正是IE和Netscape,但是前者提出的是我们常用的事件冒泡流,而后者提出的是事件捕获流. 第一部分:事件冒泡 即事件开始由最具体的元素接收,然后逐级向上传播到较为不具体的节点(文档). 下面举一个简单的例子: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"&