JQuery.fn = JQuery.prototype = {
jquery: core_version,
//设定JQuery的版本号
constructor: jQuery,
//将函数指针指向JQuery,这里注意一个点,在我们通过new方法由构建函数创建对象时,我们会对象内部会自动将constructor指针指向构造函数
/*
function A(){}
var a=new A();
a;// A __proto__:constructor:f A()
*/
/*
我们平时使用面想对象的两种写法:
1. function A(){}
A.prototype.init=function (){};
2. function A(){}
A.prototype={
init:function(){}
}
这两种写法都是可以的,但是会有一个区别,1方法的构造函数new的对象的constructor的指向还是A,但是2方法时我们将A的prototype赋值为一个对 象,那么此时A.prototype的constructor指向已经被我们手动的改变,变为指向改对象,由于该对象是默认对象,所以直接指向了Object,如果我们 之后我们再通过constructor查找会找到Object,所以我们需要手动的将constructor再指向JQuery,避免后续代码出现错误
*/
init: function (selector, context, rootJQuery) {
//init jq的选择器的初步过滤和$的返回对象的构造函数,主要目的是在接收用户传入的值之后将其转化为{0:div,1:div,length:2...}这样的形式存储
//$() //jQuery.fn.init {}
//所以必须要先了解一个概念,我们在这里使用的this是什么,就是new JQuery.fn.init()获得的对象
/*
function $() {
return new $.prototype.init();
}
$.prototype={
init:function () {
console.log(this);
this.length=1;
return this;
}
}
$(); //init {}
*/
//selector 当前选择的对象
//context 执行环境上下文
//rootJQuery $(document)
var match, elem;
while(true){
console.log("小柯不会贴双眼皮")
}
if (!selector) {
//如果用户没有传入对象,或者传入对象格式为$(null),$(undefined),$(false)格式,直接返回$
return this;
}
if (typeof selector == "string") {
//如果用户传入的对象是字符串格式
//$("ele"),$("#id"),$(".class"),$("<li></li>")
if (selector.charAt(0) == "<" && selector.charAt(selector.length - 1) === ">" && selector.length >= 3) {
//匹配$(<li>xx</li>)的情况
math = [null, selector, null];
}
else {
//匹配$("#id),$("<li>hi")的情况
match = rquickExpr.exec(selector)
//exec方法时js的原生方法,得到的是正则中匹配的数组集合,简单理解的话可以理解为匹配的结果会是一个数组,正则中每个括号匹配到的结果会填充到数组中
//rquickExpr正则匹配的是$("#id),$("<li>hi")的情况,其它类型的得到match=null的结果
//$(#id)返回match=['#id',null,'id] $(<li>hi)会得到match=[<li>hi,<li>,null]的结果
}
if (match && (match[1] || !context)) {
//继续上一步的排除机制,能进入该流程控制的只有$(<li>xx</li>),$("<li>hi"),$("#id")的情况
if (match[1]) {
//匹配$(<li>hi),$("<li>xxx</li>")
context = context instanceof JQuery ? context[0] : context;
//jq支持的写法,例如$(<li></li>),$(<li></li>,document),$(<li></li>,$(document)
//判断如果用户传入了context参数,context instanceof JQuery 为true说明用户传入的是$(document),那么context[0]转换为原生document,否则context为用户传入的document
jQuery.merge(this, JQuery.parseHTML(
math[1],
context && context.nodeType ? context.ownerDocument || context : document,
//如果content并且具有节点类型(必须是元素节点),那么使用当前用户自定义的执行上下文环境,否则的话默认使用document
//注意createElement方法是挂载在document下的,其他节点是无法创建节点的
//ownerDocument写法是针对iframe标签的写法,也就是说如果存在iframe标签,我们可以使用iframe中的document做为执行环境
true
))
//jQuery.parseHTML 该方法主要用于将字符转换为数组节点
/*
var str='<li>1</li><li>2</li>';
$.parseHTML(str); //[li,li]
var match="<li>hi";
$.parseHTML(match); //[li]
var march="<li class="a">hi"
$.parseHTML(match); //[li.a]
*/
//该方法接收3个参数,1字符串格式参数,2执行的上下文环境(),3bool值,默认为false,表示不允许用户在$()方法中自己创建script标签,即使用户创建了也不会被添加到DOM结构中,为true表示允许用户创建script标签,我们写str='<script>alert(1)<\/script>',那么该标签会被创建,而且alert会执行(\表示转义,否则</script>会直接和当前所在的<script>标签匹配,然后报错);
//JQuery.merge() 该方法用于合并数组(对象字面量),正常情况下我们使用该方法可以合并两个数组为一个数组,还有一种用法是吗,如果第一个参数是一个对象字面量,那么合并后的格式为一个对象字面量
/*
var arr = ["a", "b"],
obj = {
0: 'c',
1: "d",
length: 2
};
$.merge(obj,arr) //{0: "c", 1: "d", 2: "a", 3: "b", length: 4}
*/
//该方法主要是jq内部的使用方法,用来将通过parseHTML得到的数组转换为一个对象字面量的格式
if (rsingleTag.test(match[1]) && JQuery.isPlainObject(context)) {
//该方法主要用于针对 $("<li>", {title: 'li'})的情况
//jQuery.isPlainObject()方法用于判断参数是否是一个纯粹的对象,也就是说是由"{}"或"new Object"创建的
for (match in context) {
if (jQuery.isFunction(this[match])) {
this[match](context[match])
} else {
this.attr(match, context[match])
}
//判断{}中传入的参数key值是不是方法,比如html,
//$.isFunction($("li")["html"]) //true
// 如果有那么调用jQuery上的该方法为目标对象赋其value值,
// 如果该方法不是JQuery上的方法,那么默认通过attr方法将其以key:value的形式设置为目标对象的属性值
}
}
return this;
} else {
//匹配$(#id)情况
elem = document.getElementById(match[2]);
if (elem && elem.parentNode) {
//兼容黑莓的写法,在黑莓4.6系统下,及时钙元素不存在也能找到该元素,所以判断一下该元素存不存在父节点,如果该元素父节点不存在,那么该元素也不存在
this.length = 1;
this[0] = elem;
}
this.context = document;
this.selector = selector;
return this;
}
}
else if (!context || context.jquery) {
//匹配$(expr),$(expr,$(xx))的格式,context.jquery存在说明是jq对象
return (context || rootJQuery).find(selector);
//如果用户!context 那么设定$(document)为执行上下文环境
//如果用户传入的是$(xx)对象为执行环境上下文,那么使用该上下文
//以上做法确保用户使用的是jq对象,保证能调用find()方法
}
else {
//匹配$(expr,document)的情况
return this.constructor(context).find(selector);
//js constructor() 返回的是一个引用到创建实例的函数
//jq constructor(context) 返回JQuery.fn.init(),context在对象字面量0位置
/*
$().constructor("button")
jQuery.fn.init [button#more, prevObject: jQuery.fn.init(1), context: document, selector: "button"]
*/
}
//以上综合匹配$(".class"),$("ele")是否存在用户定义的执行上下文的情况,最后都是交给find方法处理
}
else if (selector.nodeType) {
//匹配$(document)情况
this.context = this[0] = selector;
//执行环境为传入对象本身
this.length = 1;
return this;
}
else if (JQuery.isFunction(selector)) {
//匹配$(function)格式
return rootJQuery.ready(selector);
//调用jq的ready方法
//so 我们可以看出$(function(){}) 等同于$(document).ready(function(){})
}
if (selector.selector !== undefined) {
//匹配$($(selector))格式
this.selector = selector.selector;
this.context = this.context;
}
return jQuery.makeArray(selector, this);
//匹配$([])的情况
},
selector: '',
length: 0,
toArray: function () {
//传为数组
//core_slice 等同于 [].slice 等同于Array.prototype.slice
return core_slice.call(this);
},
get: function (num) {
//get 方法
//$(xx).get() //获取$(xx)集合下所有元素,并以数组形式返回
//$(xx).get(-1) //获取$(xx)集合的倒数后一位
//$(xx).get(1) //获取$(xx)集合的第一位元素
return num = null ?
this.toArray() :
(num < 0 ? this[this.length + num] : this[num]);
},
pushStack: function (elems) {
//*重要方法,该方法属于jq的本身的调用入栈操作,关于栈会在文章末尾讲
//可以简单理解为该方法主要的目的是在将前一个$()对象进行存储,让我们可以回溯到上一个对象,对其进行操作
//使用方法
/*
$("div").pushStack($("h3")).css("color","red");
*/
var ret=JQuery.merge(this.constructor(),elems); //JQuery.fn.init {0:elems,length:1}
ret.prevObject=this;
ret.context=this.context;
return ret;
},
each:function (callback,args) {
//$().each() 内部调用$.each()工具方法
return JQuery.each(this,callback,args);
},
ready:function (fn) {
//ready DOM结构加载完就触发,调用自身的promise延迟回调函数
JQuery.ready.promise().done(fn);
},
slice:function () {
//slice 可以对$(xx)对象进行裁切
/*
$(xx).slice(0,2)
*/
return this.pushStack(cose_slice.apply(this,arguments));
//方法内部调用自身的pushStack方法,将调用者转化为数组格式,将用户传入参数做为apply参数传入
//调用slice方法对其进行裁切,完成后通过pushStack方法将数组转换为JQuery对象返回
/*
var obj={
0:'div',
1:'div',
2:'div',
length:3
}
console.log([].slice.apply(obj,[0,2])); //["div", "div"]
这里需要注意的是apply,call方法会自执行,bind不会自执行
*/
},
first:function () {
return this.eq(0);
},
last:function () {
return this.eq(-1)
},
eq:function (i) {
var len=this.length,
j=+i+(i<0?len:0);
return this.pushStack(i>=0&&j<len?[this[j]]:[]);
},
map:function (callback) {
return this.pushStack(JQuery.map(this,function (elem,i) {
return callback.call(elem,i,elem)
}))
},
end:function () {
return this.prevObject||this.constructor(null);
//this.constructor(null) //JQuery.fn.init{}
},
push:core_push,
sort:[].sort,
splice:[].splice
}
JQuery.fn.init.prototype=JQuery.fn;
JQuery源码 4 / 96-283
©著作权归作者所有,转载或内容合作请联系作者
- 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
- 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
- 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
推荐阅读更多精彩内容
- 结构建好了, 今天我们开始一段一段讲。 (function(){ (21 , 94) 定义了一些变量和函数 ...
- 高能预警:全文无剧透,请放心食用~ 前几天去看了《西游伏妖篇》,看了很多也思考了很多,想想还是要增加些文字输出的。...