jquery源码学习

来源:互联网 发布:宏观经济指标数据 编辑:程序博客网 时间:2024/06/15 23:00

源码架构

  • 变量正则初始化
  • $.fn
  • $.ready()
  • 复杂选择器sizzle
  • $.callback()
  • $.defferred()
  • $.data
  • 队列方法:queue()|dequeue()
  • 对元素属性的操作:attr() prop() val()
  • 事件操作方法:on trigger
  • window,jQuery = window.$ = jQuery

无new构造原理

正常使用:

$('test').text('Test')

相当于:

var test = new $('#test');test.text('Test');

内部实现源码:

(function(window, undefined) {    var    // ...    jQuery = function(selector, context) {        // The jQuery object is actually just the init constructor 'enhanced'        // 看这里,实例化方法 jQuery() 实际上是调用了其拓展的原型方法 jQuery.fn.init        return new jQuery.fn.init(selector, context, rootjQuery);    },    // jQuery.prototype 即是 jQuery 的原型,挂载在上面的方法,即可让所有生成的 jQuery 对象使用    jQuery.fn = jQuery.prototype = {        // 实例化化方法,这个方法可以称作 jQuery 对象构造器        init: function(selector, context, rootjQuery) {            // ...        }    }    // 这一句很关键,也很绕    // jQuery 没有使用 new 运算符将 jQuery 实例化,而是直接调用其函数    // 要实现这样,那么 jQuery 就要看成一个类,且返回一个正确的实例    // 且实例还要能正确访问 jQuery 类原型上的属性与方法    // jQuery 的方式是通过原型传递解决问题,把 jQuery 的原型传递给jQuery.prototype.init.prototype    // 所以通过这个方法生成的实例 this 所指向的仍然是 jQuery.fn,所以能正确访问 jQuery 类原型上的属性与方法    jQuery.fn.init.prototype = jQuery.fn;})(window);

解析:

jQuery.fn.init.prototype = jQuery.fn = jQuery.prototype ;new jQuery.fn.init() 相当于 new jQuery() ;

jQuery.fn.extend 与 jQuery.extend

  • jQuery.extend() :把两个或者更多的对象合并到第一个当中,$.xxx()
var settings = { validate: false, limit: 5, name: "foo" }; var options = { validate: true, name: "bar" }; jQuery.extend(settings, options); 结果:settings == { validate: true, limit: 5, name: "bar" }

第一个参数为true为深拷贝,false为浅拷贝

  • jQuery.fn.extend():把对象挂载到 jQuery 的 prototype 属性,来扩展一个新的 jQuery 实例方法。.().xxx,.fn.extend({ hello:function(){alert(‘hello’);} });
$.fn.extend({              alertWhileClick:function() {                      $(this).click(function(){                                  alert($(this).val());                      });                }       });       $("#input1").alertWhileClick(); 

源码:

// 扩展合并函数// 合并两个或更多对象的属性到第一个对象中,jQuery 后续的大部分功能都通过该函数扩展// 虽然实现方式一样,但是要注意区分用法的不一样,那么为什么两个方法指向同一个函数实现,但是却实现不同的功能呢,// 阅读源码就能发现这归功于 this 的强大力量// 如果传入两个或多个对象,所有对象的属性会被添加到第一个对象 target// 如果只传入一个对象,则将对象的属性添加到 jQuery 对象中,也就是添加静态方法// 用这种方式,我们可以为 jQuery 命名空间增加新的方法,可以用于编写 jQuery 插件// 如果不想改变传入的对象,可以传入一个空对象:$.extend({}, object1, object2);// 默认合并操作是不迭代的,即便 target 的某个属性是对象或属性,也会被完全覆盖而不是合并// 如果第一个参数是 true,则是深拷贝// 从 object 原型继承的属性会被拷贝,值为 undefined 的属性不会被拷贝// 因为性能原因,JavaScript 自带类型的属性不会合并jQuery.extend = jQuery.fn.extend = function() {    var src, copyIsArray, copy, name, options, clone,        target = arguments[0] || {},        i = 1,        length = arguments.length,        deep = false;    // Handle a deep copy situation    // target 是传入的第一个参数    // 如果第一个参数是布尔类型,则表示是否要深递归,    if (typeof target === "boolean") {        deep = target;        target = arguments[1] || {};        // skip the boolean and the target        // 如果传了类型为 boolean 的第一个参数,i 则从 2 开始        i = 2;    }    // Handle case when target is a string or something (possible in deep copy)    // 如果传入的第一个参数是 字符串或者其他    if (typeof target !== "object" && !jQuery.isFunction(target)) {        target = {};    }    // extend jQuery itself if only one argument is passed    // 如果参数的长度为 1 ,表示是 jQuery 静态方法    if (length === i) {        target = this;        --i;    }    // 可以传入多个复制源    // i 是从 1或2 开始的    for (; i < length; i++) {        // Only deal with non-null/undefined values        // 将每个源的属性全部复制到 target 上        if ((options = arguments[i]) != null) {            // Extend the base object            for (name in options) {                // src 是源(即本身)的值                // copy 是即将要复制过去的值                src = target[name];                copy = options[name];                // Prevent never-ending loop                // 防止有环,例如 extend(true, target, {'target':target});                if (target === copy) {                    continue;                }                // Recurse if we're merging plain objects or arrays                // 这里是递归调用,最终都会到下面的 else if 分支                // jQuery.isPlainObject 用于测试是否为纯粹的对象                // 纯粹的对象指的是 通过 "{}" 或者 "new Object" 创建的                // 如果是深复制                if (deep && copy && (jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)))) {                    // 数组                    if (copyIsArray) {                        copyIsArray = false;                        clone = src && jQuery.isArray(src) ? src : [];                        // 对象                    } else {                        clone = src && jQuery.isPlainObject(src) ? src : {};                    }                    // Never move original objects, clone them                    // 递归                    target[name] = jQuery.extend(deep, clone, copy);                    // Don't bring in undefined values                    // 最终都会到这条分支                    // 简单的值覆盖                } else if (copy !== undefined) {                    target[name] = copy;                }            }        }    }    // Return the modified object    // 返回新的 target    // 如果 i < length ,是直接返回没经过处理的 target,也就是 arguments[0]    // 也就是如果不传需要覆盖的源,调用 $.extend 其实是增加 jQuery 的静态方法    return target;};

链式调用及回溯

源码:

jQuery.fn = jQuery.prototype = {    // 将一个 DOM 元素集合加入到 jQuery 栈    // 此方法在 jQuery 的 DOM 操作中被频繁的使用, 如在 parent(), find(), filter() 中    // pushStack() 方法通过改变一个 jQuery 对象的 prevObject 属性来跟踪链式调用中前一个方法返回的 DOM 结果集合    // 当我们在链式调用 end() 方法后, 内部就返回当前 jQuery 对象的 prevObject 属性    pushStack: function(elems) {        // 构建一个新的jQuery对象,无参的 this.constructor(),只是返回引用this        // jQuery.merge 把 elems 节点合并到新的 jQuery 对象        // this.constructor 就是 jQuery 的构造函数 jQuery.fn.init,所以 this.constructor() 返回一个 jQuery 对象        // 由于 jQuery.merge 函数返回的对象是第二个函数附加到第一个上面,所以 ret 也是一个 jQuery 对象,这里可以解释为什么 pushStack 出入的 DOM 对象也可以用 CSS 方法进行操作        var ret = jQuery.merge(this.constructor(), elems);        // 给返回的新 jQuery 对象添加属性 prevObject        // 所以也就是为什么通过 prevObject 能取到上一个合集的引用了        ret.prevObject = this;        ret.context = this.context;        // Return the newly-formed element set        return ret;    },    // 回溯链式调用的上一个对象    end: function() {        // 回溯的关键是返回 prevObject 属性        // 而 prevObject 属性保存了上一步操作的 jQuery 对象集合        return this.prevObject || this.constructor(null);    },    // 取当前 jQuery 对象的第 i 个    eq: function(i) {        // jQuery 对象集合的长度        var len = this.length,            j = +i + (i < 0 ? len : 0);        // 利用 pushStack 返回        return this.pushStack(j >= 0 && j < len ? [this[j]] : []);    }, }

end()

end() 方法结束当前链条中的最近的筛选操作,并将匹配元素集还原为之前的状态。
大多数 jQuery 的遍历方法会操作一个 jQuery 对象实例,并生成一个匹配不同 DOM 元素集的新对象。当发生这种情况时,应该会把新的元素集推入维持在对象中的堆栈内。每次成功的筛选方法调用都会把新元素推入堆栈中。如果我们需要老的元素集,可以使用 end() 从堆栈中弹出新集合。
列子:

<div></div><div></div><script src="jquery-3.1.1.js"></script><script>    console.log($('div').eq(0));   // 第二个div元素    console.log($('div').eq(0).end());  // $('div)</script>

而end()的作用就是返回当前jQuery对象(在本例中就是$(‘div’).eq(0))的prevObject对象。

正则

用法 说明 返回值 pattern.test(str) 判断str是否包含匹配结果 包含返回true,不包含返回false。 pattern.exec(str) 根据pattern对str进行正则匹配 返回匹配结果数组,如匹配不到返回null str.match(pattern) 根据pattern对str进行正则匹配 返回匹配结果数组,如匹配不到返回null str.replace(pattern, replacement) 根据pattern进行正则匹配,把匹配结果替换为replacement 返回一个新的字符串

exec和match

这两个返回值虽然都是数组,但是区别很大。完整地址:https://segmentfault.com/a/1190000003497780

零宽断言 。。。

原创粉丝点击