Javascript - Arraylike的7种实现
jQuery的崛起让ArrayLike(类数组)在javascript中大放异彩,它的出现为一组数据的行为(函数)扩展提供了基础。
类数组和数组相似,具有数组的某些行为,但是它相比数组可以更加自由的扩展,它的存在让一组数据的表现不再受限于数组,也无需去污染数组本身的原型——它来自javascript对象的挖掘和扩展,而并非javascript本身就存在的。简单的说,它来自数组,比数组更加适合扩展。
这篇文章主要分为以下知识
锋芒毕露的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次实现...有点标题党的意思.....不要打脸...
其他
Javascript - Arraylike的7种实现的更多相关文章
- Javascript函数调用的四种模式
一 前言 Javascript一共有四种调用模式:方法调用模式.函数调用模式.构造器调用模式以及apply调用模式.调用模式不同,对应的隐藏参数this值也会不同. 二 方法调用模式 函数作为对象的属 ...
- 探究JavaScript中的五种事件处理程序
探究JavaScript中的五种事件处理程序 我们知道JavaScript与HTML之间的交互是通过事件实现的.事件最早是在IE3和Netscape Navigator 2中出现的,当时是作为分担服务 ...
- 在WebBrowser中执行javascript脚本的几种方法整理(execScript/InvokeScript/NavigateScript) 附完整源码
[实例简介] 涵盖了几种常用的 webBrowser执行javascript的方法,详见示例截图以及代码 [实例截图] [核心代码] execScript方式: 1 2 3 4 5 6 7 8 9 1 ...
- JS学习笔记——JavaScript继承的6种方法(原型链、借用构造函数、组合、原型式、寄生式、寄生组合式)
JavaScript继承的6种方法 1,原型链继承 2,借用构造函数继承 3,组合继承(原型+借用构造) 4,原型式继承 5,寄生式继承 6,寄生组合式继承 1.原型链继承. <script t ...
- 面向面试编程——javascript对象的几种创建方式
javascript对象的几种创建方式 总共有以下几个模式: 1.工厂模式 2.构造函数模式 3.原型模式 4.混合构造函数和原型模式 5.动态原型模式 6.寄生构造函数模式 7.稳妥构造函数模式 1 ...
- JavaScript中的三种弹出对话框
学习过js的小伙伴会发现,我们在一些实例中用到了alert()方法.prompt()方法.prompt()方法,他们都是在屏幕上弹出一个对话框,并且在上面显示括号内的内容,使用这种方法使得页面的交互性 ...
- JavaScript中的三种弹出框的区别与使用
JavaScript中有三种原生的弹出框,分别是alert.confirm.prompt.分别表示弹出框.确认框.信息框. 以下是示例代码: <!DOCTYPE html> <htm ...
- JavaScript脚本的两种放置方式
JavaScript脚本的两种放置方式 1在body里用 script标签引用 2 直接写在<script></script>标签之中
- 对 JavaScript 中的5种主要的数据类型进行值复制
定义一个函数 clone(),可以对 JavaScript 中的5种主要的数据类型(包括 Number.String.Object.Array.Boolean)进行值复制 使用 typeof 判断值得 ...
随机推荐
- [转载]SVN使用教程
SVN简介: 为什么要使用SVN? 程序员在编写程序的过程中,每个程序员都会生成很多不同的版本,这就需要程序员有效的管理代码,在需要的时候可以迅速,准确取出相应的版本. Subversion是什么? ...
- Spark入门实战系列--8.Spark MLlib(下)--机器学习库SparkMLlib实战
[注]该系列文章以及使用到安装包/测试数据 可以在<倾情大奉送--Spark入门实战系列>获取 .MLlib实例 1.1 聚类实例 1.1.1 算法说明 聚类(Cluster analys ...
- Deep learning:四十五(maxout简单理解)
maxout出现在ICML2013上,作者Goodfellow将maxout和dropout结合后,号称在MNIST, CIFAR-10, CIFAR-100, SVHN这4个数据上都取得了start ...
- JavaScript 回忆录
作者:禅楼望月(http://www.cnblogs.com/yaoyinglong) 1. 基本类型和应用类型 1.1 复制变量值的不同 值类型的变量在复制变量值后互不影响,因为值类型本身保存的就是 ...
- 【Android】Android应用安装失败及无法打开
以下是我个人遇到过的APP无法安装的一些问题: 无法安装应用: 手机系统版本过低:不符合应用支持的最低版本.(比如应用只支持Android 4.0以上的手机,而手机是Android2.3的)解决方案: ...
- Windows Azure Web Site (17) Azure Web Site 固定公网IP地址
<Windows Azure Platform 系列文章目录> 在之前的文档中,笔者介绍了Azure Web Site是一个多租户的环境,每个部署单元有一个可以通过Internet访问的入 ...
- SQL Server中的事务日志管理(8/9):优化日志吞吐量
当一切正常时,没有必要特别留意什么是事务日志,它是如何工作的.你只要确保每个数据库都有正确的备份.当出现问题时,事务日志的理解对于采取修正操作是重要的,尤其在需要紧急恢复数据库到指定点时.这系列文章会 ...
- .NET平台下IIS7.5+无后缀名伪静态实现办法
首先新建一个应用程序池,名称任意,比如:nettest,托管管道模式先暂时设置为集成模式,等下面的一系列设置完成之后再设置成经典模式: 部署好站点,并将此站点的应用程序池设置为nettest; 选中站 ...
- 基于HT for Web的3D呈现A* Search Algorithm
最近搞个游戏遇到最短路径的常规游戏问题,正巧看到老同事写的3D机房最短路径巡线文章,一时起兴基于HT for Web写了个A*算法的WebGL 3D呈现,算法基于开源 https://github.c ...
- 勤能补挫-简单But易错的JS&CSS问题总结
错误频率较高的JS&CSS问题 勤能补拙,不管是哪门子技术,在实践中多多总结,开发效率慢慢就会提升.本篇介绍几个经常出错的JS&CSS问题,包括事件冒泡.(使用offset.scrol ...