jQuery 源码分析

jQuery源码分析系列(一)初识jQuery 一个工厂 (function(global, factory){ "use strict" // operation_1 })(typedef window !== "undefined" ? window : this, function (window, noGlobal) { // operation_2 }) =====> (function(args,...){...})(params,...) // 一种自执行方法,形式为(. 阅读详情
jQuery 是一款开源的,最流行的,面向对象的Javascript框架,因为平时经常使用,也就萌生了阅读源码的想法,尝试着去分析一下,如果有问题,还请大家指点。

阅读 jQuery 源码之前,必须要了解 jQuery 的工作机制,在我看来,实际上 jQuery 是一个标准的以面向对象的程序结构,jQuery 本身是一个类,而每一个我们构建的 jQuery 对象则是这个类实例。

因此我从如下两个方法来入手
1、如何构造 jQuery 对象
2、jQuery对象的继承

1、如何构造 jQuery 对象
jQuery 提供了四种封装jQuery对象的方法,分别是

DOMElement
HTML strings
TAG
expr, $(…)
这四种方法的调用如下。

elem = document.getElementsByTagName("div");
elem = $(elem);
var elem = $("<p>hello</p>");
var elem = $("div");
var elem = $("body > div");
通过这些方法,就生成一个jQuery对象。

那么,在jQuery内部,是如何去封装这个对象的呢?
看代码

var jQuery = function( selector, context ) {
                // 创建一个自定义对象,并返回
                return new jQuery.fn.init( selector, context );
        }
不难理解,jQuery 对象实际上通过new运算符创建的一个继承jQuery.prototype.init()返回的对象的原型的对象。

让我们简化jQuery.fn.init方法,并看看他是如何去运作的

jQuery.fn = jQuery.prototype = {
        init: function( selector, context ) {
                var match, elem, ret, doc;
 
                // 如果选择器为空、null、undefined的时候,返回 jQuery对象
                if ( !selector ) {
                        return this;
                }
 
                // Handle $(DOMElement)
                if ( selector.nodeType ) {
                        // 如果选择器为DOM对象,写入jQuery属性并返回jQuery对象
                        this.context = this[0] = selector;
                        this.length = 1;
                        return this;
                }
 
                // The body element only exists once, optimize finding it
                if ( selector === "body" && !context ) {
                        //code here...
                        return this;
                }
 
                // Handle HTML strings
                if ( typeof selector === "string" ) {
                // 通过正则判断传入的选择器是HTML字符串还是ID
                        // Are we dealing with HTML string or an ID?
                        // quickExpr = /^[^< ]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,
 
                        match = quickExpr.exec( selector );
 
                        // 如果match[1]不为空或者context为空的时候
                        if ( match && (match[1] || !context) ) {
 
                                // HANDLE: $(html) -> $(array)
                                // 如果match[1] 不为空(html字符串)
                                if ( match[1] ) {
                                        // 返回jQuery、选择器两数组合并后的对象
                                        return jQuery.merge( this, selector );
 
                                // HANDLE: $("#id")
                                // 如果context为空的时候 (id)
                                } else {
                                        // 尝试按照DOM节点的方式返回jQuery对象
                                        elem = document.getElementById( match[2] );
 
                                        if ( elem ) {
                                                // 如果得到elem对象,但id非match[2]
                                                if ( elem.id !== match[2] ) {
                                                        return rootjQuery.find( selector );
                                                }
 
                                                // Otherwise, we inject the element directly into the jQuery object
                                                this.length = 1;
                                                this[0] = elem;
                                        }
 
                                        this.context = document;
                                        this.selector = selector;
                                        return this;
                                }
 
 
                        // HANDLE: $("TAG")
                        // 如果context为空并且是一个纯字母的字符串
                        } else if ( !context && /^\w+$/.test( selector ) ) {
                                // 返回jQuery、选择器两数组合并后的对象
                                return jQuery.merge( this, selector );
 
                        // HANDLE: $(expr, $(...))
                        // 如果context为空 或者 context是一个jQuery对象
                        } else if ( !context || context.jquery ) {
                                // 在上下文或者document对象中找到节点并返回jQuery对象
                                return (context || rootjQuery).find( selector );
 
                        // HANDLE: $(expr, context)
                        // (which is just equivalent to: $(context).find(expr)
                        } else {
                                return jQuery( context ).find( selector );
                        }
 
                // HANDLE: $(function)
                // Shortcut for document ready
                // 对$(document).ready(functon(){});的一个缩写$(function(){});
                } else if ( jQuery.isFunction( selector ) ) {
                        return rootjQuery.ready( selector );
                }
 
                // 如果选择器是一个jQuery对象,引用下
                if (selector.selector !== undefined) {
                        this.selector = selector.selector;
                        this.context = selector.context;
                }
 
                return jQuery.makeArray( selector, this );
 
        }
}
至此,我们就完成了对jQuery对象的构造。

2、jQuery对象的继承
当我们构造完成jQuery对象后,需要jQuery对象继承jQuery中的方法与属性。
看代码

jQuery.fn.init.prototype = jQuery.fn;
如此,就将jQuery的原型赋到jQuery对象的原型上去了,jQuery对象就继承了jQuery初始的方法和属性。

那么,当我需要向已实例化的对象原型中增加方法或者属性怎么办?

这就需要用到扩展方法 –> extend
上文我们提到了jQuery框架中的一个重要功能,扩展方法。

当我们使用new运算法实例化一个对象后,是如何对这个实例的原型再扩展方法的,因为这个实例的原型实际上是他类的原型的引用。(不知道这儿理解的是否正确)

因此当我们试图向实例化对象的原型中增加方法或者属性都是不行的。

function car(){}
 
car.prototype = {
    name: function(name){
        return name;
    }
}
 
var jeep = new car();
jeep.prototype = {
    type: "jeep"
}
 
alert(jeep.name("aodi")); //aodi
alert(jeep.type); //undefined
那么当我们需要扩展jQuery对象或者jQuery中的时候怎么办?

使用 jQuery.fn.extedn()、jQuery.extend()方法。

1、扩展jQuery静态方法

jQuery.extend({
  max: function(a, b) { return a > b ? a : b; }
});
 
jQuery.max(5,4) //5
2、给jQuery对象扩展方法

jQuery.fn.extend({
  max: function(a, b) { return a > b ? a : b; }
});
 
$(elem).max(5,4) //5
3、合并多个对象

var object1 = {
  apple: { value1:1,value2:2}
};
var object2 = {
  apple: { value1:3}
};
$.extend(object1, object2);
 
// object1 === {apple: { value1:3}}
4、深度合并多个对象

var object1 = {
  apple: { value1:1,value2:2}
};
var object2 = {
  apple: { value1:3}
};
$.extend(true, object1, object2);
 
// object1 === {apple: { value1:3,value2:2}}
那么extend方法是如何实现这种扩展的呢?看代码。

jQuery.extend = jQuery.fn.extend = function() {
        // copy reference to target object
        // 在这里我们约定被扩展的对象(object1)叫目标对象,其他被合并的对象(objectN)叫合并对象
        var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options, name, src, copy;
 
        // 当使用深度扩展的时候
        if ( typeof target === "boolean" ) {
                deep = target;
                target = arguments[1] || {};
                i = 2;
        }
 
        // 当目标对象不是一个对象的时候,建立一个空的对象
        if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
                target = {};
        }
 
        // 当没有合并对象的时候,则扩展this本身(jQuery,或者jQuery对象)
        if ( length === i ) {
                target = this;
                // 为后面的扩展做处理
                --i;
        }
 
        for ( ; i < length; i++ ) {
                // 当合并对象不为null的情况下,将合并对象赋值给options变量
                if ( (options = arguments[ i ]) != null ) {
                        // 遍历合并对象
                        for ( name in options ) {
                                src = target[ name ];
                                copy = options[ name ];
 
                                // 如果两个方法或属性相同,跳出本次循环
                                if ( target === copy ) {
                                        continue;
                                }
 
                                // 当使用深度扩展并且合并对象中的属性或者方法是一个字面量或数组(真复杂....)
                                if ( deep && copy && ( jQuery.isPlainObject(copy) || jQuery.isArray(copy) ) ) {
                                        // 如果目标对象中存在对应的属性或方法,并且是一个对象字面量或数组,则克隆它
                                        // 反之(不存在),则判断合并对象的属性或方法的类型,并依次类型建立对应的对象/数组字面量
                                        var clone = src && ( jQuery.isPlainObject(src) || jQuery.isArray(src) ) ? src
                                                : jQuery.isArray(copy) ? [] : {};
 
                                        // 再扩展这个克隆,并将这个克隆赋给目标对象的属性或者方法中
                                        target[ name ] = jQuery.extend( deep, clone, copy );
 
                                // 当没有使用深度扩展并且合并对象中仍然存在没有合并的方法或属性时
                                } else if ( copy !== undefined ) {
                                        // 扩展目标对象
                                        target[ name ] = copy;
                                }
                        }
                }
        }
 
        // 返回目标对象
        return target;
};
需要注意的是,这是jQuery中的一个静态方法,也是一个实例方法。

jQuery.extend = jQuery.prototype.extend = function() {}
至此,一个面向对象的js框架基础就搭建好了,下一步,我们将向这个对象中扩展各种不同的方法~

上次我们分析了jQuery对象的构建方法以及如何去扩展jQuery对象的方法extend()。

这次让我们先看看jQuery本身还有那些静态属性和方法实例方法。

需要注意的是,这里定义的所有jQuery的静态属性/方法实例方法也是是jQuery对象的静态属性/方法实例方法。

jQuery.fn = jQuery.prototype = {
        init: function( selector, context ) {
                // code here ...
        },
 
        // 为所有jQuery对象初始化一个空的选择器
        selector: "",
 
        // The current version of jQuery being used
        jquery: "1.4.2",
 
        // 初始化 jQuery 对象长度(jQuery含有多少个DOM对象,长度就是几)
        length: 0,
 
        // 获取jQuery.length属性的方法
        size: function() {
                return this.length;
        },
 
        // 将this对象转为数组
        //注意,这里的slice方法并不是Array.slice(),而是jQuery.slice()
        toArray: function() {
                return slice.call( this, 0 );
        },
 
        // Get the Nth element in the matched element set OR
        // Get the whole matched element set as a clean array
        // 取得匹配DOM对象的集合,或者根据传入的参获取对应的DOM对象
        get: function( num ) {
                return num == null ?
 
                        // Return a 'clean' array
                        this.toArray() :
 
                        // Return just the object
                        ( num < 0 ? this.slice(num)[ 0 ] : this[ num ] );
        },
 
        // Take an array of elements and push it onto the stack
        // (returning the new matched element set)
        // 将一个数组或者对象压入栈中,同时返回一个新的对象集合
        // 这是一个非常巧妙的方法,在后面,我们会看到
        // jQuery利用这个方法来筛选、查找上一个,下一个对象
        pushStack: function( elems, name, selector ) {
                // Build a new jQuery matched element set
                // 新建一个jQuery对象
                var ret = jQuery();
 
                // 如果传入的对象是数组
                // 则调用Array.push方法 (jQuery.push只是Array.push的一个引用)
                // 如果是对象,则合并且返回一个新的jQuery对象
                if ( jQuery.isArray( elems ) ) {
                        push.apply( ret, elems );
                } else {
                        jQuery.merge( ret, elems );
                }
 
                // Add the old object onto the stack (as a reference)
                // 将当前调用的对象(this),作为新jQuery对象的前一个对象
                ret.prevObject = this;
 
                // 将当前调用的对象(this)的文档集、DOM对象集
                // 或者jQuery对象赋给新的jQuery对象
                ret.context = this.context;
 
                if ( name === "find" ) {
                        // 如果name是find方法,构建一个子查询器(Sizzle)
                        ret.selector = this.selector + (this.selector ? " " : "") + selector;
                } else if ( name ) {
                        // 如果name是其他方法,则调用其他方法
                        ret.selector = this.selector + "." + name + "(" + selector + ")";
                }
 
                // 返回这个新的jQuery对象
                return ret;
        },
 
        // Execute a callback for every element in the matched set.
        // (You can seed the arguments with an array of args, but this is
        // only used internally.)
        // 后面再说each方法
        each: function( callback, args ) {
                return jQuery.each( this, callback, args );
        },
 
        // 判断jQuery是否载入完毕
        ready: function( fn ) {
                // Attach the listeners
                // 调用bindReady方法,建立一个侦听
                jQuery.bindReady();
 
                // If the DOM is already ready
                // 如果DOM已经渲染完成
                if ( jQuery.isReady ) {
                        // Execute the function immediately
                        // 执行fn,并且将jQuery作为参数传入fn中
                        // 实际上,$(function(){...})内的fn是一个存在于window/document对象下匿名函数
                        // 传入jQuery后,我们可以将它看作
                        // (function (jQuery){ ... })(jQuery);
                        fn.call( document, jQuery );
 
                // Otherwise, remember the function for later
                // 如果没有准备好,则将要执行的函数放到readyList数组中暂存
                } else if ( readyList ) {
                        // Add the function to the wait list
                        readyList.push( fn );
                }
 
                return this;
        },
 
        // 调用jQuery.slice方法取对用dom元素
        eq: function( i ) {
                return i === -1 ?
                        this.slice( i ) :
                        this.slice( i, +i + 1 );
        },
 
        // 获取对象中的第一个dom元素
        first: function() {
                return this.eq( 0 );
        },
 
        // 获取对象中的最后一个dom元素
        last: function() {
                return this.eq( -1 );
        },
 
        // 将函数实参转为数组
        slice: function() {
                return this.pushStack( slice.apply( this, arguments ),
                        "slice", slice.call(arguments).join(",") );
        },
 
        // 将当前jQuery对象中的每一个元素作为参数放入callback中执行,然后返回执行后的新数组
        map: function( callback ) {
                return this.pushStack( jQuery.map(this, function( elem, i ) {
                        return callback.call( elem, i, elem );
                }));
        },
 
        // 返回当前调用对象之前的对象,注意pushStack()
        end: function() {
                return this.prevObject || jQuery(null);
        },
 
        // For internal use only.
        // Behaves like an Array's method, not like a jQuery method.
        push: push,
        sort: [].sort,
        splice: [].splice
};
现在jQuery中主要的实例方法已经分析完毕~

同时,最重要的静态方法extend也已经说明

下次将从api入手,依照Core Event Manipulation Ajax这四类来尝试分析
Claude Code 安装指南(Windows / macOS) 本文介绍了Claude Code的安装准备和基本流程。作为开发者工具,安装前需具备命令行操作能力、可用的Claude账号和稳定网络环境。Windows和macOS的安装都需通过终端完成,安装后以命令行形式运行。首次使用建议在熟悉项目中执行简单任务,逐步建立使用习惯。强调安装只是开始,后续需学习如何有效使用工具,包括任务提交流程和上下文提供方法。核心要点是:Claude Code是开发者工具,网络和终端环境是关键,建议先运行再深入理解。 阅读详情

相关推荐

电赛MSP430F5529之超声波测距+OLED 显示(含主要代码)

目录 前言 一、超声波介绍 二、部分代码 三、总结 题外话: 前言 哎,又双叒叕,电赛延期了,看起来像是遥遥无期,但是肯定要考虑大局,大家就继续抓紧时间学习知识,巩固自己的知识,更进一步。好了,今天主要是继承上一篇博文(OLED显示)写的,主要是写超声波测距的(US-015) 一、超声波介绍 利用TI公司的MSP430F5529单片机+超声波模块+OLED 实现 这里主要是利用单片机定时器的捕获功能来捕获超声波高电平持续时间,再计算得到距离,最后再在OLED 屏幕上显示出来,上图

不说二话的自家人的博客 1万+

JQuery 源码分析

jQuery 源码分析 类库 提供了一些常用的方法,我们在开发项目的时候,随时可以引入这些类库,实现我们的业务需求,常用的有 jQuery zepto; 插件 : 带有一定的业务逻辑 , 选项卡插件 轮播图插件 我们在项目开发过程的时候,重复的业务可以用插件来代替 常用的插件 : swiper ,iscroll , Echarts UI组件: 通过html css js 组成,使用的...

lingkeyu的博客 341

《AI大模型评测与智能测试》

第三篇“实践蓝图”(第6-7章)着眼于测试领域AI应用,重点介绍了RAG、AI Agent开发及构建端到端智能测试系统的方法。本书聚焦AI时代的“质量”核心,以“AI赋能测试”(AI For Testing)与“AI模型评测”(Testing For AI)为双轮驱动。内容不仅深入剖析如何科学度量AI大模型的内在质量,还系统讲解如何利用AI赋能传统软件测试,帮助读者贯通从模型原理、评测到应用开发的全链路知识。

酌沧 1142

jQuery源码分析

jQuery源码分析

Mortimer's Blogs 232

【IC】Redhawk-SC输入件配置

Redhawk sc

u011808788的博客 1778

【STM32】H743的25MHZ外部晶振下480MHz时钟的CubeMX配置图

备忘 有时候临时调一个外设做实验需要快速搭建工程的时候,老忘记配置的数据 再补一段代码格式的 void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0}; /** Supply configuration up

一只爱做笔记的码农 3395

jQuery源码分析6: jQuery.isEmptyObject与jQuery.isPlainObject

jQuery源码分析6: jQuery.isEmptyObject与jQuery.isPlainObject var hasOwn = Object.prototype.hasOwnProperty, isEmptyObject: function( obj ) {     for ( var name in obj ) {         return false;     }

香巴拉 1883

Open3D点云算法与点云深度学习案例汇总(长期更新)

Open3D-python算法专栏文章汇总,长期更新算法原理与实战案例。

纵马踏花向自由 7473

jquery源码分析

jquery源码分析

weixin_45101949的博客 163

lammps教程:官方自带eam_database程序拟合EAM势方法介绍

使用lammps模拟金属体系,最常用的势函数为eam、meam势。 对于大多数金属或者合金来说,可以从网上下载相对应的eam或meam势力场文件。 而对于一些新的合金材料,可能就找不到相对应的力场文件。 解决的方法有两种,一是采用混合势(pair_style hybrid),第二种方法是自己拟合势函数文件。 混合势的写法在前面文章中已经介绍过,本文介绍一种拟合EAM势的方法。 该方法是运行Xiaowang Zhou编写的一个拟合程序,该程序可自动生成所需要的合金势。 这个程序能够拟合的合金元素有: Cu,

lammps_jiayou的博客 4618

使用PostgreSQL构建高可用数据库集群

在现代企业级应用中,数据库的高可用性已成为业务连续性的核心要求。PostgreSQL作为一款功能强大的开源关系型数据库,其高可用架构的设计直接影响着系统的稳定性和可靠性。

九天的专栏 1260

[转] jQuery源码分析-如何做jQuery源码分析

jQuery源码分析系列(持续更新) jQuery的源码有些晦涩难懂,本文分享一些我看源码的方法,每一个模块我基本按照这样的顺序去学习。 当我读到难度的书或者源码时,会和《如何阅读一本书》结合起来进行学习。推荐读读这本书,你可以从这里和这里下载。 第一部分:检视阅读 1.收集参考资料:官方文档、书籍、百度/谷歌,专题/博客等,快速的浏览,对涉及的知识点、范围、深...

weixin_30536513的博客 151

交通仿真软件:SUMO_(13).Python脚本与TraCI接口

通过TraCI接口,用户可以实现与SUMO仿真器的实时交互,获取和控制交通数据。本文档详细介绍了如何使用Python脚本与TraCI接口进行交互,包括基本的设置、数据获取和控制方法。此外,还展示了几个高级应用示例,如交通流优化、多代理系统、车辆路径规划和车辆行为模拟。这些示例可以帮助用户更好地理解如何利用TraCI接口进行复杂的交通仿真和分析。希望这些内容对您有所帮助,如果您有任何问题或需要进一步的帮助,请参考SUMO的官方文档或社区支持。

kkchenjj的博客 1005

jquery源码分析学习地址

http://www.ccvita.com/121.htmljQuery工作原理解析以及源代码示例http://www.cnblogs.com/haogj/archive/2010/04/19/1715762.htmljquery原型关系图 http://www.ihiro.org/blog/wp-content/uploads/2010/08/sizzle_flow1.jpgjquery选择器h

萌萌的It人 www.itmmd.com 1543

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

jQuery1.6.1源码分析系列 链接

jQuery1.6.1源码分析系列(停止更新)

u010403387的专栏 532

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

jquery-jquery源码分析学习地址

原博文地址:http://www.cnblogs.com/geek-lee/archive/2013/06/26/3155888.html http://www.ccvita.com/121.html jQuery工作原理解析以及源代码示例 http://www.cnblogs.com/haogj/archive/2010/04/19/1715762.html jquery原型关系

Tankpt的专栏 1184

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

Jquery 源码分析

[原创] jQuery1.6.1源码分析系列(持续更新) 作者:nuysoft/高云 QQ:47214707 Email:nuysoft@gmail.com  声明:本文为原创文章,如需转载,请注明来源并保留原文链接。   jQuery源码分析(版本1.6.1) 00 前言开光 01 总体架构 02 正则表达式-RegExp-常用正则表达式 03 构造jQuery对象-源码

327

Jquery1.2.6源码分析

Jquery1.2.6源码分析

pwz1985的专栏 142

jQuery源码分析之jQuery.fn.each与jQuery.each用法

本文实例讲述了jQuery源码分析之jQuery.fn.each与jQuery.each用法。分享给大家供大家参考。具体分析如下: 先上例子,下面代码的作用是:对每个选中的div元素,都给它们添加一个red类 复制代码代码如下: $('div').each(function(index, elem){        $(this).addClass('red'); }

CathyLou的博客 608

jQuery源码分析系列 学习记录

最近在跟nuysoft 的jQuery源码分析系列,把不深入的知识点自己做些摘要取出来,记录下。 1.函数自调用括号有两种写法(仔细看括号的位置): (function() { console.info( this ); console.info( arguments ); }( window ) ); //(foo{}()); (fun...

weixin_30906425的博客 73

jquery源码分析-工具函数

jQuery的版本一路狂飙啊,现在都到了2.0.X版本了。有空的时候,看看jquery的源码,学习一下别人的编程思路还是不错的。 下面这里是一些jquery的工具函数代码,大家可以看看,实现思路还是很清晰的。 1 // 是否函数 2 isFunction: function( obj ) { 3 return jQuery.type(obj...

115

jquery 源码分析学习地址

http://www.ccvita.com/121.htmljQuery工作原理解析以及源代码示例http://www.cnblogs.com/haogj/archive/2010/04/19/1715762.htmljquery原型关系图 http://www.ihiro.org/blog/wp-content/uploads/2010/08/sizzle_flow1.jpgjquery选择器h...

weixin_30640291的博客 123

jQuery源码分析之closest,has,addBack,addSelf,add方法

closest源码分析:        //调用方式:jQueryObject.closest( expr [, context ] ) closest: function( selectors, context ) { var cur, i = 0, //选择器选择对象的个数 l = this.length, //保存结果集合 matched = [], //var rneed

高山上的鱼 1479
上一篇: JS实现进度条效果
下一篇: 光标自动定位到输入框
dw_java08
博客等级 码龄15年 122粉丝 66原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值