【jQuery源码】总体架构

jQuery源码能够精简为如下内容:javascript

  方框上面的代码根据Jq注释咱们能够知道是对AMD规范的支持。css

  jQuery总体上被包裹在一个匿名函数中,这个匿名函数再做为另外一个匿名函数的参数被传入,形参factory。html

 

1、自调用函数java

  整个部分被自调用函数包裹,经过定义一个匿名函数,建立了一个“私有”的命名空间,该命名空间的变量和方法,不会破坏全局的命名空间。jquery

  自调用函数中的第一个参数至关于window,经过传入window变量,使得window由全局变量变为局部变量,当在jQuery代码块中访问window时,不须要将做用域链回退到顶层做用域,这样能够更快的访问window;这还不是关键所在,更重要的是,将window做为参数传入,能够在压缩代码时进行优化。编程

  第二个参数为包裹jQuery的匿名函数,在匿名参数中被调用——factory()。api

 

2、jQuery的无new构建数组

  jQuery框架的核心就是从HTML文档中匹配元素并对其执行操做、浏览器

  例如:框架

$().find().css()
$().hide().html('....').hide().

  从上面的写法上至少能够发现2个问题

  1. jQuery对象的构建方式

  2 .jQuery方法的调用方式

 

分析一:jQuery的无new构建

  JavaScript是函数式语言,函数能够实现类,类就是面向对象编程中最基本的概念

复制代码
复制代码
var aQuery = function(selector, context) {
        //构造函数
}
aQuery.prototype = {
    //原型
    name:function(){},
    age:function(){}
}

var a = new aQuery();

a.name();
复制代码
复制代码

  这是常规的使用方法,显而易见jQuery不是这样玩的

  jQuery没有使用new运行符将jQuery显示的实例化,仍是直接调用其函数

  按照jQuery的抒写方式

$().ready() 
$().noConflict()

  要实现这样,那么jQuery就要当作一个类,那么$()应该是返回类的实例才对

  因此把代码改一下:

复制代码
复制代码
var aQuery = function(selector, context) {
       return new aQuery();
}
aQuery.prototype = {
    name:function(){},
    age:function(){}
}
复制代码
复制代码

  经过new aQuery(),虽然返回的是一个实例,可是也能看出很明显的问题,死循环了!

 


那么如何返回一个正确的实例?

  在javascript中实例this只跟原型有关系

  那么能够把jQuery类看成一个工厂方法来建立实例,把这个方法放到jQuery.prototye原型中

复制代码
复制代码
var aQuery = function(selector, context) {
       return  aQuery.prototype.init();
}
aQuery.prototype = {
    init:function(){
        return this;
    }
    name:function(){},
    age:function(){}
}
复制代码
复制代码

  当执行aQuery() 返回的实例:

image

  很明显aQuery()返回的是aQuery类的实例,那么在init中的this其实也是指向的aQuery类的实例

  问题来了init的this指向的是aQuery类,若是把init函数也看成一个构造器,那么内部的this要如何处理?

复制代码
复制代码
var aQuery = function(selector, context) {
       return  aQuery.prototype.init();
}
aQuery.prototype = {
    init: function() {
        this.age = 18
        return this;
    },
    name: function() {},
    age: 20
}

aQuery().age  //18
复制代码
复制代码

  这样的状况下就出错了,由于this只是指向aQuery类的,因此须要设计出独立的做用域才行

 


jQuery框架分隔做用域的处理

jQuery = function( selector, context ) {
        // The jQuery object is actually just the init constructor 'enhanced'
        return new jQuery.fn.init( selector, context, rootjQuery );
    },

  很明显经过实例init函数,每次都构建新的init实例对象,来分隔this,避免交互混淆

  那么既然都不是同一个对象那么确定又出现一个新的问题

例如:

复制代码
复制代码
var aQuery = function(selector, context) {
       return  new aQuery.prototype.init();
}
aQuery.prototype = {
    init: function() {
        this.age = 18
        return this;
    },
    name: function() {},
    age: 20
}

//Uncaught TypeError: Object [object Object] has no method 'name' 
console.log(aQuery().name())
复制代码
复制代码

  抛出错误,没法找到这个方法,因此很明显new的init跟jquery类的this分离了

 


怎么访问jQuery类原型上的属性与方法?

     作到既能隔离做用域还能使用jQuery原型对象的做用域呢,还能在返回实例中访问jQuery的原型对象?

实现的关键点

// Give the init function the jQuery prototype for later instantiation
jQuery.fn.init.prototype = jQuery.fn;

  经过原型传递解决问题,把jQuery的原型传递给jQuery.prototype.init.prototype

  换句话说jQuery的原型对象覆盖了init构造器的原型对象

  由于是引用传递因此不须要担忧这个循环引用的性能问题

