jquery源码分析

jquery 简化版代码

;(function (ROOT) {

  // 构造函数
  var jQuery = function (selector) {

    // 在jQuery中直接返回new过的实例,这里的init是jQuery的真正构造函数
    return new jQuery.fn.init(selector)
  }

  jQuery.fn = jQuery.prototype = {
    constructor: jQuery,

    version: '1.0.0',

    init: function (selector) {
      // 在jquery中这里有一个复杂的判断,但是这里我做了简化
      var elem, selector;
      elem = document.querySelector(selector);
      this[0] = elem;

      // 在jquery中返回一个由所有原型属性方法组成的数组,我们这里简化,直接返回this即可
      // return jQuery.makeArray(selector, this);
      return this;
    },

    // 在原型上添加一堆方法
    toArray: function () { },
    get: function () { },
    each: function () { },
    ready: function () { },
    first: function () { },
    slice: function () { }
    // ... ...
  }

  jQuery.fn.init.prototype = jQuery.fn;

  // 实现jQuery的两种扩展方式
  jQuery.extend = jQuery.fn.extend = function (options) {

    // 在jquery源码中会根据参数不同进行很多判断,我们这里就直接走一种方式,所以就不用判断了
    var target = this;
    var copy;

    for (name in options) {
      copy = options[name];
      target[name] = copy;
    }
    return target;
  }

  // jQuery中利用上面实现的扩展机制,添加了许多方法,其中

  // 直接添加在构造函数上,被称为工具方法
  jQuery.extend({
    isFunction: function () { },
    type: function () { },
    parseHTML: function () { },
    parseJSON: function () { },
    ajax: function () { }
    // ...
  })

  // 添加到原型上
  jQuery.fn.extend({
    queue: function () { },
    promise: function () { },
    attr: function () { },
    prop: function () { },
    addClass: function () { },
    removeClass: function () { },
    val: function () { },
    css: function () { }
    // ...
  })

  // $符号的由来,实际上它就是jQuery,一个简化的写法,在这里我们还可以替换成其他可用字符
  ROOT.jQuery = ROOT.$ = jQuery;
})(window);

关键代码

jQuery.fn = jQuery.prototype
jQuery.fn.init.prototype = jQuery.fn

jQuery构造器

jQuery使用非常方便,其中一个原因就是我们在调用的时候并不需要使用关键字new来创造一个jQeury对象,直接使用jQuery(“#id”)或$(“.class”)就可轻松得到一个新的jQuery对象。原因就是jQuery使用工厂方法,利用构造器创造一个新的jQuery对象并返回,省去了用户的new操作。

一般构造器

var $ = jQuery = function() {
    return new jQuery();
}
jQuery.prototype = {
    jquery: "1.11"
};
console($().jquery);

一个构造器返回利用它自身创建了一个新的对象并返回,这样形成了一个死循环,产生栈溢出的异常。

为了解决循环,就必须在jQuery.prototype中定义的一个构造器,这就是jQuery.fn.init了。

var $ = jQuery = function() {
    return jQuery.fn.init();
}
jQuery.fn = jQuery.prototype = {
    init: function(){
        return this;
    },
    jquery: "1.11"
}

虽然这个方法解决了嵌套为问题,也将jQuery的原型方法传递给了jQuery对象,但它是将一个完整的jQuery.prototype暴露处理,jQuery的任何对this关键字的操作,实际上就是直接在jQuery.prototype上进行操作,那么jQuery.fn的完整性很容易就被破坏了,jQuery对象之间也可能产生不可预估的影响。
为了避免直接返回jQuery.fn,我们需要借鉴一开始使用关键字new的方法,通过new创建一个新Oject,改变了this所指向的对象,从而避开对jQuery.fn的直接暴露。

var $ = jQuery = function() {
    return new jQuery.fn.init();
}
jQuery.fn = jQuery.prototype = {
    init: function(){
        this.num = 2015;
        return this;
    },
    jquery: "1.11"
}

但此时问题来了,new jQuery.fn.init()所返回的新对象并没有继承jQuery.fn,因为jQuery.fn.init.prototype仅仅是指向了一个function对象的原型,并不包含jQuery.fn。

既然jQuery.fn.init.prototype只是指向了Object.prototype,那么我们只需要改变它的指向,让它指向jQuery.fn不就好了吗?

jQuery.fn.init.protoytpe = jQuery.fn;

总结

  • jQuery.prototype,挂载jQuery对象的原型方法
  • jQuery.fn是jQuery.prototype的别名,方便使用
  • jQuery.fn.init.prototype,则是为了让jQuery.fn.init这个工厂方法的实例均能够继承jQuery.fn上的原型方法。