JQ对象到底是什么

jQuery对象是什么,举个例子,$('#id') 返回的就是jQuery对象,这个东西是整个jQuery的核心所在,所以我先来分析它。

var jQuery = function( selector, context ) {

    // The jQuery object is actually just the init constructor 'enhanced'

    return new jQuery.fn.init( selector, context, rootjQuery );

};

......

jQuery.fn = jQuery.prototype = {

    constructor: jQuery,

    init: function( selector, context, rootjQuery ),

    selector: "",

    jquery: "1.7.2",

    length: 0,

    size: function(),

    toArray: function(),

    get: function( num ),

    pushStack: function( elems, name, selector ),

    each: function( callback, args ),

    ready: function( fn ),

    eq: function( i ),

    first: function(),

    last: function(),

    slice: function(),

    map: function( callback ),

    end: function(),

    push: push,

    sort: [].sort,

    splice: [].splice

};

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

 

 

我敢说,第一次看到这段代码的人都会被它的结构搞晕,因为这种写法实在是太罕见了。关于john的想法,我斗胆一猜:

1. jQ对象的构造函数为啥是protptype.init

答:构造函数中的逻辑和prototype的其他 属性/方法 有联系,比如 selector 和 length,写在一起比较易读。

调用init 方法时,如果参数为空,jQ对象可以等同于 prototype,这样看起来结构很清晰;

如果参数不为空,会覆盖一些 prototype 的属性(如 selector 和 length,也就说prototype 上的属性只是为了给一个默认值而已),还会创建一些 prototype 上没有列出的属性(如context),这些属性本该写在构造函数中的。但同样的,为了让逻辑保持紧凑,就一并写在 init 方法中。

 


2. jQ到底想创建一个怎样的对象?
答:这个对象有点像数组,比如下面这段代码:

?

function FuckYou(who) {

    this[0] = who;

}

var fy = new FuckYou('john');

于是 fy[0] === 'john',是不是有点小晕了?千万搞清楚,这不是数组,别被你的眼睛蒙蔽了,这是一个对象,0是它的属性名而已。因为this.0 = who 会语法报错,所以就用了一个小技巧。


再看prototype 中的其他方法,如each, slice, map, push, sort, splice,无一不是在模拟数组。

 

3. 解释一下pushStack呗?

答:顾名思义,就是一个压栈操作,主要是用于undo

 

 

pushStack: function( elems, name, selector ) {

    // 创建一个全新的jQ对象,API和prototype一模一样

    var ret = this.constructor();

 

    // elems 是数组,直接push

    if ( jQuery.isArray( elems ) ) {

        push.apply( ret, elems );

 

    // elems 是类数组,调用merge(),这存在两种情况:

    // 1. elems是childNodes这样的类数组

    // 2. elems是this[0], this[1]这样的模拟数组,上面举过例子的

    } else {

        jQuery.merge( ret, elems );

    }

 

    // 如 $("p").find("span")

    // 第一次是匹配p,第二次是在上次的结果中匹配span

    // 类似这样破坏上一个链的行为,jQ都会把上一个链存起来,以便回退(调用end())

    ret.prevObject = this;

 

    ret.context = this.context;

 

    // 这小段很有意思,从这里你几乎可以看出整个jQ的设计思想

    // name 表示一个方法名

    // selector 表示 选择器

    // 你懂的,jQ里面有很多DOM相关的方法,比如after, find之类的,它们的参数就是一个选择器

    if ( name === "find" ) {

        // find 相当于后代选择器,如 "div span" 这样的

        ret.selector = this.selector + ( this.selector ? " " : "" ) + selector;

    } else if ( name ) {

        // 这个分支比较复杂,光看这里的代码是看不懂的

        // 我就一直为什么要加 . 因为除了匹配class,是用不到 . 的

        // 我觉得吧,这个应该涉及到 Sizzle 的匹配模式问题,我不关心这种细节问题,囧

        ret.selector = this.selector + "." + name + "(" + selector + ")";

    }

 

    return ret;

}

 

4. 再解释一下 init ?

: 必须解释啊,这个方法真是重中之重,jQ的构造函数不解释还能解释啥。

