JQuery源码分析(二)
立即调用表达式:
任何库与框架设计的第一个要点就是解决命名空间与变量污染的问题。jQuery就是利用了JavaScript函数作用域的特性,采用立即调用表达式包裹了自身的方法来解决这个问题。
jQuery的立即调用函数表达式的写法有三种:
写法1:
- (function(window, factory) {
- factory(window)
- }(this, function() {
- return function() {
- //jQuery的调用
- }
- }))
可以看出上面的代码中嵌套了2个函数,而且把一个函数作为参数传递到另一个函数中并且执行,这种方法有点复杂,我们简化一下写法:
- var factory = function(){
- return function(){
- //执行方法
- }
- }
- var jQuery = factory();
写法2:
- var factory = function(){
- return function(){
- //执行方法
- }
- }
- var jQuery = factory();
上面的代码效果和方法1是等同的,但是这个factory有点变成了简单的工厂方法模式,需要自己调用,不像是一个单例的jQuery类,所以我们需要改成“自执行”,而不是另外调用。
写法3:
- (function(window, undefined) {
- var jQuery = function() {}
- // ...
- window.jQuery = window.$ = jQuery;
- })(window);
从上面的代码可看出,自动初始化这个函数,让其只构建一次。详细说一下这种写法的优势:
1、window和undefined都是为了减少变量查找所经过的scope作用域。当window通过传递给闭包内部之后,
在闭包内部使用它的时候,可以把它当成一个局部变量,显然比原先在window scope下查找的时候要快一些。
2、undefined也是同样的道理,其实这个undefined并不是JavaScript数据类型的undefined,而是一个普普通通的变量名。
只是因为没给它传递值,它的值就是undefined,undefined并不是JavaScript的保留字。
- Javascript 中的 undefined 并不是作为关键字,因此可以允许用户对其赋值。
我们看一个
- var undefined = '孙丽媛'
- ;(function(window) {
- alert(undefined);//IE8 '孙丽媛'
- })(window)
IE8存在这个问题,当然,大部分浏览器都是不能被修改的
如果函数调用不传递,参数默认就是undefined
- ;(function(window,undefined) {
- //undefined
- })(window)
jQuery为什么要创建这样的一个外层包裹,其原理又是如何?
这里要区分2个概念一个是匿名函数,一个是自执行。顾名思义,匿名函数,就是没有函数名的函数,也就是不存在外部引用。但是是否像下面代码实现呢
- function(){
- //代码逻辑
- }
上面这种写法是错了,声明了它但是又不给名字又没有使用,所以在语法上错误的,那么怎么去执行一个匿名的函数呢?
要调用一个函数,我们必须要有方法定位它、引用它。所以,我们要取一个名字:
- var jQuery = function(){
- //代码逻辑
- }
jQuery使用()将匿名函数括起来,然后后面再加一对小括号(包含参数列表),那么这小括号能把我们的表达式组合分块,
并且每一块(也就是每一对小括号),都有一个返回值。这个返回值实际上也就是小括号中表达式的返回值。
所以,当我们用一对小括号把匿名函数括起来的时候,实际上小括号返回的,就是一个匿名函数的Function对象。
因此,小括号对加上匿名函数就如同有名字的函数般被我们取得它的引用位置了。所以如果在这个引用变量后面再加上参数列表,就会实现普通函数的调用形式。
最后,我们回到写法1看看jQuery利用写法3的写法,然后把整个函数作为参数传递给另外一个函数,
主要是为了判断jQuery在不同平台的下的加载逻辑,主流的库一般都有对 AMD 和 CommonJS 的支持代码,看看jQuery的代码:
- if (typeof module === "object" && typeof module.exports === "object") {
- module.exports = global.document ?
- factory(global, true) :
- function(w) {
- if (!w.document) {
- throw new Error("jQuery requires a window with a document");
- }
- return factory(w);
- };
- } else {
- factory(global);
- }
总结:全局变量是魔鬼, 匿名函数可以有效的保证在页面上写入JavaScript,而不会造成全局变量的污染,通过小括号,让其加载的时候立即初始化,这样就形成了一个单例模式的效果从而只会执行一次。
jQuery的类数组对象结构
为什么是类数组对象呢?
很多人迷惑的jQuery为什么能像数组一样操作,通过对象get方法或者直接通过下标0索引就能转成DOM对象。
首先我们看jQuery的入口都是统一的$, 通过传递参数的不同,实现了9种方法的重载:
- 1. jQuery([selector,[context]])
- 2. jQuery(element)
- 3. jQuery(elementArray)
- 4. jQuery(object)
- 5. jQuery(jQuery object)
- 6. jQuery(html,[ownerDocument])
- 7. jQuery(html,[attributes])
- 8. jQuery()
- 9. jQuery(callback)
9种用法整体来说可以分三大块:选择器、dom的处理、dom加载。
换句话说jQuery就是为了获取DOM、操作DOM而存在的!所以为了更方便这些操作,让节点与实例对象通过一个桥梁给关联起来,
jQuery内部就采用了一种叫“类数组对象”的方式作为存储结构,所以我们即可以像对象一样处理jQuery操作,
也能像数组一样可以使用push、pop、shift、unshift、sort、each、map等类数组的方法操作jQuery对象了。
jQuery对象可用数组下标索引是什么原理?
通过$(".Class")构建的对象结构如下所示:
整个结构很明了,通过对象键值对的关系保存着属性,原型保存着方法。我们来简单的模拟一个这样的数据结构:
以上是模拟jQuery的对象结构,通过aQuery方法抽象出了对象创建的具体过程,这也是软件工程领域中的广为人知的设计模式-工厂方法。
代码如下:
- <!DOCTYPE HTML>
- <html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <script src="http://img.mukewang.com/down/540812440001e40e00000000.js" type="text/javascript"></script>
- <style type="text/css">
- div{
- width: 30px;
- height: 10px;
- float:left;
- }
- </style>
- <title>无标题文档</title>
- </head>
- <body>
- <button id="test1">jQuey[0]</button>
- <button id="test2">jQuey.get</button>
- <button id="test3">aQuery[0]</button>
- <button id="test4">aQuery.get</button>
- <p id="book">book</p>
- <div id="show1"></div>
- <div id="show2"></div>
- <div id="show3"></div>
- <div id="show4"></div>
- <script type="text/javascript">
- var aQuery = function(selector) {
- //强制为对象
- if (!(this instanceof aQuery)) {
- return new aQuery(selector);
- }
- var elem = document.getElementById(/[^#].*/.exec(selector)[0]);
- this.length = 1;
- this[0] = elem;
- this.context = document;
- this.selector = selector;
- this.get = function(num) {
- return this[num];
- }
- return this;
- }
- //结果是一个dom元素,可以把代码放到Google Chrome下运行
- //按F12通过调试命令 console.log() 打印出对象
- $("#test1").click(function() {
- $('#show1').append($('#book')[0])
- })
- $("#test2").click(function() {
- $('#show2').append($('#book').get(0))
- })
- $("#test3").click(function() {
- $('#show3').append(aQuery("#book")[0])
- })
- $("#test4").click(function() {
- $('#show4').append(aQuery("#book").get(0))
- })
- </script>
- </body>
- </html>
jQuery的无new构建原理
函数aQuery()内部首先保证了必须是通过new操作符构建。这样就能保证当前构建的是一个带有this的实例对象,
既然是对象我们可以把所有的属性与方法作为对象的key与value的方式给映射到this上,
所以如上结构就可以模拟出jQuery的这样的操作了,即可通过索引取值,也可以链式方法取值,但是这样的结构是有很大的缺陷的,
每次调用ajQuery方法等于是创建了一个新的实例,那么类似get方法就要在每一个实例上重新创建一遍,
性能就大打折扣,所以jQuery在结构上的优化不仅仅只是我们看到的,除了实现类数组结构、方法的原型共享,
而且还实现方法的静态与实例的共存,这是我们之后将会重点分析的。
JQuery源码分析(二)的更多相关文章
- jquery源码分析(二)——架构设计
要学习一个库首先的理清它整体架构: 1.jQuery源码大致架构如下:(基于 jQuery 1.11 版本,共计8829行源码)(21,94) 定义了一些变量和函数jQu ...
- jQuery 源码分析(二十一) DOM操作模块 删除元素 详解
本节说一下DOM操作模块里的删除元素模块,该模块用于删除DOM里的某个节点,也可以理解为将该节点从DOM树中卸载掉,如果该节点有绑定事件,我们可以选择保留或删除这些事件,删除元素的接口有如下三个: e ...
- jQuery 源码分析(二十) DOM操作模块 插入元素 详解
jQuery的DOM操作模块封装了DOM模型的insertBefore().appendChild().removeChild().cloneNode().replaceChild()等原生方法.分为 ...
- jQuery 源码分析(二) 入口模块
jQuery返回的对象本质上是一个JavaScript对象,而入口模块则可以保存对应的节点的引用,然后供其它模块操作 我们创建jQuery对象时可以给jQuery传递各种不同的选择器,如下: fals ...
- jQuery源码分析系列
声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://git ...
- [转]jQuery源码分析系列
文章转自:jQuery源码分析系列-Aaron 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAaro ...
- jQuery源码分析系列(转载来源Aaron.)
声明:非本文原创文章,转载来源原文链接Aaron. 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAa ...
- jQuery源码分析系列——来自Aaron
jQuery源码分析系列——来自Aaron 转载地址:http://www.cnblogs.com/aaronjs/p/3279314.html 版本截止到2013.8.24 jQuery官方发布最新 ...
- Fresco 源码分析(二) Fresco客户端与服务端交互(1) 解决遗留的Q1问题
4.2 Fresco客户端与服务端的交互(一) 解决Q1问题 从这篇博客开始,我们开始讨论客户端与服务端是如何交互的,这个交互的入口,我们从Q1问题入手(博客按照这样的问题入手,是因为当时我也是从这里 ...
- [转] jQuery源码分析-如何做jQuery源码分析
jQuery源码分析系列(持续更新) jQuery的源码有些晦涩难懂,本文分享一些我看源码的方法,每一个模块我基本按照这样的顺序去学习. 当我读到难度的书或者源码时,会和<如何阅读一本书> ...
随机推荐
- ARM汇编中的标号
标号(LABEL)是为一组机器指令所起名字,表示程序中的指令或者数据地址的符号.标号可有可无,只有当需要用符号地址来访问该语句时,才给此语句赋予标号.通过在目标地址的前面放上一个标号,可以在指令中使用 ...
- iOS项目的目录结构和开发流程
转自无网不剩的博客 网上相关的资源不多,开源的且质量还不错的iOS项目也是少之又少,最近正好跟同事合作了一个iOS项目,来说说自己的一些想法. 目录结构 AppDelegate Models Ma ...
- lucene Lock obtain timed out: Lock@
出错界面: 解决办法: 出现以上异常主要有两种原因: 1.系统正在写索引未完成之前,应用程序关闭 解决方法:删除提示的lock文件后重启应用(最好在应用中捕捉到,自动删除) 2.系统中有多个线程或程序 ...
- 51nod 1613翻硬币
题目链接:51nod 1613 翻硬币 知乎上的理论解法http://www.zhihu.com/question/26570175/answer/33312310 本题精髓在于奇偶性讨论. 若 n ...
- spring关于urlpattern
视图解析器(ViewResolver)注册中央调度器定制处理器jsp页面搭建springmvc.xml配置效果图第一个案例提升----视图解析器关于urlpattern说法最好配成*.do 不能配成/ ...
- php提示Fatal error: Call to undefined function imagecreate()
在php中imagecreate函数是一个图形处理函数,主要用于新建一个基于调色板的图像了,然后在这个基础上我们可以创建一些图形数字字符之类的,但这个函数需要GD库支持,如果没有开启GD库使用时会 / ...
- OLAP 模型
OLAP分析的基础是多维数据集,按照其数据存储格式的不同可以分为关系型OLAP(Relational OLAP,ROLAP)和多维型OLAP(Multidimensional OLAP,MOLAP). ...
- Linux查看程序端口占用情况【转】
今天发现服务器上Tomcat 8080端口起不来,老提示端口已经被占用. 使用命令: ps -aux | grep tomcat 发现并没有8080端口的Tomcat进程. 使用命令:netstat ...
- 快餐问题(dp好题)
Peter最近在R市开了一家快餐店,为了招揽顾客,该快餐店准备推出一种套餐,该套餐由A个 汉堡,B个薯条和C个饮料组成.价格便宜.为了提高产量,Peter从著名的麦当劳公司引进了N条 生产线.所有的生 ...
- C#保存登录用户名供其他页面调用
一.保存登录用户名供其他页面调用 步骤: (1)项目自带的Program.cs,类方法里定义登录的用户名为全局变量loginid,这样整个项目都可以调用它 static class Program { ...