$()函数到底做的什么

  jQuery在前端领域路人皆知,对于一向喜欢玩js的博主来说,虽然能力有限,但是还是很喜欢研究他的做为。那么一个简单的美元符号$与一对常见的()括号,jQuery底层到底做了哪些工作,如果你是前端新人,并喜欢刨根问底,你可以看一下下面的介绍。如果你是有经验的牛人,你可以指出错误,毕竟博主还是个半瓶子醋,没法完全理解。

一、函数调用

$(selector, context) : $()这一个简单的代码,其实调用了jQuery的构造函数,其中的selector和context是传递给jQuery构造函数的参数表达式,前者代表着选择器字符串,后者是上下文,jQuery会根据这两个参数,在页面中找到相应的dom对象,并包装成jQuery对象返回给我们。

var jQuery = function(selector, context) {
return new jQuery.fn.init(selector, context, rootjQuery);
} jQuery.fn = jQuery.prototype = {
init: function (selector, context, rootjQuery) {
// ...
}
} jQuery.fn.init.prototype = jQuery.fn;

调用jQuery()后,返回了一个new jQuery.fn.init(),其实就是jQuery实例,因为jQuery.fn.init 的原型指向了jQuery.fn,也就是jQuery的原型,这部分不需要解释了,是原型链的问题了。

二、步入函数

就这样jQuery进入到了init函数中,接下来看下面代码

// 如果没有参数,返回当前jQuery对像
if (!selector) {
return this;
}
//如果selector有nodeType属性,则是dom对象,返回包装后的jQuery对象
if (selector.nodeType) {
this.context = this[0] = selector;
this.length = 1;
return this;
}
//如果selector是body,由于body只有一个,并且我们可以明确的指出,所以直接把body元素包装后返回jQuery对象
if (selector === "body" && !context && document.body) {
this.context = document;
this[0] = document.body;
this.selector = selector;
this.length = 1;
return this;
}

由此我们可以知道 $()反回了个空的jQuery对象, $(document.body) 走了第二个if返回了body对象的jQuery对象, $('body')走了第三个if也返回了body对象的jQuery对象。

接下来,jQuery构造函数进入的最难的部分,请看着源码来读下面的介绍。

一、如果selector是字符串类型,有两种情况:

1. 如果是dom元素字符串,如$('<div>')  或者 $('<div>', {title: 'good'})等情况,走一种处理方法

2. 如果是单独的div选择器,如$('#box') ,走一种处理方法

3.1. 如果不是上面两种情况,如果context参数存在并且是jQuery对象,则用这个jQuery对象查找selector,如 $('span', $('body')) 相当于 $('body').find('span')

3.2. 如果不是1、2两种情况,并没有context参数,就用根jQuery对象(document的jQuery对象)查找selector,如: $('span') 相当于 $(document).find('span')

4. 其他情况,就是context是dom对象时,用context包装成jQuery对象查找selector, 如$('span', document.body) => $(document.body).find('span')

二、如果selector是函数,那么进入ready方法,等待dom加载完毕执行函数,也就是$(function () {...})

三、如果以上都不是,并且selector有selector和context属性,这代表什么呢?当然是传进来的jQuery对象了,如果是这样,那jQuery返回一个类似的新对象。。。

三、难区详解(selector是字符串类型的1、2两种情况)

  由容易到难,现说是id选择器的情况,再详细说是dom元素字符串的情况。。。

1. selector是个id选择器

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

// 黑莓手机兼容解决
if (elem && elem.parentNode) {
// ie 欧朋 选择name解决
if (elem.id !== match[2]) {
return rootjQuery.find(selector);
} this.length = 1; this[0] = elem;
} this.context = document;
this.selector = selector;
return this;

如果是id选择器,就直接选出元素包装成jQuery对象返回。

2. selector是dom元素字符串


这里又分为3种情况:

1. 如果selector是一个简单的dom字符串,如: $('<div>'), $('<div></div>')

  1.1 如果第二个参数context是个纯js对象,也就是键值对,保存着属性和属性值,如$('<div>', {title: 'good'}) ,那么创建dom对象并赋值这些属性

  1.2 如果第二个参数存在并且是dom对象,那么就用这个dom对象创建selector这个dom, 如果context不是dom对象,就用document创建selector这个dom

2. 如果selector并不是一个简单的字符串,其中有属性赋值在里面,如 $('<div id="box">'),进入 jQuery.buildFragment 方法构造dom对象结构

四、dom对象的构造中转站

这里不贴代码了,同学们可以看着源码走下去。

jQuery.buildFragment中,jquery先取到要转化成dom的字符串,和要插入到真是dom的dom对象,也就是context参数或者是document对象。

然后根据各种条件判断,这个dom对象是否是可缓存的,如果是可缓存,查找缓存区是否已经有了,如有有了,直接返回,如果没有,那么进入jQuery.clean方法创建并缓存。

