导读:

本人JS菜鸟一枚,为加强代码美观和编程思想。所以来研究下jQuery,有需要进阶JS的同学很适合阅读此文!我是边看代码(jquery2.2.1),边翻“javascript高级程序设计”写的,有很多基本知识点我都写了书本对应的章节。有分析得不好的还请各位多多指教,更正!

希望我的分析对大家有所帮助,谢谢!

一、代码构成


(function(global, factory){
if ( typeof module === "object" && typeof module.exports === "object" ) {
//模块化环境
}else{
factory( global );
}
})(typeof window !== "undefined" ? window: this, function(window, noGlobal) {
//回调函数 if ( typeof noGlobal === strundefined ) {
window.jQuery = window.$ = jQuery;
}
return jQuery;
});

首先一个优美的JS库都会是在一个匿名函数里去写自己的代码对吧,jQuery也不列外。这其实只要把代码格式化一看,就一目了然。
这个匿名函数接受的是两个参数,global(当期执行作用域链的对象),factory(回调)
  匿名函数 : 他本身是做了一个初始化方法的判断,判断当前JS使用环境是不是采用的模块化开发。如果是再做一些相应的逻辑处理(模块化就不多介绍了,可以自己到网上查询),否则直接执行回调factory并把当前执行的作用域对象当参数传递过去。
  回调函数: factory 所有的JQ方法属性都是在这个回调里实现.里的最后一段代码,就是对外开放方法的接口。

二、jQuery类的构建


开始入正题了,嘎嘎。。
首先我们来看几个jQ的一常用场景方法:

$.get(); $.post(); $.extend();//场景一

这么一看jQuery不就是一个Object对象里面添加了几个方法么,其实确实是的,只不过他是一个Function类型的对象。看下面代码:

$("#id").html(); $(".class").show();//场景二

是的jQuery就是一个Function对象,那么我们就有几个问题了:

  1、在我们印象中jQuery不是一个类库么?
  2、JS中的类不是用构造函数来仿造的吗?
  3、JS构造函数不是都是用new操作符来实例化的么,为什么jQuery不需要使用new来实例化???
要实现无new操作,我们想到可以使用工厂模式。(工厂模式可以参阅 “高级-第六章 面向对象的程序设计” )接下来我们先看看这个function的代码:

// Define a local copy of jQuery
jQuery = function(selector, context) { // The jQuery object is actually just the init constructor 'enhanced'
// Need init if jQuery is called (just allow error to be thrown if not included)
return new jQuery.fn.init(selector, context);
}

是的他的这段代码使用的就是JS中典型的工厂模式,工厂模式是不需new操作符的,我们来把jQuery改成一个典型的工厂模式看看;

var jQuery = function(selector,context){
// jQuery("#id")这种使用场景我们留到选择器的时候再分析 var o = new Object();
o.get = function(){
console.log("get");
};
o.post = function(){
console.log("post");
};
return o;
//或者
retrun {
get : function(){}
,post : function(){}
}
//对jQuery中返回一个 new 实例有疑问的参阅(第5章 引用类型)
}; jQuery.get(); //get
jQuery.post(); //post

嗯,如果改成上面这种方式似乎也可以是吧,但我们仔细看下代码。是不是每次使用我们都需要创建一个新对象,也在内存堆里开辟一个新的内存空间了(堆、栈知识参阅 第4章 变量、作用域和内存问题)那样就影响性能了,所以我们再改写下:

var fn = {
get : function(){}
,name : "cH"
,post : function(){}
,init : function(){
//这是一个内部构造函数
this.age = 18;
console.log(this.age);
console.log(this.name);
}
}; var jQuery = function(selector,context){
return new fn.init();
};
jQuery();

首先解释下为什么又返回是 return new init 而不是 return fn:
  我们如果直接返回fn的话那它就会造成对象引用而且是单实例的。而我们用new实例的话,我们每次使用jQuery都是一个新的实例,这样与其他的实例他就是没有任何干扰的。还有就是考虑到$(".calss").hide().html()这种应用场景,我们也必须考虑使用多实例。
  那我们每次调用不又是new一个实例,他不是又会造成性影响了么?是的,但是我们把fn拿出来了,所以每次new只是在栈里拷贝了一份这个对象的指针,并不像开始一样是在堆里新建了一个对象。所以照样减少了性能开销。
OK,我们运行下代码,发现打this.name属性是undefied,为啥呢?看这个new fn.init(),我们new的init是个实例,在这个实例里的this指向是当前实例的,而name、get这些是fn这个局部变量的,所以this.name当然是undefied咯。那我们再改下代码:

var fn = {
get : function(){
console.log("get");
}
,name : "cH"
,post : function(){}
,init : function(){
//这是一个内部构造函数
this.age = 18;
console.log(this.age);
console.log(this.name);
}
};
//我们把内部构造函数的原型继承fn的属性 (继承 第6章 6.2)
fn.init.prototype = fn; var jQuery = function(selector,context){
return new fn.init();
};
jQuery();
jQuery().get();

