最近有点浮躁,总想着快速前端进阶,成为高手。奈何现实残酷,故此用原生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