• 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 };
    复制代码

  • 相关阅读:
    Eclipse打jar包的方法
    Eclipse中SVN插件的安装
    无法远程访问 MySql Server
    TCP(Socket基础编程)
    安装淘宝镜像cnpm时出现问题及解决方案
    搭建vue脚手架---vue-cli
    padding和margin设置成百分比
    响应式布局
    响应式网页:用em,rem设置网页字体大小自适应
    url两次编码
  • 原文地址:https://www.cnblogs.com/slgkaifa/p/7226972.html
Copyright © 2020-2023  润新知