五、dom对象的构造工坊

jQuery.clean 函数中, jQuery还是先确定下要创建的dom字符串数组,和在哪个dom下创建。

进入创建循环,看到这样一句话

if (!rhtml.test(elem)) {
elem = context.createTextNode(elem);
}

博主看rhtml正则,觉得这个是判断是不是实体字符和html标签,如果不是实体字符或html标签,创建文本节点。

往下看,如果不是实体字符,jQuery把这个字符串转化为开闭式的合格dom字符串并且取到了dom的名称

elem = elem.replace(rxhtmlTag, "<$1></$2>");

var tag = (rtagName.exec(elem) || ["", ""])[1].toLowerCase()

由于tr td option等标签,是依赖于上级的标签,所以jQuery在这里需要考虑如果要创建他们,需要先创建他们上级,所以下面代码有个

wrap = wrapMap[tag] || wrapMap._default

那我们去查看wrapMap这个对象是什么

wrapMap = {
option: [1, "<select multiple='multiple'>", "</select>"],
legend: [1, "<fieldset>", "</fieldset>"],
thead: [1, "<table>", "</table>"],
tr: [2, "<table><tbody>", "</tbody></table>"],
td: [3, "<table><tbody><tr>", "</tr></tbody></table>"],
col: [2, "<table><tbody></tbody><colgroup>", "</colgroup></table>"],
area: [1, "<map>", "</map>"],
_default: [0, "", ""]
}

我们来到了这里,看到了这个对象。我们可以看到,这个对象包含了那些需要上级dom才能存活的dom列表,他们的值是一个数组,数组第一个表示的层级关系,比如option是select下的第一级, tr是table下的第二级,数组第二个和第三个元素很明显分别表示了开口标签闭口标签,那么有什么用呢?继续往下看

div.innerHTML = wrap[1] + elem + wrap[2];

看到下面几行,终于茅塞顿开,原来是这样创建的依赖上级元素的 dom元素,前后上级标签中间夹着需要创建dom的字符串,把整个字符串赋值给一个div,构成了完整的dom。知道了后,往回走。

safeChildNodes = safeFragment.childNodes

上面还有一行这个是什么意思呢?我们去看看safeFragment把

//    h5标签
nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" +
"header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", safeFragment = createSafeFragment(document); function createSafeFragment(document) {
var list = nodeNames.split("|"),
safeFrag = document.createDocumentFragment(); if (safeFrag.createElement) {
while (list.length) {
safeFrag.createElement(
list.pop()
);
}
}
return safeFrag;
}

我们找到了这三个重要的表达式,哦,原来这个safeFragment保存着所有h5的标签

if (context === document) {
safeFragment.appendChild(div);
} else {
createSafeFragment(context).appendChild(div);
}

接下来jQuery保存了之前我们创建的div,还不知道要干什么,继续往下看吧。

while (depth--) {
div = div.lastChild;
}

接下来,jQuery根据层级关系,一层一层的把外部容器去掉,只留下最后一层包裹着我们想要的那个dom。

下面,就是对ie的两个兼容,ie下自动生成tobody,jQuery做了兼容,ie下破坏首行缩进,jQuery做了兼容。

elem = div.childNodes;
if (div) {
div.parentNode.removeChild(div); if (safeChildNodes.length > 0) {
remove = safeChildNodes[safeChildNodes.length - 1]; if (remove && remove.parentNode) {
remove.parentNode.removeChild(remove);
}
}
}
}

保存我们需要的dom节点后,把此次循环的div删掉。把插入到safeFragment的div也删掉,清空干净,马上要进入下一循环

var len;
if (!jQuery.support.appendChecked) {
if (elem[0] && typeof(len = elem.length) === "number") {
for (j = 0; j < len; j++) {
findInputs(elem[j]);
}
} else {
findInputs(elem);
}
}

上面这一个if针对的 checkbox和radio元素在ie下checked失效的bug

if (elem.nodeType) {
ret.push(elem);
} else {
ret = jQuery.merge(ret, elem);
}

终于,保存了此次dom元素到ret数组中。然后下面的一个if是对script标签的插入处理,博主没有去看,因为不常用啊。。

终点

  终于,jQuery方法走完了,走的好慢好艰辛,走过了这次行程,下一次是不是就很好走了O(∩_∩)O~。

