jQuery源码 框架分析

来源:互联网 发布:java启动cmd命令 编辑:程序博客网 时间:2024/06/05 20:59

每一个框架都有一个核心,所有的结构都是基于这个核心之上,结构建立好了之后,剩下的就是功能的堆砌。

jQuery的核心就是从HTML文档中匹配元素并对其操作。

就跟一座大楼一样,让我们一步一步了解这座大厦的基石和结构。


1.构造函数

2.链式语法

3.选择器

4.扩展性


 一、构造函数

  我们知道类是面向对象编程的一个重要概念,它是对事物的最高抽象,它是一个模型。通过实例化一个类,我们可以创建一个实例。

javascript本身没有类的概念,只有原型prototype,prototype是什么呢?它是为构造函数设置的一个属性,这个属性包含一 个对象(简称prototype对象)。这个对象中包含了实例对象共享的属性(properties)和方法(methods)。有了这么一个属性,我们 就可以让所有的实例共享某些东西,来实现面向对象中类的概念。

下面用一个例子来解释一下,链接--------------

因此jQuery利用原型继承来实现类的

 1 var jQuery = function(){} 2  3 jQuery.prototype = { 4     //扩展的原型对象 5 } 6 //由于prototype名称太长,我们可以另起一名fn 7 jQuery.fn = jQuery.prototype = { 8     //扩展的原型对象 9 }10 /********************我是分割线************************/11 //同理jQuery也可以用别名$来代替,因此代码可以写成12 var $ = jQuery = function(){};13 jQuery.fn = jQuery.prototype = {14     //扩展的原型对象15     jquery : "1.7.2",16     size : function(){17         return this.length;18     }19 }

那我们如何调用它吗?

1 var my$ = new $();//实例化2 console.log(my$.jquery);//1.7.23 console.log(my$.size());//undefined

但是jQuery并不是这么调用的,它类似$().jquery 这种形式。

也就是说jQuery没用new实例化,而是直接调用jQuery()函数,然后后面跟jQuery的原型方法。怎么实现呢?

 1 var $ = jQuery = function(){ 2     return  new jQuery(); 3 };     4 jQuery.fn = jQuery.prototype = { 5      6     jquery : "1.7.2", 7     size : function(){ 8         return this.length; 9     }10 }11 12 console.log($().jquery);13 console.log($().size());

如果按照上面的做法会出错,内存溢出,因为创建实例的时候循环引用导致出错。 

我们需要返回一个实例,我们知道在new一个对象的时候,this指向new的实例,实例获取了prototype的属性方法。

因此我们可以用工厂方法创建一个实例,把这个方法放在jQuery.prototype 的原型对象当中,然后在jQuery函数中返回这个原型方法的调用。

 1 var $ = jQuery = function(){ 2     return  jQuery.fn.init(); 3 };     4 jQuery.fn = jQuery.prototype = { 5     init: function(){ 6         console.log(this); 7         return this;//返回实例的引用 8     }, 9     jquery : "1.7.2",10     size : function(){11         return this.length;12     }13 }14 console.log($().jquery);15 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初始化类型来分割作用域的

1 var $ = jQuery = function(){2     return new jQuery.fn.init();3 };    

  这样就可以把init()构造函器中的this和jQuery.fn对象中的this关键字隔离开来。避免混淆。但是这种方法带来的另一个问题是无法访问jQuery.fn 的对象的属性和方法。

Object [object Object] has no method 'size'.

如何做到既能分割初始化构造函数与jQuery原型对象的作用域,又能够在返回实例中访问jQuery原型对象呢?

jQuery框架巧妙地通过原型传递解决了这个问题

 

1 jQuery.fn.init.prototype = jQuery.fn;//使用jQuery原型对象覆盖init原型对象

 

  这样 new jQuery.fn.init() 创建的新对象拥有init构造器的prototype原型对象的方法,通过改变prototype指针的指向,使其指向jQuery类的prototype,这样创造出来的对象就继承了jQuery.fn原型对象定义的方法。

 二、扩展性


  jQuery 自定义扩展方法用的extend () 函数

1 jQuery.extend = jQuery.fn.extend = function() {2     //code    3 }

在讲源码之前,先说一下什么是拷贝,浅拷贝,深拷贝。

我们知道js 种不同的数据类型

基本类型:按值传递  (undefined,NULL,boolean,String,Number)
引用类型:传递内存地址 Object


/* 深度拷贝,所有的元素和属性完全clone,并与原引用对象完全独立,克隆后的对象与原对象再也没有任何关系,也就是当你拷贝完成后,原对象值有任何更改,都不会影响到我们克隆后那个对象的值*/

所以我们在进行深拷贝(clone)的时候,注意将复制对象中的每一个值,而不是引用,换句话说,就是采用递归的方法浅拷贝对象。

1.浅拷贝

 1 var clone = _.clone = function(obj){ 2     //不是对象直接放回返回值 3     if (typeof obj != 'object') return obj; 4     var result; 5     //数组用slice方法 不改变原数组 6     if(Object.prototype.toString.call(obj)==="[Object Array]"){ 7         result =  obj.slice(); 8     }else{ 9         //对象 for遍历10         result = {};11         for(var name in obj){12             result[name] = object[name];13         }14     }15     return result;16 }

2.深拷贝

 1 var _deepClone = function(source){ 2     if(source===null) return null; 3     var result; 4     if(source instanceof Array){ 5         result = []; 6         //如果是数组,递归调用 7         for(var i = 0;i<source.length;i++){ 8             result[i] = _deepClone(source[i]); 9         }10         return result;11     }else if(source instanceof Object){12         //如果是对象,递归调用13         result = {};14         for(var name in source){15             result[name] = _deepClone(source[name]);16         }17         return result;18     }19     else{20         //如果都不是就返回值21         return source;22     }23 };

3.jQuery 的实现

 1 jQuery.extend = jQuery.fn.extend = function() { 2     //所有使用的的变量最好最好在函数定义的最前就写下来,原因与ECMA有关 详解 3  4     var options, name, src, copy, copyIsArray, clone, 5         target = arguments[0] || {}, 6         i = 1, 7         length = arguments.length, 8         deep = false; 9 10     // 判断是否为深拷贝|浅拷贝11     if ( typeof target === "boolean" ) {12         deep = target;13         target = arguments[1] || {};    //返回的目标对象14         // 遍历的时候跳过 deep | target 参数15         i = 2;16     }17 18     // 如果初始值不为对象 且不是一个函数则置空,比如一个string ""置为{};19     if ( typeof target !== "object" && !jQuery.isFunction(target) ) {20         target = {};21     }22 23     // 如果只有自己一个参数,而没有被克隆继承的参数,则返回自己本身24     if ( length === i ) {25         target = this;26         --i;27     }28 29     for ( ; i < length; i++ ) {30         // 处理值为null undefined情况31         if ( (options = arguments[ i ]) != null ) {32             // 继承对象options33 34             for ( name in options ) {35                 src = target[ name ];       //原对象中对应的值36                 copy = options[ name ];     //需要拷贝的对象中对应的值37 38                 // 防止陷入死循环,如果原对象本身就等于需要拷贝的对象中的那值(o),            //在对o遍历的时候就把自己重新遍历赋值了一遍39                 if ( target === copy ) {40                     continue;41                 }42 43                 //在 Array和Object的情况,且deep为true和传进对象有值(true)的情况下,递归调用本身方法进行深拷贝44                 if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {45                     if ( copyIsArray ) {//如果需要拷贝的对象为数组46                         copyIsArray = false;47                         clone = src && jQuery.isArray(src) ? src : [];48 49                     } else {50                         clone = src && jQuery.isPlainObject(src) ? src : {};51                     }52 53                     target[ name ] = jQuery.extend( deep, clone, copy );54                     // 如果不是上述情况,则进行浅拷贝,我们在执行jQuery.extend({})或jQuery.fn.extend({})的时候都是执行的这个方法,但却搞不明白为什么一个是对JQuery类的自定义扩展,一个是JQuery对象的自定义扩展,那么这里的target究竟代表什么呢,我们看下面的例子55                 } else if ( copy !== undefined ) {56                     target[ name ] = copy;57                 }58             }59         }60     }61 62     // 返回修改过后的target63     return target;64 };

注意:虽然jQuery.extend = jQuery.fn.extend 它们是一个方法,但是它们的具体作用是不一样的,因为this的指向不同。

function jQuery() {}//使用字面量的方式创建原型对象,这里{}就是对象,是Object,new Object就相当于{}jQuery.fn = jQuery.prototype = {constructor : jQuery,//强制指向jQueryname : 'Lee', age : 100,run : function () {return this.name + this.age + '运行中...';}};jQuery.extend = jQuery.fn.extend = function(){var option = arguments[0] ;for(var v in option){this[v] = option[v];}return this;};var jquery = new jQuery();document.write("<p style='color:blue'>"+jQuery.extend({add:function(){alert("aaaa");}})+"</p>");document.write("<p style='color:blue'>"+jQuery.fn.extend({minu:function(){alert("bbbb");}})+"</p>");jQuery.add();jquery.minu();
this打印结果

function jQuery() {}

[object Object]


在构造函数那个模块我们看到

jQuery .extend 的this 是jQuery类本身,在jQuery类上添加(对象,方法)

jQuery.fn.extend 的this是jQuery的原型,在jQuery原型上添加(对象,方法),那么JQuery对象本身也会具有哪些方法

jQuery.fn = jQuery.prototype

 

四、来一个简化版的

 1 var _deepClone = function(source){ 2     if(source===null) return null; 3     var result; 4     if(source instanceof Array){ 5         result = []; 6         //如果是数组,递归调用 7         for(var i = 0;i<source.length;i++){ 8             result[i] = _deepClone(source[i]); 9         }10         return result;11     }else if(source instanceof Object){12         //如果是对象,递归调用13         result = {};14         for(var name in source){15             result[name] = _deepClone(source[name]);16         }17         return result;18     }19     else{20         return source;21     }22 };
复制代码

0 1
原创粉丝点击