javascript笔记:临摹jQuery(二)


======================================================
注:本文源代码点此下载
======================================================

在我前一篇博客里有位童鞋问了xquery.fn.init.prototype = xquery.fn;写法的目的,这个问题在我临摹jquery时候也碰到过,最开始我是没有加入这个方法,在firebug里面,方法console.log(xquery('').xquery);打印出的结果是:undefined,这就表明xquery属性根本就没有定义,我构建的xquery对象只是保留了在xquery.fn.init(selector,context);里面存在的属性,如是我做了下面的测试:

xquery.fn = xquery.prototype = {

init:function(){

return this;

},

tempquery:'1.0.1',

length:23,

size:function(){

return this.length;

}

};

console.log(xquery().tempquery);//undefined

console.log(xquery().length);//undefined

console.log(xquery().size());//error:xquery().size is not a function

那么xquery.fn.init.prototype = xquery.fn;作用到底是怎样的?我做了下面一系列的测试:

测试一:对于代码:

var xquery = function(selector,context){

return new xquery.fn.init(selector,context);

}

这是使用了javascript里面的工厂模式构建对象,只不过这个工厂比较特别,让人第一眼觉得是个迭代的调用的工厂模式,因此我写了下面的测试代码:

var xquery = function(){

return new tempquery();

};

var tempquery = function(){};

tempquery.prototype = {

tempquery:'1.0.1',

length:4,

size:function(){

return this.length;

}

};

console.log(xquery().tempquery);//1.0.1

console.log(xquery().length);//4

console.log(xquery().size());//4

console.log(xquery() instanceof xquery);//false

