javascript 伪数组实现方法


Posted in Javascript onOctober 11, 2010

这篇文章来回答javascript通用循环遍历方法forEach中最后提到的关于伪数组的问题。
什么是伪数组
能通过Array.prototype.slice转换为真正的数组的带有length属性的对象。
这种对象有很多,比较特别的是arguments对象,还有像调用getElementsByTagName,document.childNodes之类的,它们都返回NodeList对象都属于伪数组。
我们可以通过Array.prototype.slice.call(fakeArray)将伪数组转变为真正的Array对象。
来看个示例:

var fakeArray01 = {0:'a',1:'b',length:2};//这是一个标准的有伪数组对象 
var arr01 = Array.prototype.slice.call(fakeArray01); 
alert(arr01[0]);//a 
var arr02 = [].slice.call(fakeArray01); 
alert(arr02[0]);//a

slice 可以用来获取数组片段,它返回新数组,不会修改原数组。
示例中可以看到fakeArray被成功的转换成了Array对象。也许大家对Array.prototype.slice.call这种写法比较陌生,其实我们也可以通过[].slice.call这种形式实现同样的效果,那为什么我们要通过prototype的形式实现呢,答案是以prototype的形式执行程序效率更高,同样代码也更加优美。
伪数组的实现
让我们再深入的看一下伪数组的实现。
我们来看一些特殊的用例:
var fakeArray01 = {a:'a',b:'b',length:2};//没有length下标对应的值 
var arr01 = Array.prototype.slice.call(fakeArray01); 
alert(arr01[0]);//undefined 
var fakeArray02 = {0:'a',1:'b',length:'num'};//length不是数值 
var arr02 = Array.prototype.slice.call(fakeArray02); 
alert(arr02[1]);//undefined

同样fakeArray01和fakeArray02被转换成了真正的数组,但是数组中的值都为undefined
查看 V8 引擎 array.js   的源码,可以将 slice 的内部实现简化为:
function slice(start, end) { 
var len = ToUint32(this.length), result = []; 
for(var i = start; i < end; i++) { 
result.push(this[i]); 
} 
return result; 
}

可以看出,slice 并不需要 this 为 array 类型,只需要有 length 属性即可。并且 length 属性可以不为 number 类型,当不能转换为数值时,ToUnit32(this.length) 返回 0.
根据以上结论可以得出:fakeArray01被转换成了lenth为2的数组,其值都被初始化为undefined,fakeArray02被转换成了length为0的数组,自然访问下标为1的元素返回undefined
IE的问题
针对于标准浏览器slice实现已经可以解释所有的问题,但是IE在处理NodeList时出现了问题。IE中无法将NodeList转换为真正的数组,会出错。这又是为什么呢?严格说,在IE内部定义了一个抽象类Arraioid,Array和Arguments都继承与此,所以可以用slice。但DOM对象是通过COM接入到JScript的,slice检测的时候失效。
Jquery与伪数组
Jquery内部大量运用了伪数组。可以说整个Jquery对象,都是构建在伪数组的基础之上的,好让我们来看一些Jquery的实际运用:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 
<html> 
<head> 
<title>fakeArray</title> 
<meta http-equiv="content-type" content="text/html; charset=UTF-8"> 
<script src="jquery-1.4.2.js" type="text/javascript"></script> 
<script> 
$(document).ready(function(){ 
var body = $("body"); 
alert(body.get(0).tagName); 
}); 
</script> 
</head> 
<body> 
<div id="test"></div> 
</body> 
</html>

再简单不过的程序了,好,让我们来看一下其内部的实现原理:
jQuery.fn = jQuery.prototype = { 
init: function( selector, context ) { 
var match, elem, ret, doc; 
// Handle $(""), $(null), or $(undefined) 
if ( !selector ) { 
return this; 
} 
// Handle $(DOMElement) 
if ( selector.nodeType ) { 
this.context = this[0] = selector; 
this.length = 1; 
return this; 
} 
// The body element only exists once, optimize finding it 
if ( selector === "body" && !context ) { 
this.context = document; 
this[0] = document.body; 
this.selector = "body"; 
this.length = 1; 
return this; 
} 
//... ... 
}, 
get: function( num ) { 
return num == null ? 
// Return a 'clean' array 
this.toArray() : 
// Return just the object 
( num < 0 ? this.slice(num)[ 0 ] : this[ num ] ); 
} 
}

最后,我们来解释一下,程序的执行细节.但是在这之前,还得说一下关于Jquery的内部的一些东西。
用过Jquery的用户应该都知道$()函数,它是Jquery的选择器代表。我们可能通过$()函数去选取页面中的元素(具体语法可参数Jquery帮助文档)。实际上当我们执行$()函数时,程序去执行上面列出的init方法,我们来看一下在调用$(document)时所发生的事件:
//$(document) 
init: function( selector, context ) { 
var match, elem, ret, doc; 
// Handle $(DOMElement) : 处理DOM元素, 
if ( selector.nodeType ) { 
this.context = this[0] = selector; //给属性0赋予selector值,此时就是document对象 
this.length = 1; //创建伪数组,更新下标 
return this; //返回Jquery对象 
} 
//... ... 
}

