jQuery基础框架
(function( global, factory ) {
"use strict";
if ( typeof module === "object" && typeof module.exports === "object" ) {
module.exports = global.document ?
factory( global, true ):
function( w ) {
if ( !w.document ) throw new Error( "jQuery requires a window with a document" );
return factory( w );
};
} else {
factory(global);
};
})(typeof window !== "undefined" ? window : this,function factory( window, noGlobal){/*...*/})
$(".box").xxx();
复制数组及其方法到jquery对象或原型上
鸭子类型:
- [].slice.call(实例)
- 实例所属类的.prototype.slice = arr.slice
function factory( window, noGlobal){
"use strict";
//定义数组以及赋值其方法
var arr = [],
slice = arr.slice,
push = arr.push,
indexOf = arr.indexOf,
version = "3.5.1",
jQuery = function (selector, context) {return new jQuery.fn.init(selector, context)};
jQuery.fn = jQuery.prototype = {
//原型重定向
constructor: jQuery,//保证重定向后原型的结构完整性
jquery: version,//jQuery版本号
length: 0,//参数的长度
//赋值数组原型上的方法到jquery的原型上
push: push,
sort: arr.sort,
splice: arr.splice,
get: function (num) {
// 基于索引把AJQ对象转为DOM对象
if (num == null) return slice.call(this);
//支持负数索引
return num < 0 ? this[num + this.length] : this[num];
},
eq: function (i) {
// 基于索引查找集合中的某一项,返回一个新的“JQ对象”
var len = this.length, j = +i + (i < 0 ? len : 0); //支持负数索引
return this.pushStack(j >= 0 && j < len ? [this[j]] : []);
},
pushStack: function (elems) {
// this.constructor() ->jQuery ->jQuery() ->空的JQ对象
var ret = jQuery.merge(this.constructor(), elems);
//prevObject:在链式调用中,可以快速回到原始操作的JQ对象(根对象)
ret.prevObject = this;
return ret;
}
//...
}//Query.fn = jQuery.prototype END
jQuery.extend({
// 合并两个非数组集合
// 传递两个集合,把第二个集合中的每一项全部放置到第一个集合末尾
// - 实现“两个集合合并”,返回的是第一个集合
// - 类似于数组的concat,但是concat只能数组使用
// - merge方法可以支持类数组集合的处理
merge: function merge(first, second) {
var len = +second.length, j = 0,
i = first.length;
for (; j < len; j++) {
// 把第二个集合中的每一项全部添加到一个集合的末尾
first[i++] = second[j]
}
first.length = i;
return first;// 返回第一个集合返回
},
});
if (typeof noGlobal === "undefined") {window.jQuery = window.$ = jQuery}
return jQuery;
}
init = jQuery.fn.init
$([selector]):
- 返回的是init方法(类)的实例 [ obj ] =>
obj.__proto__ === init.prototype
init.prototype === jQuery.fn === jQuery.prototype
,即让init的原型重定向为jQuery的原型obj.__proto__ === jQuery.prototype
总结:基于JQ选择器 " $([selector])、jQuery([selector]) " 获取的是jQuery类的实例。
目的:
- 让用户使用的时候把jQuery/$当做普通函数执行,但是到最后的结果是创建在其类的一个实例
- 用户体验好,使用方便
- “工厂设计模式”
- 不基于new执行函数,创建构造函数的实例
- generator函数
prevObject:返回根元素集合
var rootjQuery,
rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,
init = jQuery.fn.init = function (selector, context, root) {
var match, elem;
if (!selector) {return this}//返回空jQuery实例: $(""), $(null), $(undefined), $(false)
root = root || rootjQuery;
if (typeof selector === "string") {
if (selector[0] === "<" &&
selector[selector.length - 1] === ">" &&
selector.length >= 3) {
match = [null, selector, null];
} else {
match = rquickExpr.exec(selector);
}
if (match && (match[1] || !context)) {
if (match[1]) {
context = context instanceof jQuery ? context[0] : context;
jQuery.merge(this, jQuery.parseHTML(
match[1],
context && context.nodeType ?
context.ownerDocument || context:
document,true));
if (rsingleTag.test(match[1]) && jQuery.isPlainObject(context)) {
for (match in context) {
if (isFunction(this[match])) {
this[match](context[match]);
} else {
this.attr(match, context[match]);
}
}
}
return this;
} else {
elem = document.getElementById(match[2]);
if (elem) {
this[0] = elem;
this.length = 1;
};
return this;
}
} else if (!context || context.jquery) {
return (context || root).find(selector);
} else {
return this.constructor(context).find(selector);
}
//传递的是一个原生的DOM/JS对象:把DO对象转换为JQ对象,这样就可以使用JQ原型上提供的方法
} else if (selector.nodeType) {
this[0] = selector;
this.length = 1;
return this;
//传递的是一个函数
} else if (isFunction(selector)) {
return root.ready !== undefined ?
root.ready(selector) :
selector(jQuery);
};
return jQuery.makeArray(selector, this);
};
init.prototype = jQuery.fn;
rootjQuery = jQuery(document);
- JQ对象:就是"jQuery实例对象",是基于选择器获取的结果,一般返回的是一个类数组集合(索引和length属性)
- " DOM / JS对象 " :基于浏览器内置方法获取的元素对象,即“浏览器内置类的实例对象”
- “JQ对象 VS DOM对象”:
- “DOM对象” -> “JQ对象”:$(“DOM对象”)
- “JQ对象” -> “DOM对象”:“JQ对象”[索引] OR “JQ对象”.get(索引) 使用内置类原型上的方法
- jQuwet.fn get VS eq
- “JQ对象”.get(索引) :基于索引把JQ对象转换为DOM对象,不传参,返回一个数组集合(把类数组转换为数组)
- 把类数组集合转换为数组集合: if( num == null ) return slice.call(this);
- 支持负数索引: num < 0 ? this[ num +this.length ] : this[ num ] ;
- eq : 基于索引查找集合中的某一项,返回一个新的“JQ对象”
- 支持负数索引:j = +i + ( i < 0 ? len : 0 )
- 支持负数索引:j = +i + ( i < 0 ? len : 0 )
- “JQ对象”.get(索引) :基于索引把JQ对象转换为DOM对象,不传参,返回一个数组集合(把类数组转换为数组)
- $(function(){}) 等价于 $(document).ready(function(){})
- 基于addEventListener实现的事件绑定,可以页面中多次使用
- 给当前元素document的DOMCOntentLoaded事件绑定多个不同的方法
- DOMCOntentLoaded VS load
- DOMCOntentLoaded: document.addEventListener(“DOMCOntentLoaded”,function(){})
- 等待页面中的DOM结构都加载完成,会触发事件执行“把对应的函数执行”
- load: document.addEventListener(“load”,function(){})
- 等待页面中的所有资源都加载完成(含DOM结构加载完成 & 其他资源加载完成)
- 等待页面中的所有资源都加载完成(含DOM结构加载完成 & 其他资源加载完成)
- DOMCOntentLoaded: document.addEventListener(“DOMCOntentLoaded”,function(){})
$("<div>xxx</div>")
VS$(".div")
$("<div>xxx</div>")
:动态创建DOM对象$(".div")
:获取一个DOM对象
- $([selector])传入的非[字符串/ OM对象/函数]
- 返回一个JQ对象,是一个类数组集合
//转为类数组
jQuery.makeArray = function makeArray(arr, result){
var ret = results || [];
if(arr != null){
if(isArray(Object(arr))){
jQuery.merge(ret, typeof arr === "string" ?[arr]:arr)
}else{
push.call(ret, arr);
};
};
return ret;
};
// 传递两个集合,把第二个集合中的每一项全部放置到第一个集合的末尾“合并两个集合”,返回的是第一个集合
// + 类似数组的concat
// + merge方法可以支持类数组集合的处理
jQuery.merge = function merge(first, second){
var len = +second.length,
j = 0,
i = first.length;
for(; j<len;j++){
first[i++] = second(j)
};
first.length = i;
return first;
}
JQ 原型上的方法 VS JQ对象上的方法
- JQ 原型上的方法:jquery.prototype.xxx = jquery.fn.xxx(实例可调用的方法)
$(".div").xxx()
调用 - JQ对象上的方法:jQuery可调用的方法:
$.xxx() / jQuery.xxx()
调用
引申:封装一个遍历数组、类数组、对象的EACH插件
//utile.js
var each = function each(obj,callback){
//保证callback是一个函数,Function.prototype:匿名空函数
typeof callback !=="function" ? Function.prototype: null;
var length, i = 0;
if (isArrayLike(obj)) {//obj是一个数组或者类数组
length = obj.length;
for (; i < length; i++) {
var item = obj[i],
result = callback(item, item, i)
if (result === false) break;
}
} else {//obj是对象
var keys = Object.keys(obj);
typeof Symbol !== "undefined"? keys.concat(Object.getOwnPropertySymbols(obj)) :null;
i = 0;
length = keys.length;
var key = keys[i], value = obj[key];
if( callback.call(value,value,key) === false ) break;
};
return obj;
};
var utiles = {each :each }
window.utiles = window._ = utiles;
each支持回调函数返回值处理:返回false则循环结束,这是内置的forEach/map方法不具备的