js菜鸟进阶-jQuery源码分析(1)-基本架构
导读:
本人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)-基本架构的更多相关文章
- jQuery源码分析-01总体架构
1. 总体架构 1.1自调用匿名函数 self-invoking anonymous function 打开jQuery源码,首先你会看到这样的代码结构: (function( window, und ...
- jQuery源码分析系列 : 整体架构
query这么多年了分析都写烂了,老早以前就拜读过, 不过这几年都是做移动端,一直御用zepto, 最近抽出点时间把jquery又给扫一遍 我也不会照本宣科的翻译源码,结合自己的实际经验一起拜读吧! ...
- 【菜鸟学习jquery源码】数据缓存与data()
前言 最近比较烦,深圳的工作还没着落,论文不想弄,烦.....今天看了下jquery的数据缓存的代码,参考着Aaron的源码分析,自己有点理解了,和大家分享下.以后也打算把自己的jquery的学习心得 ...
- jquery源码分析之一前言篇
1.问:jquery源码分析的版本是什么? 答:v3.2.1 2.问:为什么要分析jquery源码? 答:javascript是一切js框架的基础,jquery.es6.vue.angular.rea ...
- jQuery源码分析系列
声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://git ...
- [转] jQuery源码分析-如何做jQuery源码分析
jQuery源码分析系列(持续更新) jQuery的源码有些晦涩难懂,本文分享一些我看源码的方法,每一个模块我基本按照这样的顺序去学习. 当我读到难度的书或者源码时,会和<如何阅读一本书> ...
- jQuery 源码分析 8: 回头看jQuery的构造器(jQuery.fn,jQury.prototype,jQuery.fn.init.prototype的分析)
在第一篇jQuery源码分析中,简单分析了jQuery对象的构造过程,里面提到了jQuery.fn.jQuery.prototype.jQuery.fn.init.prototype的关系. 从代码中 ...
- [转]jQuery源码分析系列
文章转自:jQuery源码分析系列-Aaron 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAaro ...
- diff.js 列表对比算法 源码分析
diff.js列表对比算法 源码分析 npm上的代码可以查看 (https://www.npmjs.com/package/list-diff2) 源码如下: /** * * @param {Arra ...
随机推荐
- Qt之添加图标
导读: 在使用Qt Creator编写完应用程序后,设置release版的应用程序图标着实困扰了不少的人.一个漂亮的图标是一个软件的脸,没有一个漂亮的图标,那么这个程序是不完整的.那么我们来看看如何设 ...
- mysql之 innobackupex备份+binlog日志的完全恢复(命令行执行模式)
前言:MySQL的完全恢复,我们可以借助于完整的 备份+binlog 来将数据库恢复到故障点.备份可以是热备与逻辑备份(mysqldump),只要备份与binlog是完整的,都可以实现完全恢复. 1. ...
- NLTK学习笔记(三):NLTK的一些工具
主要总结一下简单的工具:条件频率分布.正则表达式.词干提取器和归并器. 条件分布频率 <自然语言学习>很多地方都用到了条件分布频率,nltk提供了两种常用的接口:FreqDist 和 Co ...
- 微信公众平台开发实战Java版之如何网页授权获取用户基本信息
第一部分:微信授权获取基本信息的介绍 我们首先来看看官方的文档怎么说: 如果用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑. 关于网页授权回调域 ...
- 我的第一个jQuery插件--表格隔行变色
虽然网上有大量的插件供我们去使用,但不一定有一款适合你的,必要的时候还是要自己动手去敲的.下面,开始我的第一个插件... 参考<锋利的JQuery>,JQuery为开发插件增设了俩个方法: ...
- Linux上的防病毒软件ClamAV
Clam AntiVirus(ClamAV)是免费而且开放源代码的防毒软件,软件与病毒码的更新皆由社群免费发布.目前ClamAV主要是使用在由Linux.FreeBSD等Unix-like系统架设的邮 ...
- POJ 1845-Sumdiv 题解(数论,约数和公式,逆元,高中数学)
题目描述 给定A,B,求A^B的所有因数的和,再MOD 9901 输入 一行两个整数 A 和 B. 输出 一行,一个整数 样例输入 2 3 样例输出 15 提示 对于100%的数据满足:0 <= ...
- ecshop图片上传JPEG格式失败问题
在根目录下找到includes文件目录,在其目录中找到cls_image.php打开并找到: $allow_file_types = '|GIF|JPG|JEPG|PNG|BMP|SWF|'; 此处J ...
- B. Karen and Coffee
B. Karen and Coffee time limit per test 2.5 seconds memory limit per test 512 megabytes input standa ...
- css实现选项卡
<style> *{ margin: ; padding: ; text-decoration: none; list-style: none; outline:none; } .box{ ...