浅谈jQuery构造函数的更多相关文章

  1. 浅谈jquery关于select框的取值和赋值

    浅谈jquery关于select框的取值和赋值   jQuery("#select_id").change(function(){}); // 1.为Select添加事件,当选择其 ...

  2. 浅谈jQuery中的Ajax

    浅谈jQuery中的Ajax 一.前言 jQuery 对 Ajax 操作进行了封装, 在 jQuery 中最底层的方法时 $.ajax(), 第二层是 load(), $.get() 和 $.post ...

  3. 车大棒浅谈jQuery源码(二)

    前言 本来只是一个自己学习jQuery笔记的简单分享,没想到获得这么多人赏识.我自己也是傻呵呵的一脸迷茫,感觉到受宠若惊. 不过还是有人向批判我的文章说,这是基本知识点,完全跟jQuery源码沾不上边 ...

  4. [转]浅谈jQuery EasyUI的属性设置

    原文地址:http://www.easyui.info/archives/1664.html 对jQuery EasyUI有一定了解的话,应该知道基本上每一个组件都有一个"options&q ...

  5. 浅谈 jQuery 核心架构设计

    jQuery对于大家而言并不陌生,因此关于它是什么以及它的作用,在这里我就不多言了,而本篇文章的目的是想通过对源码简单的分析来讨论 jQuery 的核心架构设计,以及jQuery 是如何利用javas ...

  6. 浅谈 jQuery 事件源码定位问题

    该方法已过期,chrome 48还是49开始,自带各种流行框架的事件绑定解析. 勾上这个选项即可. 昨天群里有人问了个事件源码定位的问题,简单描述下是这样的. 在一个不是自己写的页面上,如何快速定位到 ...

  7. 浅谈Jquery中的bind(),live(),delegate(),on()绑定事件方式

    前言 因为项目中经常会有利用jquery操作dom元素的增删操作,所以会涉及到dom元素的绑定事件方式,简单的归纳一下bind,live,delegate,on的区别,以便以后查阅,也希望该文章日后能 ...

  8. 浅谈jQuery页面的滚动位置scrollTop、scrollLeft

    Web页面常常比显示该页面的浏览器窗口还要大,因为Web文档具有很多内容,往往会导致页面比浏览器还要高,有时候甚至还要宽,这迫使访问者通过滚动来查看整个页面(如图10-8所示).当访问者滚动页面的时候 ...

  9. 浅谈jquery选择器

    首先来说说jquery选择器的优势: 1.简洁的写法  2.支持css1.0到3.0选择器 3.完善的处理机制. 再来说说分类: jquery选择器分为基本选择器.层次选择器.属性选择器.基本过滤选择 ...

随机推荐

  1. (转)MySQL高可用方案MHA的部署和原理

    背后深层次的逻辑: MHA Node则运行在每个mysql节点上,MHA Manager会定时探测集群中的master节点,当master出现故障时,它自动将最新数据的slave提升为master,然 ...

  2. 统计单词出现的最多次数(Trie树)

    A Time Limit: 60ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描写叙述 给出n(1<= n && n <= 2*10^6) ...

  3. Mysql5.7登录错误1045和1130的解决方法,亲测有用,希望能帮助到你们。

    Mysql (针对Mysql5.7版本,其他版本可能略有不同) 错误:1045 解决方法: 以管理员身份运行cmd(win8系统:win+x 键 ,再按 A键 ),进入Mysql安装目录下的bin目录 ...

  4. 文本处理三剑客之 awk

    GAWK:报告生成器,格式化文本输出 awk [options] ‘program’ var=value file… awk [options] -f programfile var=value fi ...

  5. php 查看使用多少内存

    方法memory_get_usage 是指当前脚本正在使用的内存 unset只是把内存标记为空闲但并没有释放,要GC程序结束后才会释放 $bytes = memory_get_peak_usage() ...

  6. kettle安装部署及远程执行

    一.windows环境 1.安装jdk 随意选择目录 只需把默认安装目录 \java 之前的目录修改即可 2.安装jre→更改→ \java 之前目录和安装 jdk 目录相同即可 注:若无安装目录要求 ...

  7. PostgreSQL处理xml数据初步

    磨砺技术珠矶,践行数据之道,追求卓越价值回到上一级页面:PostgreSQL基础知识与基本操作索引页    回到顶级页面:PostgreSQL索引页[作者 高健@博客园  luckyjackgao@g ...

  8. centos 中sshd莫名其妙不见了?

    发现问题 遇到问题:首先莫要慌:事出有因:先检查一波: 首先呢,看一下/var/log/yum.log  是否有误删的记录: 如有被误删的操作的话:可以去看看日志:到底咋回事: 然后么 yum ins ...

  9. [PLC]ST语言五:STL/RET/CMP/ZCP

    一:STL/RET/CMP/ZCP 说明:简单的顺控指令不做其他说明. 控制要求:无 编程梯形图: 结构化编程ST语言: (*步进指令STL(EN,s);*) SET(M8002,S3); STL(T ...

  10. 我在华为,软件测试人员在工作中如何运用Linux?

    从事过软件测试的小伙们就会明白会使用Linux是多么重要的一件事,工作时需要用到,面试时会被问到,简历中需要写到.对于软件测试人员来说,不需要你多么熟练使用Linux所有命令,也不需要你对Linux系 ...