init: function( selector, context, rootjQuery ) {

    var match, elem, ret, doc;

 

    // 处理 $(""), $(null), or $(undefined)

    if ( !selector ) {

        return this;

    }

 

    // 处理 $(DOMElement)

    if ( selector.nodeType ) {

        this.context = this[0] = selector;

        this.length = 1;

        return this;

    }

 

    // 因为 body 元素只有一个,所以可以优化一下

    if ( selector === "body" && !context && document.body ) {

        this.context = document;

        this[0] = document.body;

        this.selector = selector;

        this.length = 1;

        return this;

    }

 

    // 处理 HTML 字符串

    if ( typeof selector === "string" ) {

        // 如果是标签,如

,可省略第二个分支的正则匹配

        if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {

            match = [ null, selector, null ];

 

        } else {

            // quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/

            // 再来看早期的一个版本

            // quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/

            //

            // 大同小异,但前者肯定是做了更多的考虑,

            // 比如防止通过location.hash 进行XSS攻击

            //

            // 这个正则有点复杂,它分为两个分支

            // 1. ^[^#<]*(<[\w\W]+>)[^>]*$

            // 2. ^#([\w\-]*)$

            // 第二个不用说,是匹配ID的

            // 第一个用来匹配HTML代码段:

            //   [^#<]* 表示开头不能包含#和<</span>

            //   (<[\w\W]+>) 表示匹配完整的标签,如

123

            //   [^>]*$ 表示结尾不能包含>

            match = quickExpr.exec( selector );

        }

 

        // 匹配成功,并且匹配上ID时没有指定context

        // 为什么 ID 和context 不能同时指定?拜托,ID全局唯一的好不

        if ( match && (match[1] || !context) ) {

 

            // 处理 $(html) -> $(array)

            if ( match[1] ) {

                // 确保 context 是一个 HTMLElement

                context = context instanceof jQuery ? context[0] : context;

                // 确保 doc 是 document

                doc = ( context ? context.ownerDocument || context : document );

 

                // rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/

                // 就是匹配单个标签,如 或

                // 如果匹配成功,不用往下走了,直接 createElement_x 就完事

                ret = rsingleTag.exec( selector );

 

                if ( ret ) {

                    // 我凌乱了,context 可能是纯对象么?

                    // 按第一个分支的逻辑,context应该是 {id: 'id', title: 'title'}之类的

                    if ( jQuery.isPlainObject( context ) ) {

                        selector = [ document.createElement_x( ret[1] ) ];

                        jQuery.fn.attr.call( selector, context, true );

 

                    } else {

                        selector = [ doc.createElement_x( ret[1] ) ];

                    }

 

                } else {

                    // 不是单个标签,就创建文档碎片吧

                    ret = jQuery.buildFragment( [ match[1] ], [ doc ] );

                    selector = ( ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment ).childNodes;

                }

 

                return jQuery.merge( this, selector );

 

            // 处理$("#id")

            } else {

                elem = document.getElementByIdx_x( match[2] );

 

                // Check parentNode to catch when Blackberry 4.6 returns

                // nodes that are no longer in the document #6963

                // 囧,黑莓还有这种问题,我觉得移动开发就该另写一个库,不能把所有bugfix都写在jQ里

                // 就像说,难道移动开发还要考虑IE678? 肯定不要啊,基本都支持HTML5了

                // 既然这样,那又何必在这fix手机浏览器呢?john 您蛋疼了么

                if ( elem && elem.parentNode ) {

                    // IE 和 Opera 调用 getElementById 会依据name返回,而不是ID,再囧

                    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;

            }

 

        // 处理 $(expr, $(...)),相当于 $(...).find(expr)

        // 这么传参数的人纯属蛋疼,jQ就是这样被搞大的

        // 反正我觉得接口就该定死,context只能传 HTMLElement 或 选择器

        // 搞得我现在觉得jQ的接口好像万能一样,啥都能传

        } else if ( !context || context.jquery ) {

            return ( context || rootjQuery ).find( selector );

 

        // 处理 $(expr, context),也相当于$(context).find(expr)

        // 同样蛋疼的写法

        } else {

            return this.constructor( context ).find( selector );

        }

 

    // 处理 $(function)

    // 就是文档加载完成时调用的函数

    } else if ( jQuery.isFunction( selector ) ) {

        return rootjQuery.ready( selector );

    }

 

    // selector 是一个jQ对象,我真心觉得除了蛋疼没有别的理由这么写了

    if ( selector.selector !== undefined ) {

        this.selector = selector.selector;

        this.context = selector.context;

    }

 

    // 把 selector 加到 this 的尾部

    // 因为能走到这,this已经是一个类数组了

    return jQuery.makeArray( selector, this );

}

 

5. 还有啥想说的不?

答:最后提一点吧:

?

jQuery.fn = jQuery.prototype = {...}

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

为啥要这么写?比如第一行,为什么不直接赋给一个变量,而是赋给jQuery.fn?

1. 赋给一个变量就只能在jQ这个文件内部用,而jQ是有强大的插件机制的,所以为了便于外部扩展,赋给jQuery.fn。当然不处理这部分也没事,您就多打几个字,但是jQ毕竟是一个超级流行的框架,能缩写的就自己缩了吧,省的别人去做这种无畏的劳动。

 

2. 第二行的写法更加让人蛋疼。首先 init 是构造函数,还要我继续说?好吧,虽然我觉得这都是基础知识了。

?

1

2

3

4

function jQuery() { }

jQuery.prototype = {

    constructor: jQuery

}

如果jQ不写这一行,之后 obj instanceof jQuery 就永远为 false。

 

技巧:

1. 对象上的 属性/方法 会覆盖原型上对应的 属性/方法(说 override 可能好些);

2. 巧用数组的方法,如下:

?

1

2

3

4

5

6

7

8

9

10

11

var push = Array.prototype.push;

 

function ArrayLike() {

    this[0] = 0;

    this[1] = 1;

    this.length = 2;

}

 

var al = new ArrayLike();

push.apply(al, [2, 3]);

console.log(al)

最后的结果是 al.length === 4,现在知道jQ对象为啥需要 length 属性了吧

转载于:https://www.cnblogs.com/galal/p/6027125.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/254046.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

15、iOS开发之duplicate symbols for architecture x86_64错误

1. 错误提示 2. 分析错误原因 3. 解决问题办法 一、错误提示 在我们写代码过程中可能会经常遇到这样一个错误&#xff1a; [objc] view plaincopy print?<span style"font-size:32px;color:#ff0000;">ld: 4 duplicate symbols for architecture x86_64 clang…

【死磕Java并发】----- 死磕 Java 并发精品合集

【死磕 Java 并发】系列是 LZ 在 2017 年写的第一个死磕系列&#xff0c;一直没有做一个合集&#xff0c;这篇博客则是将整个系列做一个概览。 先来一个总览图&#xff1a; 【高清图&#xff0c;请关注“Java技术驿站”公众号&#xff0c;回复&#xff1a;脑图JUC】 【死磕Java…

vs官方使用教程中文版与英文版

Visual Studio IDE 中文文档 https://docs.microsoft.com/zh-cn/visualstudio/ide/ Visual Studio IDE documentation https://docs.microsoft.com/en-us/visualstudio/ide/

linux subsys_initcall

宏定义__define_initcall(level,fn)对于内核的初始化很重要&#xff0c;他指示编译器在编译的时候&#xff0c;将一系列初始化函数的起始地址值按照一定的顺序放在一个section中。在内核初始化段&#xff0c;do_initcalls() 将按顺序从该section中以函数指针的形式取出这些函数…

vue transition

Vue.js 教程 (9) : 过渡动画 Vue.js 提供非常简单的过渡动画接口。这些过渡动画在 Vue.js 将目标元素插入或移除出 DOM 的时候会自动执行。能够触发动画的指令包括 v-if , v-show 和 v-repeat。同时&#xff0c;vm 实例的 $appendTo() , $before() , $after() 和 $remove() 方法…

VS扩展工具

原文发布时间为&#xff1a;2011-03-09 —— 来源于本人的百度文章 [由搬家工具导入]http://visualstudiogallery.msdn.microsoft.com/site/search?f%5B0%5D.TypeRootCategory&f%5B0%5D.Valuetools转载于:https://www.cnblogs.com/handboy/p/7163982.html

工业相机5A参数及其对图像采集的影响

有些相机会提到5A功能&#xff0c;指的是: Automatic Shutter(自动快门)、Automatic Gain&#xff08;自动增益&#xff09;、Automatic IRIS&#xff08;自动光圈&#xff09;、Automatic Gamma&#xff08;自动伽马&#xff09;、Automatic White Balance&#xff08;自动白平…

DM6446 OSD

DM6446 OSD TMS320DM6446支持背景窗颜色&#xff0c;两个视频窗口&#xff0c;两个OSD窗口&#xff0c;一个指针(cursor)窗口。它们以递增的顺序排列&#xff1a;一个特有的第二个OSD窗口&#xff08;OSDWIN1&#xff09;可以用来配置成属性窗口来控制视频窗口和第一个OSD窗口&…

DOS的一些常用命令

原文发布时间为&#xff1a;2011-02-12 —— 来源于本人的百度文章 [由搬家工具导入]DOS远程桌面连接命令 mstsc /v: 192.168.1.250 /consolecmd 运行 command删除文件 rd 文件名/S创建文件 MD 文件名 1. net user admin godmour /add 新建一个…

机器视觉工业镜头-Computar

日本Computar镜头&#xff0c;全球工业镜头、CCTV镜头市场占有率第一。CBC板式会社成立于1925年&#xff0c;总部在日本东京。1960年 CBC香港公司成立&#xff0c;是computar镜头走向国际市场的前奏。 1979年 研制出第一只手动变焦镜头。 1985年 研制出第一款非球面高速镜头。1…

C++编程经验总结1

面向对象的精髓&#xff1a; 主函数其实就是对于类的元素和动作的重新组合来进行一项活动。 一个思想概念&#xff1a;程设是清楚的&#xff0c;完美的。 数学是清楚的&#xff0c;是完美的。 物理是有趣的&#xff0c;尤其是量子物理 生物是清楚的&#xff0c;尤其是基因 外语…

DM365的BSP源码分析-基于2.6.18内核

DM365的BSP主要包含mach-davinci和plat-davinci两个目录&#xff08;及相关头文件&#xff09;&#xff0c;BSP复杂庞大又极其重要&#xff0c;它主要完成了板级的初始化&#xff0c;比如内存映射&#xff0c;时钟和电源初始化&#xff0c;中断和IO初始化&#xff0c;CPU及各模…

第四章:Django 模型 —— 设计系统表

1. Django框架提供了完善的模型&#xff08;Model &#xff09;层来创建和存储数据&#xff0c;每一个模型对应数据库中的唯一的一张表。 2. Django 模型基础知识&#xff1a; 。每一本模型是一个Python类&#xff0c;继承了django.db.models.Model类 。该模型中每一个属性一个…

DM365 使用BT656协议驱动LCD的实现

前两天已经调好了&#xff0c;主要是对davinci_platform.c的修改 因为输入输出都为pal的制式&#xff0c;所以就在pal的函数中进行了修改。 在PAL设置的函数中&#xff0c;修改如下&#xff1a; /* * setting PAL mode */ static void davinci_enc_set_pal(struct vid_enc_mode…

工业视觉镜头NAVITAR

品牌介绍 美国NAVITAR是优越的上等光学系统制造商和供应商&#xff0c;工业视觉镜头NAVITAR为机器视觉、检测和生物医学诊断行业提供的定制光学解决方案。 工业视觉镜头NAVITAR用于鉴定产品、检查产品缺陷、测量零件尺寸、操纵机器人设备和协助进行科学分析与探索。 还用来引导…

TCP系列48—拥塞控制—11、FRTO拥塞撤销

一、概述FRTO虚假超时重传检测我们之前重传章节的文章已经介绍过了&#xff0c;这里不再重复介绍&#xff0c;针对后面的示例在说明两点1、FRTO只能用于虚假超时重传的探测&#xff0c;不能用于虚假快速重传的探测。2、延迟ER重传触发的进入Recovery状态时候&#xff0c;并不会…

娱乐一下

6年前&#xff0c;没几个人知道尤里米尔纳&#xff08;Yuri Milner&#xff09;是谁。但今天&#xff0c;他已经是地球上最有名的投资人了。 短短几年内&#xff0c;这家伙掌管的风险投资基金DST&#xff08;数字天空科技&#xff09;投遍了全球的互联网明星企业&#xff0c;并…

机器视觉行业市场现状及发展前景分析

1.中国的机器视觉起步较晚&#xff0c;目前正处于快速增长期。 我国机器视觉最早起源于20世纪80年代。机器视觉生产线和先进设备自1998年众多电子、半导体企业落户广东、上海以来&#xff0c;先后在国内诞生了国际代理商和机器视觉系统集成商。 第一个阶段是1999-2003年的启蒙…

从bootm 命令讲起/U-boot的环境变量: bootcmd 和bootargs

从bootm 命令讲起 1 找到linux的内核入口 Bootm命令通过读取uImage的头部040字节的信息&#xff0c;将uImage定位到正确的地址&#xff0c;同时找到linux的内核入口地址。 这个地方就涉及到uImage的头部040字节信息到底是什么的问题?uboot提供了mkimage命令去把040字节加在lin…