jQuery 2.0.3 源码分析core - 整体架构
拜读一个开源框架,最想学到的就是设计的思想和实现的技巧。
废话不多说,jquery这么多年了分析都写烂了,老早以前就拜读过,
不过这几年都是做移动端,一直御用zepto, 最近抽出点时间把jquery又给扫一遍
我也不会照本宣科的翻译源码,结合自己的实际经验一起拜读吧!
github上最新是jquery-master,加入了AMD规范了,我就以官方最新2.0.3为准
整体架构
jQuery框架的核心就是从HTML文档中匹配元素并对其执行操作、
例如:
$().find().css() $().hide().html('....').hide().
从上面的写法上至少可以发现2个问题
1. jQuery对象的构建方式
2 .jQuery方法的调用方式
分析一:jQuery的无new构建
JavaScript是函数式语言,函数可以实现类,类就是面向对象编程中最基本的概念
var aQuery = function(selector, context) {//构造函数 } aQuery.prototype = {//原型name:function(){},age:function(){} }var a = new aQuery();a.name();
这是常规的使用方法,显而易见jQuery不是这样玩的
jQuery没有使用new运行符将jQuery显示的实例化,还是直接调用其函数
按照jQuery的抒写方式
$().ready() $().noConflict()
要实现这样,那么jQuery就要看成一个类,那么$()应该是返回类的实例才对
所以把代码改一下:
var aQuery = function(selector, context) {return new aQuery(); } aQuery.prototype = {name:function(){},age:function(){} }
通过new aQuery(),虽然返回的是一个实例,但是也能看出很明显的问题,死循环了!
那么如何返回一个正确的实例?
在javascript中实例this只跟原型有关系
那么可以把jQuery类当作一个工厂方法来创建实例,把这个方法放到jQuery.prototye原型中
var aQuery = function(selector, context) {return aQuery.prototype.init(); } aQuery.prototype = {init:function(){return this;}name:function(){},age:function(){} }
当执行aQuery() 返回的实例:
很明显aQuery()返回的是aQuery类的实例,那么在init中的this其实也是指向的aQuery类的实例
问题来了init的this指向的是aQuery类,如果把init函数也当作一个构造器,那么内部的this要如何处理?
var aQuery = function(selector, context) {return aQuery.prototype.init(); } aQuery.prototype = {init: function() {this.age = 18return this;},name: function() {},age: 20 }aQuery().age //18
这样的情况下就出错了,因为this只是指向aQuery类的,所以需要设计出独立的作用域才行
jQuery框架分隔作用域的处理
jQuery = function( selector, context ) {// The jQuery object is actually just the init constructor 'enhanced'return new jQuery.fn.init( selector, context, rootjQuery );},
很明显通过实例init函数,每次都构建新的init实例对象,来分隔this,避免交互混淆
那么既然都不是同一个对象那么肯定又出现一个新的问题
例如:
var aQuery = function(selector, context) {return new aQuery.prototype.init(); } aQuery.prototype = {init: function() {this.age = 18return this;},name: function() {},age: 20 }//Uncaught TypeError: Object [object Object] has no method 'name' console.log(aQuery().name())
抛出错误,无法找到这个方法,所以很明显new的init跟jquery类的this分离了
怎么访问jQuery类原型上的属性与方法?
做到既能隔离作用域还能使用jQuery原型对象的作用域呢,还能在返回实例中访问jQuery的原型对象?
实现的关键点
// Give the init function the jQuery prototype for later instantiation jQuery.fn.init.prototype = jQuery.fn;
通过原型传递解决问题,把jQuery的原型传递给jQuery.prototype.init.prototype
换句话说jQuery的原型对象覆盖了init构造器的原型对象
因为是引用传递所以不需要担心这个循环引用的性能问题
var aQuery = function(selector, context) {return new aQuery.prototype.init(); } aQuery.prototype = {init: function() {return this;},name: function() {return this.age},age: 20 }aQuery.prototype.init.prototype = aQuery.prototype;console.log(aQuery().name()) //20
百度借网友的一张图,方便直接理解:
fn解释下,其实这个fn没有什么特殊意思,只是jQuery.prototype的引用
分析二:链式调用
DOM链式调用的处理:
1.节约JS代码.
2.所返回的都是同一个对象,可以提高代码的效率
通过简单扩展原型方法并通过return this的形式来实现跨浏览器的链式调用。
利用JS下的简单工厂模式,来将所有对于同一个DOM对象的操作指定同一个实例。
这个原理就超简单了
aQuery().init().name()分解 a = aQuery(); a.init() a.name()
把代码分解一下,很明显实现链式的基本条件就是实例this的存在,并且是同一个
aQuery.prototype = {init: function() {return this;},name: function() {return this} }
所以我们在需要链式的方法访问this就可以了,因为返回当前实例的this,从而又可以访问自己的原型了
aQuery.init().name()
优点:节省代码量,提高代码的效率,代码看起来更优雅
最糟糕的是所有对象的方法返回的都是对象本身,也就是说没有返回值,这不一定在任何环境下都适合。
Javascript是无阻塞语言,所以他不是没阻塞,而是不能阻塞,所以他需要通过事件来驱动,异步来完成一些本需要阻塞进程的操作,这样处理只是同步链式,异步链式jquery从1.5开始就引入了Promise,jQuery.Deferred后期在讨论。
分析三:插件接口
jQuery的主体框架就是这样,但是根据一般设计者的习惯,如果要为jQuery或者jQuery prototype添加属性方法,同样如果要提供给开发者对方法的扩展,从封装的角度讲是不是应该提供一个接口才对,字面就能看懂是对函数扩展,而不是看上去直接修改prototype.友好的用户接口,
jQuery支持自己扩展属性,这个对外提供了一个接口,jQuery.fn.extend()来对对象增加方法
从jQuery的源码中可以看到,jQuery.extend和jQuery.fn.extend其实是同指向同一方法的不同引用
jQuery.extend = jQuery.fn.extend = function() {
jQuery.extend 对jQuery本身的属性和方法进行了扩展jQuery.fn.extend 对jQuery.fn的属性和方法进行了扩展
通过extend()函数可以方便快速的扩展功能,不会破坏jQuery的原型结构
jQuery.extend = jQuery.fn.extend = function(){...}; 这个是连等,也就是2个指向同一个函数,怎么会实现不同的功能呢?这就是this 力量了!
针对fn与jQuery其实是2个不同的对象,在之前有讲述:
- jQuery.extend 调用的时候,this是指向jQuery对象的(jQuery是函数,也是对象!),所以这里扩展在jQuery上。
- 而jQuery.fn.extend 调用的时候,this指向fn对象,jQuery.fn 和jQuery.prototype指向同一对象,扩展fn就是扩展jQuery.prototype原型对象。
- 这里增加的是原型方法,也就是对象方法了。所以jQuery的api中提供了以上2中扩展函数。
extend的实现
jQuery.extend = jQuery.fn.extend = function() {var src, copyIsArray, copy, name, options, clone,target = arguments[0] || {}, // 常见用法 jQuery.extend( obj1, obj2 ),此时,target为arguments[0]i = 1,length = arguments.length,deep = false;// Handle a deep copy situationif ( typeof target === "boolean" ) { // 如果第一个参数为true,即 jQuery.extend( true, obj1, obj2 ); 的情况deep = target; // 此时target是truetarget = arguments[1] || {}; // target改为 obj1// skip the boolean and the targeti = 2;}// Handle case when target is a string or something (possible in deep copy)if ( typeof target !== "object" && !jQuery.isFunction(target) ) { // 处理奇怪的情况,比如 jQuery.extend( 'hello' , {nick: 'casper})~~target = {};}// extend jQuery itself if only one argument is passedif ( length === i ) { // 处理这种情况 jQuery.extend(obj),或 jQuery.fn.extend( obj )target = this; // jQuery.extend时,this指的是jQuery;jQuery.fn.extend时,this指的是jQuery.fn--i;}for ( ; i < length; i++ ) {// Only deal with non-null/undefined valuesif ( (options = arguments[ i ]) != null ) { // 比如 jQuery.extend( obj1, obj2, obj3, ojb4 ),options则为 obj2、obj3...// Extend the base objectfor ( name in options ) {src = target[ name ];copy = options[ name ];// Prevent never-ending loopif ( target === copy ) { // 防止自引用,不赘述continue;}// Recurse if we're merging plain objects or arrays// 如果是深拷贝,且被拷贝的属性值本身是个对象if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {if ( copyIsArray ) { // 被拷贝的属性值是个数组copyIsArray = false;clone = src && jQuery.isArray(src) ? src : [];} else { 被拷贝的属性值是个plainObject,比如{ nick: 'casper' }clone = src && jQuery.isPlainObject(src) ? src : {};}// Never move original objects, clone themtarget[ name ] = jQuery.extend( deep, clone, copy ); // 递归~// Don't bring in undefined values} else if ( copy !== undefined ) { // 浅拷贝,且属性值不为undefinedtarget[ name ] = copy;}}}}// Return the modified objectreturn target;
总结:
- 通过new jQuery.fn.init() 构建一个新的对象,拥有init构造器的prototype原型对象的方法
- 通过改变prorotype指针的指向,让这个新的对象也指向了jQuery类的原型prototype
- 所以这样构建出来的对象就继续了jQuery.fn原型定义的所有方法了
本文转自艾伦 Aaron博客园博客,原文链接:http://www.cnblogs.com/aaronjs/p/3278578.html,如需转载请自行联系原作者
jQuery 2.0.3 源码分析core - 整体架构相关推荐
- 菜鸟读jQuery 2.0.3 源码分析系列(1)
原文链接在这里,作为一个菜鸟,我就一边读一边写 jQuery 2.0.3 源码分析系列 前面看着差不多了,看到下面一条(我是真菜鸟),推荐木有入门或者刚刚JS入门摸不着边的看看,大大们手下留情,想一起 ...
- jQuery 2.0.3 源码分析 Deferred(最细的实现剖析,带图)
Deferred的概念请看第一篇 http://www.cnblogs.com/aaronjs/p/3348569.html ******************构建Deferred对象时候的流程图* ...
- 最细的实现剖析:jQuery 2.0.3源码分析Deferred
Deferred的概念请看第一篇 http://www.cnblogs.com/aaronjs/p/3348569.html **构建Deferred对象时候的流程图** **源码解析** 因为cal ...
- jQuery 2.0.3 源码分析 事件体系结构
那么jQuery事件处理机制能帮我们处理那些问题? 毋容置疑首先要解决浏览器事件兼容问题 可以在一个事件类型上添加多个事件处理函数,可以一次添加多个事件类型的事件处理函数 提供了常用事件的便捷方法 支 ...
- jQuery 2.0.3 源码分析Sizzle引擎 - 解析原理
声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 先来回答博友的提问: 如何解析 div > p + div.aaron input[type="checkb ...
- jQuery源码分析系列 : 整体架构
query这么多年了分析都写烂了,老早以前就拜读过, 不过这几年都是做移动端,一直御用zepto, 最近抽出点时间把jquery又给扫一遍 我也不会照本宣科的翻译源码,结合自己的实际经验一起拜读吧! ...
- Nmap源码分析(整体架构)
整体架构 功能目录 docs :相关文档 libdnet-stripped :开源网络接口库 liblinear:开源大型线性分类库 liblua:开源Lua脚本语言库 libnetutil:基本的网 ...
- Mongodb 源码分析:整体架构
最近一直在学习Mongodb的源码,很希望能够搞清楚Mongodb内部的具体实现.从Mongodb中文社区和其他人的博客里面学到了很多, 因此, 开了这个博客希望把自己学到的一些分享给大家. 任何源码 ...
- Android 11.0 Settings源码分析 - 主界面加载
Android 11.0 Settings源码分析 - 主界面加载 本篇主要记录AndroidR Settings源码主界面加载流程,方便后续工作调试其流程. Settings代码路径: packag ...
最新文章
- 浅谈Android系统开发中LOG的使用
- rxandroid 源码分析
- Vue本地执行build之后打开dist目录下index.html正常访问
- 数据访问层,完整的增,删,改,查
- ubuntu的网络配置
- 医疗:ERP进销存系统(8)
- 一些加快 程序运行速度的方法
- python入门教程第三讲_第三讲 使用Template
- usermod命令 用户密码管理 mkpasswd命令
- php 放大镜代码,jQuery实现放大镜效果实例代码_jquery
- flutter字体不跟随系统_Flutter小技巧总结之flutter 适配宽高,字体
- KiCad: 一个电子原理图设计和布局创建套件
- 初学必看 C陷阱与缺陷(第二版)读书笔记
- 同时买票是怎么实现的_如果是你来构建火车票订票系统,你如何实现?
- lint-staged 自动修复格式错误
- 初识华为MDC智能驾驶计算平台
- 为什么中国的程序员喜欢用英文写代码,甚至注释也用英文?
- 《詹姆斯·高斯林Java白皮书1996自译》07:多线程
- 电脑ping手机该怎么玩(很多网友都说电脑ping不通手机)?
- mysql制空快捷键_mysql数据库常用操作
热门文章
- 请写出查询该表中成绩最大值的sql语句_SQL-汇总分析
- bootstrap 两个轮播图冲突_Bootstrap的轮播图样式
- android c聊天功能,Android实现简单C/S聊天室应用
- authy不同账户间不同步_「第七期」shopify产品还能同步到微信小程序销售?看这里...
- 提花原理与计算机,电脑提花袜的设计原理与方法:提花女袜
- 【css】padding 和 margin的区别
- iOS 高可控性日历基础组件 - SKCalendarView 的使用和实现思路的分享
- ESTabBarController
- 20位程序员关于求职的疑问,以及我给出的参考答案
- CesiumLab V1.2 新功能 倾斜数据处理