这里我另起了一个对象tempquery ,初始化了tempquery 的原型链的属性,那么每次调用xquery()就是在使用作为xquery构造函数返回值的tempquery 对象。如果这么做,就存在了javascript里工厂模式的缺点了:1.会产生重复的相似对象,造成系统资源的浪费;2.xquery对象识别的问题(例如:console.log(xquery() instanceof xquery);//false)。

要解决这些问题,那么xquery的构造函数应该是构造自己本身,而不是每次构造都是一个新的对象,这样的思路就能避免javascript工厂模式的缺陷,因此我又做了下面测试。

测试二:

var xquery = function(){

return new xquery();//too much recursion new xquery();//too much recursion,无限递归调用

};

xquery.prototype = {

tempquery:'1.0.1',

length:8,

size:function(){

return this.length;

}

};

console.log(xquery().tempquery);

console.log(xquery().length);

console.log(xquery().size());

console.log(xquery() instanceof xquery);

最终的结果是会在new xquery();代码处报出too much recursion new xquery();//too much recursion,无限递归调用 的错误,也就是所谓的内存溢出了。这种写法真白痴,如果不是为了验证结果,我不会去写这么明显的递归调用的代码的。

那么有什么更好的写法了,如是我再看看jquery源码,发现它是使用到了prototype原型链来构造xquery对象。因此就有下面测试代码:

测试三:

var xquery = function(){

return new xquery.prototype.init();

};

xquery.prototype = {

init:function(){

return this;

},

tempquery:'1.0.1',

length:8,

size:function(){

return this.length;

}

};

console.log(xquery().tempquery);//undefined

console.log(xquery().length);//undefined

console.log(xquery() instanceof xquery);//false

console.log(xquery().size());//xquery().size is not a function

这种写法就没有报内存溢出的错误了,不过也没有达到我们预期的效果xquery().tempquery和xquery.length都是undefined,xquery() 的类型也不是xquery,size()还没有定义(这个我和文章前面没写xquery.fn.init.prototype = xquery.fn;的情况一样),为什么会有这样的结果了?其实从javascript对象的理论老理解这种结果就很简单了。原因分析如下:

我们知道javascript里面除了string,boolean等基本类型外其他都是object(对象),而对象的名称只是该对象的在栈内存中的地址,换种说法就是该对象的引用。其实我们程序中的return new xquery.prototype.init();这个只是调用了xquery.prototype.init();这个引用的所对应的对象,其他的对象里的属性都没管,也就是说代码里的:

size:function(){

return this.length;

}

还没有被调用过,所以我们看到报了size is not a function的错误。呵呵,是不是被我说糊涂了啊,其实这里我们还要了解一个javascript里面的一个知识,javascript是一个脚本语言,他和c、java在编译,运行上有很大不同。javascript里面如果我们定义一个function,例如:

var ftn = function(){

alert('hello world');

}

页面加载时候,浏览器里面的javascript解释器只是生命了ftn变量,这个过程叫做javasript的预编译,这时候的ftn = undefined,当我们new ftn();时候,也就是我们运行js代码时候,javascript解释器是先编译再执行,而return new xquery.prototype.init();只是运行了init()对象,而size还没运行,所以运行结果就是size is not a function的错误。

那么要解决这个问题,我们只要在return new xquery.prototype.init();时候同时让xquery.prototype其他代码也运行起来,那么上面的问题不是就解决了吗?

如是我做了第四个测试。

测试四:

var xquery = function(){

return new xquery.prototype.init();

};

xquery.prototype = {

init:function(){

return this;

},

tempquery:'1.0.1',

length:8,

size:function(){

return this.length;

}

};

xquery.prototype.init.prototype = xquery.prototype;

console.log(xquery().tempquery);//1.0.1

console.log(xquery().length);//8

console.log(xquery() instanceof xquery);//true

console.log(xquery().size());//8

哈哈,这不就是jquery的效果啊。我把xquery.prototype.init引用对应的对象的prototype原型链指向了xquery.prototype,那么构建 new xquery.prototype.init();

时候xquery.prototype 也同样被构造了,那么我在init里面修改和init平级的属性,效果如何了?

测试五:

var xquery = function(){

return new xquery.prototype.init();

};

xquery.prototype = {

init:function(){

this.length = 10001;

this.tempquery = '1.6.1';

return this;

},

tempquery:'1.0.1',

length:8,

size:function(){

return this.length;

}

};

xquery.prototype.init.prototype = xquery.prototype;

结果:

console.log(xquery().tempquery);//1.6.1

console.log(xquery().length);//10001

console.log(xquery() instanceof xquery);//true

console.log(xquery().size());//10001

这就说明this指针指向的对象是同一个xquery对象,表明xquery.prototype也被构建了。不过上面的写法似乎和jquery的写法还是有所不同,不过效果一样了哈。

jquery里面的jquery.fn真是没啥好说的,只是jquery的开发者觉得jquery.prototype太长了为了节省代码而将jquery.fn作为jquery.prototype的别名,下面我把代码修改成jquery的样式,最终代码如下:

var xquery = function(){

return new xquery.prototype.init();

};

xquery.fn = xquery.prototype = {

init:function(){

this.length = 10001;

this.tempquery = '1.6.1';

return this;

},

tempquery:'1.0.1',

length:8,

size:function(){

return this.length;

}

};

xquery.fn.init.prototype = xquery.fn;

console.log(xquery().tempquery);//1.6.1

console.log(xquery().length);//10001

console.log(xquery() instanceof xquery);//true

console.log(xquery().size());//10001

总结下吧:本来我想说这就是jquery架构的核心,写完后发现其实不然,所以这里的总结只是说jquery源码里的代码技巧性很高,我下篇博客打算暂停下分析jquery源码,而将一些基础知识系统的讲解下,javascript是一个常常让人蒙掉的语言,就是它有很多非常规诡异的语法,或许这些大伙都清楚,但是时不时的温故而知新还是相当有好处的。


======================================================
在最后,我邀请大家参加新浪APP,就是新浪免费送大家的一个空间,支持PHP+MySql,免费二级域名,免费域名绑定 这个是我邀请的地址,您通过这个链接注册即为我的好友,并获赠云豆500个,价值5元哦!短网址是http://t.cn/SXOiLh我创建的小站每天访客已经达到2000+了,每天挂广告赚50+元哦,呵呵,饭钱不愁了,\(^o^)/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值