jQuery源码 框架分析(一)
2013-02-23 17:16
211 查看
每一个框架都有一个核心,所有的结构都是基于这个核心之上,结构建立好了之后,剩下的就是功能的堆砌。
jQuery的核心就是从HTML文档中匹配元素并对其操作。
就跟一座大楼一样,让我们一步一步了解这座大厦的基石和结构。
1.构造函数
2.链式语法
3.选择器
4.扩展性
一、构造函数
我们知道类是面向对象编程的一个重要概念,它是对事物的最高抽象,它是一个模型。通过实例化一个类,我们可以创建一个实例。
javascript本身没有类的概念,只有原型prototype,prototype是什么呢?它是为构造函数设置的一个属性,这个属性包含一个对象(简称prototype对象)。这个对象中包含了实例对象共享的属性(properties)和方法(methods)。有了这么一个属性,我们就可以让所有的实例共享某些东西,来实现面向对象中类的概念。
下面用一个例子来解释一下,链接--------------
因此jQuery利用原型继承来实现类的
那我们如何调用它吗?
但是jQuery并不是这么调用的,它类似$().jquery 这种形式。
也就是说jQuery没用new实例化,而是直接调用jQuery()函数,然后后面跟jQuery的原型方法。怎么实现呢?
如果按照上面的做法会出错,内存溢出,因为创建实例的时候循环引用导致出错。
我们需要返回一个实例,我们知道在new一个对象的时候,this指向new的实例,实例获取了prototype的属性方法。
因此我们可以用工厂方法创建一个实例,把这个方法放在jQuery.prototype 的原型对象当中,然后在jQuery函数中返回这个原型方法的调用。
console中会看到this对象是 jQuery的一个实例。
init()方法返回的是this关键字,该关键字引用的是jQuery的实例,如果在init()中继续使用this关键字,也就是将init函数视为一个构造器,this又是如何处理呢?
返回的都是2,可以看到,this关键字引用了init函数作用域所在的对象,此时它访问length属性时,返回的为2。this关键字也能访问上级对象jQuery.fn对象的作用域,所以返回1.7.2,而调用size方法时,返回的是2而不是0。
这种设计思路很容易破坏作用域的独立性,对jQuery框架可能产生消极影响,因此jQuery通过实例化init初始化类型来分割作用域的
这样就可以把init()构造函器中的this和jQuery.fn对象中的this关键字隔离开来。避免混淆。但是这种方法带来的另一个问题是无法访问jQuery.fn 的对象的属性和方法。
Object [object Object] has no method 'size'.
如何做到既能分割初始化构造函数与jQuery原型对象的作用域,又能够在返回实例中访问jQuery原型对象呢?
jQuery框架巧妙地通过原型传递解决了这个问题
这样 new jQuery.fn.init() 创建的新对象拥有init构造器的prototype原型对象的方法,通过改变prototype指针的指向,使其指向jQuery类的prototype,这样创造出来的对象就继承了jQuery.fn原型对象定义的方法。
二、扩展性
jQuery 自定义扩展方法用的extend () 函数
在讲源码之前,先说一下什么是拷贝,浅拷贝,深拷贝。
我们知道js 种不同的数据类型
* 基本类型:按值传递 (undefined,NULL,boolean,String,Number)
* 引用类型:传递内存地址 Object
/* 深度拷贝,所有的元素和属性完全clone,并与原引用对象完全独立,克隆后的对象与原对象再也没有任何关系,也就是当你拷贝完成后,原对象值有任何更改,都不会影响到我们克隆后那个对象的值*/
所以我们在进行深拷贝(clone)的时候,注意将复制对象中的每一个值,而不是引用,换句话说,就是采用递归的方法浅拷贝对象。
1.浅拷贝
2.深拷贝
3.jQuery 的实现
注意:虽然jQuery.extend = jQuery.fn.extend 它们是一个方法,但是它们的具体作用是不一样的,因为this的指向不同。
在构造函数那个模块我们看到
jQuery .extend 的 this 是jQuery类本身,在jQuery类上添加(对象,方法)
jQuery.fn.extend 是在jQuery的原型上添加新的(方法,对象),返回的jQuery对象会拥有这个方法。jQuery.fn = jQuery.prototype
四、来一个简化版的
在下一节,介绍jQuery的链式语法。
jQuery的核心就是从HTML文档中匹配元素并对其操作。
就跟一座大楼一样,让我们一步一步了解这座大厦的基石和结构。
1.构造函数
2.链式语法
3.选择器
4.扩展性
一、构造函数
我们知道类是面向对象编程的一个重要概念,它是对事物的最高抽象,它是一个模型。通过实例化一个类,我们可以创建一个实例。
javascript本身没有类的概念,只有原型prototype,prototype是什么呢?它是为构造函数设置的一个属性,这个属性包含一个对象(简称prototype对象)。这个对象中包含了实例对象共享的属性(properties)和方法(methods)。有了这么一个属性,我们就可以让所有的实例共享某些东西,来实现面向对象中类的概念。
下面用一个例子来解释一下,链接--------------
因此jQuery利用原型继承来实现类的
var jQuery = function(){} jQuery.prototype = { //扩展的原型对象 } //由于prototype名称太长,我们可以另起一名fn jQuery.fn = jQuery.prototype = { //扩展的原型对象 } /********************我是分割线************************/ //同理jQuery也可以用别名$来代替,因此代码可以写成 var $ = jQuery = function(){}; jQuery.fn = jQuery.prototype = { //扩展的原型对象 jquery : "1.7.2", size : function(){ return this.length; } }
那我们如何调用它吗?
var my$ = new $();//实例化 console.log(my$.jquery);//1.7.2 console.log(my$.size());//undefined
但是jQuery并不是这么调用的,它类似$().jquery 这种形式。
也就是说jQuery没用new实例化,而是直接调用jQuery()函数,然后后面跟jQuery的原型方法。怎么实现呢?
var $ = jQuery = function(){ return new jQuery(); }; jQuery.fn = jQuery.prototype = { jquery : "1.7.2", size : function(){ return this.length; } } console.log($().jquery); console.log($().size());
如果按照上面的做法会出错,内存溢出,因为创建实例的时候循环引用导致出错。
我们需要返回一个实例,我们知道在new一个对象的时候,this指向new的实例,实例获取了prototype的属性方法。
因此我们可以用工厂方法创建一个实例,把这个方法放在jQuery.prototype 的原型对象当中,然后在jQuery函数中返回这个原型方法的调用。
var $ = jQuery = function(){ return jQuery.fn.init(); }; jQuery.fn = jQuery.prototype = { init: function(){ console.log(this); return this;//返回实例的引用 }, jquery : "1.7.2", size : function(){ return this.length; } } console.log($().jquery); console.log($().size());
console中会看到this对象是 jQuery的一个实例。
init()方法返回的是this关键字,该关键字引用的是jQuery的实例,如果在init()中继续使用this关键字,也就是将init函数视为一个构造器,this又是如何处理呢?
var $ = jQuery = function(){ return jQuery.fn.init(); }; jQuery.fn = jQuery.prototype = { init: function(){ this.length = 2; this.test = function(){ return this.length; } return this; }, jquery : "1.7.2", length:0, size : function(){ return this.length; } } console.log($().jquery); console.log($().test()); //2 ? 0 ? console.log($().size()); //2 ? 0 ?
返回的都是2,可以看到,this关键字引用了init函数作用域所在的对象,此时它访问length属性时,返回的为2。this关键字也能访问上级对象jQuery.fn对象的作用域,所以返回1.7.2,而调用size方法时,返回的是2而不是0。
这种设计思路很容易破坏作用域的独立性,对jQuery框架可能产生消极影响,因此jQuery通过实例化init初始化类型来分割作用域的
var $ = jQuery = function(){ return new jQuery.fn.init(); };
这样就可以把init()构造函器中的this和jQuery.fn对象中的this关键字隔离开来。避免混淆。但是这种方法带来的另一个问题是无法访问jQuery.fn 的对象的属性和方法。
Object [object Object] has no method 'size'.
如何做到既能分割初始化构造函数与jQuery原型对象的作用域,又能够在返回实例中访问jQuery原型对象呢?
jQuery框架巧妙地通过原型传递解决了这个问题
jQuery.fn.init.prototype = jQuery.fn;//使用jQuery原型对象覆盖init原型对象
这样 new jQuery.fn.init() 创建的新对象拥有init构造器的prototype原型对象的方法,通过改变prototype指针的指向,使其指向jQuery类的prototype,这样创造出来的对象就继承了jQuery.fn原型对象定义的方法。
二、扩展性
jQuery 自定义扩展方法用的extend () 函数
jQuery.extend = jQuery.fn.extend = function() { //code }
在讲源码之前,先说一下什么是拷贝,浅拷贝,深拷贝。
我们知道js 种不同的数据类型
* 基本类型:按值传递 (undefined,NULL,boolean,String,Number)
* 引用类型:传递内存地址 Object
/* 深度拷贝,所有的元素和属性完全clone,并与原引用对象完全独立,克隆后的对象与原对象再也没有任何关系,也就是当你拷贝完成后,原对象值有任何更改,都不会影响到我们克隆后那个对象的值*/
所以我们在进行深拷贝(clone)的时候,注意将复制对象中的每一个值,而不是引用,换句话说,就是采用递归的方法浅拷贝对象。
1.浅拷贝
var clone = _.clone = function(obj){ //不是对象直接放回返回值 if (typeof obj != 'object') return obj; var result; //数组用slice方法 不改变原数组 if(Object.prototype.toString.call(obj)==="[Object Array]"){ result = obj.slice(); }else{ //对象 for遍历 result = {}; for(var name in obj){ result[name] = object[name]; } } return result; }
2.深拷贝
var _deepClone = function(source){ if(source===null) return null; var result; if(source instanceof Array){ result = []; //如果是数组,递归调用 for(var i = 0;i<source.length;i++){ result[i] = _deepClone(source[i]); } return result; }else if(source instanceof Object){ //如果是对象,递归调用 result = {}; for(var name in source){ result[name] = _deepClone(source[name]); } return result; } else{ //如果都不是就返回值 return source; } };
3.jQuery 的实现
jQuery.extend = jQuery.fn.extend = function() { //所有使用的的变量最好最好在函数定义的最前就写下来,原因与ECMA有关 详解 var options, name, src, copy, copyIsArray, clone, target = arguments[0] || {}, i = 1, length = arguments.length, deep = false; // 判断是否为深拷贝|浅拷贝 if ( typeof target === "boolean" ) { deep = target; target = arguments[1] || {}; //返回的目标对象 // 遍历的时候跳过 deep | target 参数 i = 2; } // 如果初始值不为对象 且不是一个函数则置空,比如一个string ""置为{}; if ( typeof target !== "object" && !jQuery.isFunction(target) ) { target = {}; } // 如果只有自己一个参数,而没有被克隆继承的参数,则返回自己本身 if ( length === i ) { target = this; --i; } for ( ; i < length; i++ ) { // 处理值为null undefined情况 if ( (options = arguments[ i ]) != null ) { // 继承对象options for ( name in options ) { src = target[ name ]; //原对象中对应的值 copy = options[ name ]; //需要拷贝的对象中对应的值 // 防止陷入死循环,如果原对象本身就等于需要拷贝的对象中的那值(o), //在对o遍历的时候就把自己重新遍历赋值了一遍 if ( target === copy ) { continue; } //在 Array和Object的情况,递归调用本身方法 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 : {}; } target[ name ] = jQuery.extend( deep, clone, copy ); // 不赋值undefined值 } else if ( copy !== undefined ) { target[ name ] = copy; } } } } // 返回修改过后的target return target; };
注意:虽然jQuery.extend = jQuery.fn.extend 它们是一个方法,但是它们的具体作用是不一样的,因为this的指向不同。
在构造函数那个模块我们看到
jQuery .extend 的 this 是jQuery类本身,在jQuery类上添加(对象,方法)
jQuery.fn.extend 是在jQuery的原型上添加新的(方法,对象),返回的jQuery对象会拥有这个方法。jQuery.fn = jQuery.prototype
四、来一个简化版的
var _deepClone = function(source){ if(source===null) return null; var result; if(source instanceof Array){ result = []; //如果是数组,递归调用 for(var i = 0;i<source.length;i++){ result[i] = _deepClone(source[i]); } return result; }else if(source instanceof Object){ //如果是对象,递归调用 result = {}; for(var name in source){ result[name] = _deepClone(source[name]); } return result; } else{ return source; } };
在下一节,介绍jQuery的链式语法。
相关文章推荐
- 读jQuery源码之整体框架分析
- jQuery源码 框架分析
- jQuery源码分析:如何解决jQuery、$关键字与其它脚本框架的冲突问题?
- jquery源码分析--1.框架介绍
- jQuery源码 框架分析
- 006 - 读jQuery源码之整体框架分析(一)
- jQuery1.6.2源码分析(一)框架体系研究
- jquery源码分析(1)---框架结构
- jQuery源码分析(版本1.6.1)___总体架构
- jQuery源码分析之正则表达式
- 【集合框架】JDK1.8源码分析之HashMap & LinkedHashMap迭代器(三)
- 高性能网络I/O框架-netmap源码分析(6) http://blog.chinaunix.net/uid-23629988-id-3803045.html
- jQuery源码分析 (init)
- Jquery源码分析与简单模拟实现
- jQuery-1.9.1源码分析系列(三) Sizzle选择器引擎——总结与性能分析
- jQuery-1.9.1源码分析系列(十一) DOM操作
- jQuery源码分析之ready方法
- SSH框架总结(框架分析+环境搭建+实例源码下载)
- jQuery-1.9.1源码分析系列(十) 事件系统——事件体系结构
- SSH框架总结(框架分析+环境搭建+实例源码下载)