复制代码
复制代码
var aQuery = function(selector, context) {
       return  new aQuery.prototype.init();
}
aQuery.prototype = {
    init: function() {
        return this;
    },
    name: function() {
        return this.age
    },
    age: 20
}

aQuery.prototype.init.prototype = aQuery.prototype;

console.log(aQuery().name()) //20
复制代码
复制代码

 

3、链式调用

  DOM链式调用的处理:

  1.节约JS代码.

  2.所返回的都是同一个对象,能够提升代码的效率

 

  经过简单扩展原型方法并经过return this的形式来实现跨浏览器的链式调用。

  利用JS下的简单工厂模式,来将全部对于同一个DOM对象的操做指定同一个实例。

  这个原理就超简单了

复制代码
aQuery().init().name()

分解
a = aQuery();
a.init()
a.name()
复制代码

  把代码分解一下,很明显实现链式的基本条件就是实例this的存在,而且是同一个

复制代码
复制代码
aQuery.prototype = {
    init: function() {
        return this;
    },
    name: function() {
        return this
    }
}
复制代码
复制代码

  因此咱们在须要链式的方法访问this就能够了,由于返回当前实例的this,从而又能够访问本身的原型了

aQuery.init().name()

  优势:节省代码量,提升代码的效率,代码看起来更优雅

 

4、插件接口

  jQuery支持本身扩展属性,这个对外提供了一个接口,jQuery.fn.extend()来对对象增长方法

从jQuery的源码中能够看到,jQuery.extend和jQuery.fn.extend实际上是同指向同一方法的不一样引用

jQuery.extend = jQuery.fn.extend = function() {
jQuery.extend 对jQuery自己的属性和方法进行了扩展,扩展工具方法下的插件

jQuery.fn.extend 对jQuery.fn的属性和方法进行了扩展,扩展jquery对象下的插件

  经过extend()函数能够方便快速的扩展功能,不会破坏jQuery的原型结构

  jQuery.extend = jQuery.fn.extend = function(){...}; 这个是连等,也就是2个指向同一个函数,怎么会实现不一样的功能呢?这就是this 力量了!

  针对fn与jQuery实际上是2个不一样的对象,在以前有讲述:

  • jQuery.extend 调用的时候,this是指向jQuery对象的(jQuery是函数,也是对象!),因此这里扩展在jQuery上。
  • 而jQuery.fn.extend 调用的时候,this指向fn对象,jQuery.fn 和jQuery.prototype指向同一对象,扩展fn就是扩展jQuery.prototype原型对象。
  • 这里增长的是原型方法,也就是对象方法了。因此jQuery的api中提供了以上2中扩展函数。
复制代码
1 jQuery.extend = jQuery.fn.extend = function() {
 2     var src, copyIsArray, copy, name, options, clone,
 3         target = arguments[0] || {},
 4         i = 1,
 5         length = arguments.length,
 6         deep = false;
 7 
 8     // Handle a deep copy situation
 9     //判断第一个参数是否为boolean类型,也就是是否深度嵌套对象
10     if ( typeof target === "boolean" ) {
11         deep = target;
12 
13         // skip the boolean and the target
14         //跳过第一个boolean参数
15         target = arguments[ i ] || {};
16         i++;
17     }
18 
19     // Handle case when target is a string or something (possible in deep copy)
20     // 处理奇怪的状况,好比 jQuery.extend( 'hello' , {nick: 'casper})
21     if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
22         target = {};
23     }
24 
25     // extend jQuery itself if only one argument is passed
26     if ( i === length ) {
27         target = this;
28         i--;
29     }
30 
31     for ( ; i < length; i++ ) {
32         // Only deal with non-null/undefined values
33         if ( (options = arguments[ i ]) != null ) {//opitions为一个个参数对象
34             // Extend the base object
35             for ( name in options ) {
36                 src = target[ name ];
37                 copy = options[ name ];
38 
39                 // Prevent never-ending loop
40                 //防止自引用
41                 if ( target === copy ) {
42                     continue;
43                 }
44 
45                 // Recurse if we're merging plain objects or arrays
46                 //深拷贝
47                 if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
48                     if ( copyIsArray ) {//被拷贝属性值是个数组
49                         copyIsArray = false;
50                         clone = src && jQuery.isArray(src) ? src : [];
51 
52                     } else {
53                         clone = src && jQuery.isPlainObject(src) ? src : {};
54                     }
55 
56                     // Never move original objects, clone them
57                     //递归拷贝
58                     target[ name ] = jQuery.extend( deep, clone, copy );
59 
60                 // Don't bring in undefined values
61                 } else if ( copy !== undefined ) {//浅拷贝,属性值不是自定义
62                     target[ name ] = copy;
63                 }
64             }
65         }
66     }
67 
68     // Return the modified object
69     return target;
70 };
复制代码
相关文章
相关标签/搜索