jQuery源码解读 - 核心架构的处理

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 )

  • $(function(){}) 等价于 $(document).ready(function(){})
    • 基于addEventListener实现的事件绑定,可以页面中多次使用
    • 给当前元素document的DOMCOntentLoaded事件绑定多个不同的方法
  • DOMCOntentLoaded VS load
    • DOMCOntentLoaded: document.addEventListener(“DOMCOntentLoaded”,function(){})
      • 等待页面中的DOM结构都加载完成,会触发事件执行“把对应的函数执行”
    • load: document.addEventListener(“load”,function(){})
      • 等待页面中的所有资源都加载完成(含DOM结构加载完成 & 其他资源加载完成)

  • $("<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方法不具备的
在这里插入图片描述在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值