这下是正常的了,我们把init这个构造函数的原型继承了fn这个对象。这样他的属性就继承过来了。
我们知道有个这样的属性 jQuery.fn
所以再改下代码:

var jQuery = function(selector,context){
return new jQuery.fn.init();
};
jQuery.fn = {
get : function(){
console.log("get");
}
,name : "cH"
,post : function(){}
,init : function(){
//这是一个内部构造函数
this.age = 18;
console.log(this.age);
console.log(this.name);
}
};
jQuery.fn.init.prototype = jQuery.fn;

这样一看代码就跟源码差不多了,我们对应再来看下jQuery源码:

//构建jQuery类
// Define a local copy of jQuery
jQuery = function(selector, context) { // The jQuery object is actually just the init constructor 'enhanced'
// Need init if jQuery is called (just allow error to be thrown if not included)
return new jQuery.fn.init(selector, context);
}
//定义我们分析时的局部变量
jQuery.fn = jQuery.prototype = {
get :...
,name : ....
,post : ....
}
//定义fn.init方法,内部构造函数
init = jQuery.fn.init = function(selector, context, root) {
var match, elem; // HANDLE: $(""), $(null), $(undefined), $(false)
if (!selector) {
return this;
}
}
//内部构造函数的原型继承
init.prototype = jQuery.fn;

我们发现他定义的局部变量是定义在了jQuery的prototype上了,而fn又引用了他的原型。

1、为啥不用一个单独的局部变量,而是放在了jQuery的prototype上?
2、fn 他这fn也没啥特殊意思,就一个引用。感觉这俩步有点多余?
这两点 望大神解释!!

三、方法拓展(方法扩展接口)


我们把jQuery的使用方法归成了俩大类。

//全局方法类
$.get();
$.post();
... //当前实例类
$("#id").show();
$("#id").css();
...

全局方法类:
  扩展入口:$.extend()
  这个扩展就是直接给我们第一的jQuery构造器添加了一些静态方法  

$.extend({meSay : function(){
console.log("meSay")
}});
$.meSay();//meSay
//这个扩展就相当于这样 jQuery.meSay = function...;

当前实例类:

  扩展入口:$.fn.extend()
  而这个,还记得有个fn的属性吧,而他的所有属性方法是被继承到了当前实例的原型里去了的。所以我们这个扩展就只是给当前实例做的一个扩展。

$.fn.extend({
meSay : function(){
console.log("meSay");
}
});
$("#id").meSay();//meSay
//这个扩展相当于 jQuery.fn.meSay = function...;

看到jQuery的源码,jQuery.extend和jQuery.fn.extend其实是同一个方法的不同引用

jQuery.extend = jQuery.fn.extend = function() {}
//jQuery.extend 对jQuery本身的属性和方法进行了扩展
//jQuery.fn.extend 对jQuery.fn的属性和方法进行了扩展

extend的实现源码

 jQuery.extend = jQuery.fn.extend = function() {
var options, name, src, copy, copyIsArray, clone, target = arguments[0] || {},
i = 1,
length = arguments.length,
deep = false; // Handle a deep copy situation
if (typeof target === "boolean") {
deep = target; // Skip the boolean and the target
target = arguments[i] || {};
i++;
} // Handle case when target is a string or something (possible in deep copy)
if (typeof target !== "object" && !jQuery.isFunction(target)) {
target = {};
} // Extend jQuery itself if only one argument is passed
if (i === length) {
target = this;
i--;
} for (; i < length; i++) { // Only deal with non-null/undefined values
if ((options = arguments[i]) != null) { // Extend the base object
for (name in options) {
src = target[name];
copy = options[name]; // Prevent never-ending loop
if (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 {
clone = src && jQuery.isPlainObject(src) ? src: {};
} // Never move original objects, clone them
target[name] = jQuery.extend(deep, clone, copy); // Don't bring in undefined values
} else if (copy !== undefined) {
target[name] = copy;
}
}
}
} // Return the modified object
return target;
};

