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()方法返回的是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             // 继承对象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的情况,递归调用本身方法 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 // 不赋值undefined值 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的指向不同。

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

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

    jQuery.fn.extend 是在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 };

    在下一节,介绍jQuery的链式语法。 

  • 相关阅读:
    107. Binary Tree Level Order Traversal II
    108. Convert Sorted Array to Binary Search Tree
    111. Minimum Depth of Binary Tree
    49. Group Anagrams
    使用MALTAB标定实践记录
    442. Find All Duplicates in an Array
    522. Longest Uncommon Subsequence II
    354. Russian Doll Envelopes
    opencv 小任务3 灰度直方图
    opencv 小任务2 灰度
  • 原文地址:https://www.cnblogs.com/simplevoid/p/2915056.html
Copyright © 2011-2022 走看看