视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
开发自己的JQuery框架的详情
2020-11-27 20:24:33 责编:小采
文档

通过json对象实现封装

这是最简单的一种封装方式,如下:

<script type="text/javascript">
 /**
 *自执行的匿名函数,可以实现链式调用
 **/
 (function(w){
 var DQuery={ 
 'name':'DQuery', 
 'version':'2016-06-05', 
 'getVersion':function(){
 console.log(this.version); 
 return this;
 },
 getName:function(){
 console.log(this.name); 
 return this;
 },
 showinfo:function(){
 this.getVersion(); 
 this.getName(); 
 return this;
 }
 };

 window.DQuery=$$=DQuery; //让外边可以调用
 }(window)); </script>

优点,简单,很容易阅读。
缺点:DQuery是一个对象,而不是一个构造函数,当输出DQuery的时候,里面包含的所有信息都暴露了(如下图)。其次,没法对其进行相应的定制,以生成不同的对象,满足不同情况下的使用。

通过构造函数实现封装

版本一

这个版本是大家首先都会想到的版本,程序如下:

(function(w){
 var DQuery=function(){
 this.alias=null;
 }
 DQuery.prototype ={ 'name':'DQuery', 'version':'2016-06-05', 'setAlias':function(alias){
 this.alias=alias; return this;
 }, 'getAlias':function(alias){
 console.log(this.alias); return this;
 }, 'getVersion':function(){
 console.log(this.version); return this;
 },
 getName:function(){
 console.log(this.name); return this;
 },
 showinfo:function(){
 this.getVersion(); this.getName(); return this;
 }
 }

 window.DQuery=$$=DQuery; //让外边可以调用
 }(window));

调用代码如下

 var p1= new DQuery();
 p1.alias='p1'
 var p2= new $$();
 p2.alias='p2'
 console.log(p1); console.log(p2); 
 console.log(p1.showinfo==p2.showinfo); console.log(typeof(DQuery));

效果如下

优点:通过输出可以看出,首先DQuery是一个构造函数,便于我们根据相应的参数生成不同的对象。其次,在DQuery的prototype 中定义的变量和函数,是所有对象共有的,相当于是静态的。
缺点:每次创建一个对象都得去new一个DQuery难免有点麻烦。其次,还是有点暴露的。

版本二

针对版本一中的每次创建一个对象都得new一个,可能我们首先想到的是将其改为如下:

var DQuery=function(){
 this.alias=null; return new DQuery();
 }

这样,从代码上看是解决了存在的问题,但是又迎来了一个新的问题。即因为在DQuery创建起自身的对象,相当于递归调用,出现死循环的问题。

版本三

针对版本一和版本二中的问题,可以做如下改进

(function(w){
 var DQuery=function(alias){
 this.alias=alias; return new DQuery.prototype.init();
 }
 DQuery.prototype ={ 'name':'DQuery', 'version':'2016-06-05', 'init':function(){

 }, 'setAlias':function(alias){
 this.alias=alias; return this;
 }, 'getAlias':function(alias){
 return this;
 }, 'getVersion':function(){
 return this.version;
 },
 getName:function(){
 return this.name;
 },
 showinfo:function(){
 return this.name+':'+this.version;
 }
 }

 window.DQuery=$$=DQuery; //让外边可以调用
 }(window));

 console.log(typeof($$));//$$是一个构造函数
 console.log(typeof($$()));//$$()返回一个对象
 console.log(typeof(new $$()));//new $$()返回一个对象

 var p1=$$('id1');	var p2=new $$('id2');

优点:解决了版本一和版本二中存在的问题。
缺点:无法调用类中(构造函数中)的属性和方法。

版本四

由new创建的对象,对象中的作用域是函数的作用域,其prototype也是构造函数的prototype(具体可以参考补充内容),那么,既然我们使用了new DQuery.prototype.init();,返回对象的prototype等于init函数的prototype。但是我们希望其指向DQuery函数的prototype。此时,有两种做法:
方案一:我们在init方法中,返回指向DQuery对象的this,但是这个在该条件下很难做的,因为我确定,用户是通过new DQuery来创建对象还是直接调DQuery()来创建对象
方案二:我们可以让init.prototype=DQuery.prototype,这样虽然是用init构造函数创建的对象,但是对象的prototype和DQuery的prototype相同。
于是将版本三改进后,代码如下如下:

(function(w){
 var DQuery=function(alias){
 this.alias=alias; 
 return new DQuery.prototype.init();
 }
 DQuery.prototype ={ 
 'self':this, 
 'name':'DQuery', 
 'version':'2016-06-05', 
 'init':function(){

 }, 'setAlias':function(alias){
 this.alias=alias; return this;
 }, 'getAlias':function(alias){
 return this;
 }, 'getVersion':function(){
 return this.version;
 },
 getName:function(){
 return this.name;
 },
 showinfo:function(){
 return this.name+':'+this.version;
 }
 }
 DQuery.prototype.init.prototype=DQuery.prototype;
 window.DQuery=$$=DQuery; //让外边可以调用
 }(window));

 console.log(typeof($$));//$$是一个构造函数
 console.log(typeof($$()));//$$()返回一个对象
 console.log(typeof(new $$()));//new $$()返回一个对象
 var p1=new DQuery();
 console.log(p1);
 console.log(p1 instanceof DQuery); //true


可以发现,此时已经有了完全符合我们的要求了,也解决了上面出现的问题。

版本五

开始以为版本4是没有什么问题,最后发现原来版本4还是有个小问题,即返回的对象,没法访问DQuery构造函数定义的属性。针对这个问题,我们可以通过call或者apply来解决。当然,其实也没必要,因为我们,直接可以讲一些属性定义在init方法中,何必定义在DQuery,然后给自己找麻烦呢。
****后续版本会继续添加*************

总结

大致关系图如下

代码简化后如下:

(function(w){
 var DQuery=function(){
 return new DQuery.prototype.init();
 }
 DQuery.prototype ={ //定义一些静态变量
 'self':this, 'name':'DQuery', 'version':'2016-06-05', // 构造函数方法
 'init':function(){
 //定义一些变量属性
 }, //定义一些方法
 'setAlias':function(alias){
 this.alias=alias; return this;
 } 
 }
 DQuery.prototype.init.prototype=DQuery.prototype;
 window.DQuery=$$=DQuery; //让外边可以调用
 }(window));

补充

构造函数的返回值对new一个对象的影响

首先我们来总结一下new一个对象的过程。比如使用Student构造函数创建对象var s1=new Student(),过程可以归纳如下:首先创建一个新对象,其次将构造函数的作用域赋给新对象(因此this指向这个新对象,且Student.prototype赋给该对象的prototype),然后再将该对象赋值给s1。

构造函数中没有指定返回值

该情况下,默认会返回新对象实例。

构造函数中存在指定返回值

1.返回值为基本数据类型的话,仍然会返回新对象实例。
2..返回值为对象的话,被返回的对象就成了指定的对象值。在这种情况下,this值所引用的对象就被丢弃了。
3.返回function的话,new不会返回一个对象,而会返回该function。

//无返回值
 function Student1(){
 this.name='dqs';
 } var p1=new Student1();
 console.log(typeof(p1));//object
 console.log('name' in p1 ); //true
 console.log(p1 instanceof Student1 ); //true

 //返回function
 function Student2(){
 this.name='dqs'; return function(){};
 } var p2=new Student2();

 console.log(typeof(p2));//function
 console.log(p2 instanceof Student2 ); //false
 //返回基本类型

 //返回基本类型
 function Student3(){
 this.name='dqs'; return 'nihao';
 } var p3=new Student3();
 console.log(typeof(p3));//object
 console.log('name' in p3 ); //true
 console.log(p3 instanceof Student3 ); //true

 //返回对象类型
 function Student4(){
 this.name='dqs'; return {'location':'hsd'};
 } var p4=new Student4();
 console.log(typeof(p4));//object
 console.log('name' in p4 ); //false
 console.log(p3 instanceof Student4 ); //false

下载本文
显示全文
专题