$("body")是同样的道理,不再多说了。
我们知道Jquery里所有的操作返回的都是Jquery对象,那我们如何得到其所对应的dom对象呢,Jquery为我们提供了一个get方法,这是专门用来从jquery对象中取得DOM对象用的,由此,便有了body.get(0),那为什么又是get(0)而不是get()呢,因为Jquery的所有操作都是针对于数组进行的。所以,在get方法里,我们要传一个下标值,来得到具体的元素。现在该看get方法的具体实现了:
get: function( num ) { 
return num == null ? 
//如果没有num,则直接返回DOM数组 
this.toArray() : 
//如果指定的num,则返回指定下标的元素 
//this.slice是jquery的另一个方法,它内部其实还是调用Array.prototype.slice来实现将伪数组转换为真实的数组 
( num < 0 ? this.slice(num)[ 0 ] : this[ num ] ); 
}

关于伪数组就到这吧,我想应该已经差不多了。
注:有机会的话,将来可能会出一个"超越Jquery"系列,专门分析Jquery内部执行细节。但是由于Jquery内部的有各种歪门邪道的手法还不是很理解,所以这是将来的问题了。
参考:
http://lifesinger.org/blog/2010/05/array-prototype-slice/
Javascript 相关文章推荐
WebGame《逆转裁判》完整版 代码下载(1月24日更新)
Jan 29 Javascript
预加载css或javascript的js代码
Apr 23 Javascript
通过jQuery源码学习javascript(二)
Dec 27 Javascript
JS 排序输出实现table行号自增前端动态生成的tr
Aug 13 Javascript
javascript制作的简单注册模块表单验证
Apr 13 Javascript
AngularJS基础学习笔记之指令
May 10 Javascript
基于jQuery通过jQuery.form.js插件实现异步上传
Dec 13 Javascript
基于JavaScript实现图片剪切效果
Mar 07 Javascript
深入理解Javascript中的作用域链和闭包
Apr 25 Javascript
vue系列之requireJs中引入vue-router的方法
Jul 18 Javascript
layui前端框架之table表数据的刷新方法
Aug 17 Javascript
layui table设置某一行的字体颜色方法
Sep 05 Javascript
javascript forEach通用循环遍历方法
Oct 11 #Javascript
JavaScript 操作键盘的Enter事件(键盘任何事件),兼容多浏览器
Oct 11 #Javascript
JavaScript isArray()函数判断对象类型的种种方法
Oct 11 #Javascript
JSChart轻量级图形报表工具(内置函数中文参考)
Oct 11 #Javascript
jQuery 表单验证扩展代码(一)
Oct 11 #Javascript
AlertBox 弹出层信息提示框效果实现步骤
Oct 11 #Javascript
基于jQuery的实现简单的分页控件
Oct 10 #Javascript
You might like
PHP内核介绍及扩展开发指南―基础知识
2011/09/11 PHP
PHP与SQL注入攻击防范小技巧
2011/09/16 PHP
php根据年月获取季度的方法
2014/03/31 PHP
浅谈php的TS和NTS的区别
2019/03/13 PHP
通过隐藏option实现select的联动效果
2009/11/10 Javascript
输入密码检测大写是否锁定js实现代码
2012/12/03 Javascript
JavaScript结合AJAX_stream实现流式显示
2015/01/08 Javascript
在Node.js中使用Javascript Generators详解
2016/05/05 Javascript
bootstrap vue.js实现tab效果
2017/02/07 Javascript
jQuery插件HighCharts绘制的基本折线图效果示例【附demo源码下载】
2017/03/07 Javascript
详解如何在 vue 项目里正确地引用 jquery 和 jquery-ui的插件
2017/06/01 jQuery
JavaScript注册时密码强度校验代码
2017/06/30 Javascript
基于Swiper实现移动端页面图片轮播效果
2017/12/28 Javascript
validform表单验证的实现方法
2019/03/08 Javascript
微信小程序Echarts图表组件使用方法详解
2019/06/25 Javascript
在项目vue中使用echarts的操作步骤
2020/09/07 Javascript
[01:12]DOTA2次级职业联赛 - Newbee.Y 战队宣传片
2014/12/01 DOTA
[01:33]DOTA2上海特级锦标赛 LIQUID战队完整宣传片
2016/03/16 DOTA
[28:05]完美世界DOTA2联赛循环赛Inki vs DeMonsTer 第一场 10月30日
2020/10/31 DOTA
Python 序列化 pickle/cPickle模块使用介绍
2014/11/30 Python
python调用fortran模块
2016/04/08 Python
python UNIX_TIMESTAMP时间处理方法分析
2016/04/18 Python
Python基于递归算法求最小公倍数和最大公约数示例
2018/07/27 Python
使用Django简单编写一个XSS平台的方法步骤
2019/03/25 Python
Python八皇后问题解答过程详解
2019/07/29 Python
django ManyToManyField多对多关系的实例详解
2019/08/09 Python
Python3交互式shell ipython3安装及使用详解
2020/07/11 Python
Selenium+BeautifulSoup+json获取Script标签内的json数据
2020/12/07 Python
python 多线程爬取壁纸网站的示例
2021/02/20 Python
BIBLOO捷克:购买女装、男装、童装、鞋和配件
2017/01/27 全球购物
Lampenwelt德国:欧洲领先的灯具和照明在线商店
2018/08/05 全球购物
危货运输企业安全生产责任书
2014/07/28 职场文书
预备党员学习十八届三中全会精神思想汇报
2014/09/13 职场文书
2014预备党员批评与自我批评思想汇报
2014/09/20 职场文书
教师党的群众路线教育实践活动学习心得体会
2014/10/30 职场文书
纯CSS实现hover图片pop-out弹出效果的实例代码
2021/04/16 HTML / CSS