zoukankan      html  css  js  c++  java
  • jQuery源代码 框架分析

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

    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.2
    3 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()方法返回的是thiskeyword,该keyword引用的是jQuery的实例。假设在init()中继续使用thiskeyword,也就是将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,能够看到,thiskeyword引用了init函数作用域所在的对象,此时它訪问length属性时,返回的为2。thiskeyword也能訪问上级对象jQuery.fn对象的作用域,所以返回1.7.2,而调用size方法时,返回的是2而不是0。

    这样的设计思路非常easy破坏作用域的独立性,对jQuery框架可能产生消极影响,因此jQuery通过实例化init初始化类型来切割作用域的

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

      这样就能够把init()构造函器中的this和jQuery.fn对象中的thiskeyword隔离开来。

    避免混淆。可是这样的方法带来的还有一个问题是无法訪问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             // 继承对象options
    33 
    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 // 返回改动过后的target 63 return target; 64 };


    注意:尽管jQuery.extend = jQuery.fn.extend 它们是一个方法。可是它们的详细作用是不一样的,由于this的指向不同。

    	function jQuery() {}
    	//使用字面量的方式创建原型对象,这里{}就是对象,是Object,new Object就相当于{}
    	jQuery.fn = jQuery.prototype = {
    		constructor : jQuery,			//强制指向jQuery
    		name : '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 };
    复制代码

  • 相关阅读:
    Study Plan The Twelfth Day
    Study Plan The Fifteenth Day
    Study Plan The Seventeenth Day
    Study Plan The Tenth Day
    Study Plan The Eighth Day
    Study Plan The Eleventh Day
    Study Plan The Sixteenth Day
    Study Plan The Thirteenth Day
    Study Plan The Fourteenth Day
    Study Plan The Ninth Day
  • 原文地址:https://www.cnblogs.com/slgkaifa/p/7226972.html
Copyright © 2011-2022 走看看