jQuery方法原生实现---each遍历

来源:互联网 发布:淘宝网网页版 编辑:程序博客网 时间:2024/06/01 07:46

最近有点浮躁,总想着快速前端进阶,成为高手。奈何现实残酷,故此用原生js实现下jQuery一些函数,用于练手。

each遍历类数组,数组挺好用的。网上也有很多教程,原理无非是利用call,apply改变this指针指向,指向谁呢?嗯,js数组!

原生数组有着很多方法能够读取数组元素,例如新增加every()方法遍历数组,IE9以上支持该方法


function each(object,callback){[].every.call(object, function(v, i){              return callback.call(v, i, v) === false ? false : true;          });  }each(ages,function(v,i){console.log(i);});

使用扩展方式,对js原生函数进行改造,也就是利用call改变this指针,让其指向传进来的object,后方的function则是参数。合起来就是带有两个参数的匿名function连同数组array同时指向object,此时object便具有原生数组的every特性和带有两个参数方法。

那为何function方法返回布尔值?原因就是every方法遍历返回的便是布尔值。

到此,简单的each遍历就实现了。不过问题来了,此时的each只能是数组和类数组,因为调用的是数组原生方法every,对象格式就不行了。

所以我们还要对象进行遍历,就得再写一个函数,返回对象键和值(key  value),我们需要for循环遍历

function each1(obj,fn){    for(var i in obj){        if(fn.call(obj,i,obj[i])===false){            break;        }    }}var obj = {name:"张三",age:"15"}each1(obj,function(i,v){    console.log(i+"  "+v);});


call能够改变this内部指针的指向,从而改变某个对象的执行上下文环境,也就是改变了fn回掉函数的指向,指向了obj对象,从而达到遍历对象目的。所以call也能够用来实现js式的继承,不过es6的出现,弥补好多js缺陷,有兴趣的自己去百度下es6继承。

类数组验证:

<p>1</p>    <p>2</p>    <p>3</p>    <p>4</p>

var p = document.querySelectorAll('p');each1(p,function(i,v){    console.log(i+"  "+v.innerHTML);})

结果会输出下标0123和值1234

回过头来看下,我们发现其实这个实现有缺陷,我们并没有对传入的第一个参数做类型判断。如果将对象类型和类数组类型传给第一个函数each,就会报错,它只能处理数组对象。

所以我们要给它添加类型判断,用于区分处理。这里我们要用到一个数组对象属性constructor,constructor 属性返回对创建此对象的数组函数的引用,我们用它来区分各个类型数据。

var test=new Array();if (test.constructor==Array){document.write("This is an Array");}if (test.constructor==Boolean){document.write("This is a Boolean");}if (test.constructor==Date){document.write("This is a Date");}if (test.constructor==String){document.write("This is a String");}

以上是引用w3c网站的例子,我们each遍历函数主要是数组,类数组,对象格式三种格式,所以我们在参数传进来时候做一个判断即可。

var type = (function(){          switch (object.constructor){            case Object:                return 'Object';                break;            case Array:                return 'Array';                break;            case NodeList:                return 'NodeList';                break;            default:                return 'null';                break;        }    })();

到了这里我们就可以保证each函数不出错,数组和类数组调用each,对象格式调用each1,怎么?两个函数,自己开个if--else控制语句就可以合并了。到了这里基本可以算是完事了,但貌似every不支持ie9以下浏览器,那怎么办呢?你完全可以使用第二种方法嘛!数组也是对象啊!!!!!

each1([45,1,5,36],function(i,v){    console.log(i+"  "+v);});
那each和each1区别?个人猜测,毕竟是原生支持的属性,every在浏览器内部运行总归要快点吧。

那还有没有其它实现?有,你还可以使用arguments代替哈

function each2(){    for(var i=0;i<arguments[0].length;i++){        if(arguments[1].call(arguments[0],i,arguments[0][i])===false){            break;        }    }}

其实挺无聊的,就先这么着了,但是我们要实现jQuery的each函数,$('xx').each(obj,function(i,v){})这种形式才成,不然和jQuery就没关系了。

那么$('xx')怎么实现呢?简单的好实现,复杂的,自己研究jQuery源代码吧,网络上教程也不少。

<p id="aa">1</p>    <p class="bb">2</p>    <p class="bb">3</p>    <p>4</p>

//$()的简单实现var doc = document;function $(dom){    //dom元素简单区分    //清除空格    dom=dom.replace(/^(\s|\u00A0)+/,'').replace(/(\s|\u00A0)+$/,'');      //过滤#和.    var str = /[^#|\.]\w*/.exec(dom);    //获得#或.    var match1 = /^#|\./.exec(dom);    console.log(match1)    return match1 == "#"?doc.getElementById(str):(match1 == "."?doc.getElementsByClassName(str):doc.getElementsByTagName(str));}console.log($(' #aa'));console.log($('.bb'));console.log($('p'));

以上是$的简单模拟,但是问题还没有解决,它并不能链式调用,怎么办呢?采用jQuery做法,内部返回原型,可是直接返回this没用,函数中this由调用该函数的环境决定。所以我们需要封装成对象,以下是模拟jQuery插件搞得一个简单js库,可以判断是ID,clas和标签,然后做一个链式调用。


+function(window){    var aQuery = function(selector){        return new aQuery.prototype.init(selector);    }    aQuery.prototype ={        init : function(selector){            //dom元素简单区分            //清除空格            var doc = document;            selector=selector.replace(/^(\s|\u00A0)+/,'').replace(/(\s|\u00A0)+$/,'');              //过滤#和.            var str = /[^#|\.]\w*/.exec(selector);            //获得#或.            var match1 = /^#|\./.exec(selector);            this.selector = match1 == "#"?doc.getElementById(str):(match1 == "."?doc.getElementsByClassName(str):doc.getElementsByTagName(str));            return this;        },        each : function(){            console.log(this.selector)            for(var i=0;i<this.selector.length;i++){                if(arguments[0].call(this.selector,i,this.selector[i])===false){                    break;                }            }            return this;        }    }    aQuery.prototype.init.prototype = aQuery.prototype;    window.aQuery = window.$ = aQuery;    }(window);$('.bb').each(function(i,v){    console.log(i+" "+v.innerHTML);})

简单说下原理,插件使用立即执行函数包裹,避免变量污染;使用无new构造方法,通过原型赋值实现无new方式;通过每个方法末端返回this,这里this指代aQuery对象,由于js天生原型链,所以返回的方法就可以被链式调用。



参考文章链接:http://www.cnblogs.com/aaronjs/p/3278578.html

    http://www.cnblogs.com/MonaSong/p/6424366.html