jQuery源码初探(3)
来源:互联网 发布:长春知合动画 编辑:程序博客网 时间:2024/06/02 05:58
今天我们先来 聊聊 jQuery 中的 无 new 构造
写过 js 面向对象的同学知道,一般我们是这么来写的
//构造函数function myjQuery () { this.age = 20;}//方法挂载到原型上myjQuery.prototype.say = function () { console.log( 'my age is ' + this.age );}var myjQuery = new myjQuery(); // 实例化console.log( myjQuery.age ); // 20console.log( myjQuery.say() ); // my age is 20
这是一种比较常见的写法,然而 jQuery 并不是这么做的,回想一下,我们平时在用 jQuery 获取元素的时候,经常都是 $('#myId') , $('.myClass')
这种方式去调用的,难道说 jQuery 不用 new 去创建一个实例?
其实不然,jQuery 内部也是用 new 创建一个 jQuery 对象 , 只不过用的十分巧妙, 接下来我们来看看jQuery是怎么做到的!
在 jQuery 源码中有这么一段
jQuery = function( selector, context ) { return new jQuery.fn.init( selector, context ); }
这个就是 jQuery 的入口方法 , 它返回的 是一个 new jQuery.fn.init( selector, context );
我们知道, 用 new 一个对象 ,肯定是返回一个对象的实例,那么也就是返回的是jQuery.fn.init( selector, context )
的实例,那么这个 jQuery.fn
又是什么? 我们查看源码中有这么一段:
jQuery.fn = jQuery.prototype = { constructor: jQuery, /***code***/ }
也就是说,jQuery.fn
就是 jQuery
构造函数的原型。
接着我们看看jQuery.fn.init()
这个方法,源码中可以找到是这么写的:
init = jQuery.fn.init = function( selector, context, root ) { /****code****/ return this; }
有的同学可能认为到这里就结束了,是吗?ok,我们来测试一下,我们把上面我们的代码改造跟 jQuery 一样结构,
var myjQuery = function () { return new myjQuery.fn.init(); } myjQuery.fn = myjQuery.prototype= { init : function(){ //初始化 return this; }, age : 20, say : function(){ return this.age; } } myjQuery().age; //'undefiend' myjQuery().say(); // 'Uncaught TypeError: myjQuery(...).say is not a function'
什么?出错了,没错,得到的结果确实 令人意外 , 不过也是意料之中 , 我们稍加分析一下就知道了。
上面代码我们使用了return new myjQuery.fn.init();
返回的是 myjQuery 原型上的 init() 方法的实例 , 我们在发现在 init() 方法中返回了this ; 此时的 this 指向的是 myjQuery.fn.init的实例 ,
调用myjQuery() 得到的结果是 myjQuery.fn.init 的实例;
而在myjQuery.prototype.init内部中根本就没有 age 属性 和 say 方法, 所以此时通过这个结果访问不到 myjQuery.prototype 的属性 和方法的 , 因此上面得到的结果也就 不意外了。
可是我们用 jQuery 为什么没有出现这样的情况呢?原来我们还少了一句关键的代码, jQuery 中是这么写的:
init.prototype = jQuery.fn;
我们在上面就看到了 init = jQuery.fn.init
,所以说这句代码相当于
jQuery.fn.init.prototype = jQuery.fn;
这句话是关键,现在我们可以发现:
jQuery.fn.init.prototype = jQuery.fn = jQuery.prototype;
通过原型传递来解决上面的问题,把jQuery的原型传递给 jQuery.prototype.init.prototype
也就是说 jQuery 的原型对象 覆盖了内部 init 构造器的原型对象。
这样new init 的出来的实例对象也就等价于new jQuery的实例对象,所以也就可以访问jQuery的原型方法了。
我们把这句话加上去,把上面的函数改写成:
var myjQuery = function () { return new myjQuery.fn.init(); } myjQuery.fn = myjQuery.prototype= { init : function(){ //初始化 return this; }, age : 20, say : function(){ return this.age ; } } myjQuery.prototype.init.prototype = myjQuery.prototype; myjQuery().age; //20 myjQuery().say() //20
世界清静多了,不是么?
ok,这就是 jQuery 实现无 new 操作的 原理 ~
上面还有一点点东西,我们补充一下:
1. 我们看到 jQuery 对象的是通过原型中的 init 方法 return 回来的,如下:
return new jQuery.fn.init( selector, context );
也就是说,我们每次调用 jQuery 的时候去 new 一个新的对象 , 因此,我们在写代码的时候,如果频繁使用一个 jQuery 对象的话,我们不妨把它用一个变量存起来,可以避免每次去创建对象~
2. 上面 jQuery 原型上,我单独拿出来一句:
constructor: jQuery
jQuery 这么做是为了保证 constructor 的指向,防止构造函数指向错误,引起调用的问题。那么为什么会产生这样的问题呢?
//方式一 fn.prototype.say = function(){} //方式二 fn.prototype = { say : function() {} }
jQuery 采用的是 第二种 方式,这种方式与第一种方式差别很大,第一种是给原型添加一个方法,这种方式是不会影响fn原型内部 constructor 的指向问题,而第二种方式是 重写原型 , 相当于原型覆盖,原来的原型已不复存在,所以需要重新定义 constructor 的指向,小伙伴们以后写代码的时候多注意一点~
- jQuery源码初探(3)
- jQuery源码初探(1)
- jQuery源码初探(2)
- jQuery源码初探(4)
- Spark Streaming源码初探 (3)
- minidlna源码初探(一)
- jquery初探
- jQuery初探
- jquery初探
- JQuery 初探
- 初探JQuery
- JQuery初探
- JQuery初探---Jquery/Ajax
- 初探Tomcat源码 (3) —— SimpleHttpServer
- googletest初探(一) 源码中的例子
- 初探Laravel框架中的源码(一)
- jquery源码解读笔记(1.11.3)
- 菜鸡看jquery源码(3)extend
- Minimum Moves to Equal Array Elements II
- 口头表达题
- java cglib代理特性验证
- target事件
- FastJson的配置和使用步骤
- jQuery源码初探(3)
- Maven入门
- 一些关于公司和技术的理解
- java UDP 同步socket
- gcc简单入门
- python 标准库 —— io(StringIO)
- 活动选择
- android通过修改rom或通过root屏蔽Home键
- Spring中ApplicationContext和beanfactory区别