jQuery.extend = jQuery.fn.extend = function() {

这个extend函数就是一个升级的深拷贝函数,jQuery内部的方法属性也都是使用此方法扩展出来的。我们看下他的使用场景:

$.extend({"meName" : "cHjQuery"});
$.meName // cHjQuery var d = {name : "d"};
$.extend(d,{age:15});
d //{name :"d",age:15}; var c = $.extend({},{name:"c",age:15});
c //{name :"c",age:15};
//还有第个参数为true的场景,可以查询jQuery API

jQuery的基本架构就是这样的了,总结
  1、所有的工作都是在一个匿名函数内执行,保证了命名空间的安全
  2、使用了工厂模式构建的类
  3、jQuery有一个叫extend的方法,jQuery所有方法属性都是使用此方法扩展出来的
  4、使用的方法分两大类,全局方法类和实例方法类,他们都有自己的对应的扩展入口

本文为原创文章,转载请注明出处!

http://www.cnblogs.com/hrw3c/p/5304849.html

js菜鸟进阶-jQuery源码分析(1)-基本架构的更多相关文章

  1. jQuery源码分析-01总体架构

    1. 总体架构 1.1自调用匿名函数 self-invoking anonymous function 打开jQuery源码,首先你会看到这样的代码结构: (function( window, und ...

  2. jQuery源码分析系列 : 整体架构

    query这么多年了分析都写烂了,老早以前就拜读过, 不过这几年都是做移动端,一直御用zepto, 最近抽出点时间把jquery又给扫一遍 我也不会照本宣科的翻译源码,结合自己的实际经验一起拜读吧! ...

  3. 【菜鸟学习jquery源码】数据缓存与data()

    前言 最近比较烦,深圳的工作还没着落,论文不想弄,烦.....今天看了下jquery的数据缓存的代码,参考着Aaron的源码分析,自己有点理解了,和大家分享下.以后也打算把自己的jquery的学习心得 ...

  4. jquery源码分析之一前言篇

    1.问:jquery源码分析的版本是什么? 答:v3.2.1 2.问:为什么要分析jquery源码? 答:javascript是一切js框架的基础,jquery.es6.vue.angular.rea ...

  5. jQuery源码分析系列

    声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://git ...

  6. [转] jQuery源码分析-如何做jQuery源码分析

    jQuery源码分析系列(持续更新) jQuery的源码有些晦涩难懂,本文分享一些我看源码的方法,每一个模块我基本按照这样的顺序去学习. 当我读到难度的书或者源码时,会和<如何阅读一本书> ...

  7. jQuery 源码分析 8: 回头看jQuery的构造器(jQuery.fn,jQury.prototype,jQuery.fn.init.prototype的分析)

    在第一篇jQuery源码分析中,简单分析了jQuery对象的构造过程,里面提到了jQuery.fn.jQuery.prototype.jQuery.fn.init.prototype的关系. 从代码中 ...

  8. [转]jQuery源码分析系列

    文章转自:jQuery源码分析系列-Aaron 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAaro ...

  9. diff.js 列表对比算法 源码分析

    diff.js列表对比算法 源码分析 npm上的代码可以查看 (https://www.npmjs.com/package/list-diff2) 源码如下: /** * * @param {Arra ...

随机推荐

  1. UVALive-5731

    UVALive-5731 题意 一颗 n - 1 条边的有向树,要求整棵树成为强连通图,一次操作即构建一条路(一笔画), 限制: 新建的路上的所有边必须与原有的边逆向,即构建的路必须在原有的边和点上, ...

  2. JavaSE教程-03Java中分支语句与四种进制转换-思维导图

    思维导图看不清楚时: 1)可以将图片另存为图片,保存在本地来查看 2)右击在新标签中打开放大查看 if语句 a) if语句 基本语法结构: if(关系表达式) { 基本语句体 } 执行流程: 首先判断 ...

  3. 一位菜鸟的java 最基础笔记

    java的特性 简单性(Simple). 结构体系中立(Architecture Neutral). 面向对象(Object Oriented). 易于移植(Portable). 分布式(Distri ...

  4. Unity应用架构设计(11)——一个网络层的构建

    对于客户端应用程序,免不了和远程服务打交道.设计一个良好的『服务层』能帮我们规范和分离业务代码,提高生产效率.服务层最核心的模块一定是怎样发送请求,虽然Mono提供了很多C#网络请求类,诸如WebCl ...

  5. MVC分层含义与开发方式

    真正的服务层是面向数据的,假想一切数据都是从参数获得 控制层是接受页面层数据,再传给服务层,然后将结果返回给页面层的(客户) 页面层是提交格式化的数据的(容易小混乱,无格式,所以要格式化,可以在中间加 ...

  6. Mybatis中javaType和jdbcType对应和CRUD例子

    JDBC Type Java Type CHAR String VARCHAR String LONGVARCHAR String NUMERIC java.math.BigDecimal DECIM ...

  7. python 最佳实践与资源汇总

    python 最佳实践 (部分) 一. 结构化工程 文件 功能 README.rst readme LICENSE 许可证 setup.py 打包和发布管理 requirements.txt 开发依赖 ...

  8. 安装旧版的docker-engine-1.12.6

    执行kubeadm init --api-advertise-addresses=172.16.160.211命令的时候,提示docker版本太新了 想要安装旧版docker,可以使用以下方法: yu ...

  9. Normalize.css介绍,作用,使用方法

    介绍 Normalize.css 是一个很小的CSS文件(V5.0.0版本大小8KB),但它在默认的HTML元素样式上提供了跨浏览器的高度一致性.相比于传统的CSS reset,Normalize.c ...

  10. javaScript高级程序设计笔记 1

    核心  ECMAScript 文档对象模型  DOM 浏览器对象模型 BOM 延迟脚本  defer typeof操作符      判断字符类型  返回   undefined  boolean  s ...