jQuery 整体架构:
JQuery框架的核心就是从HTML文档中匹配元素并对其执行操作,那么有两个问题值得我们思考?
- jQuery 对象构建的方式?
- jQuery 方法的调用方式?
jQuery 对象构建方式 实现的关键点:
1. 可以将jQuery类当做一个工厂方法来创建实例,并将这个方法放到jQuery.prototype 原型中
2. 对作用域进行处理,来分隔this,避免交互混淆
var aQuery = function(selector,context){
//每次构建新的init 实例,来分隔this
return new aQuery.prototype.init();
}
aQuery.prototype = {
init : function(){
this.age = 18;
return this;
},
name : function(){},
age : 20
}
console.log(aQuery());
console.log(aQuery().name()); //报错:Uncaught TypeError: aQuery(...).name is not a function
访问jQuery类原型上的属性与方法:
既能做到隔离作用域还能使用jQuery原型对象的作用域,还能在返回实例中访问jQuery 的原型对象
实现的关键点:
jQuery.fn.init.prototype = jQuery.fn; //用jQuery 原型对象覆盖了init 构造器的原型对象
var aQuery = function(selector,context){
return new aQuery.prototype.init();
}
aQuery.prototype = {
init : function(){
return this;
},
name : function(){
return this.age;
},
age : 20
}
aQuery.prototype.init.prototype = aQuery.prototype;
console.log(aQuery().name()); //20
jQuery 链式调用:
DOM 链式调用的处理:
1. 节约JS代码
2. 返回的都是同一个对象,可以提高代码的效率
关键点:
通过简单扩展原型方法并通过return this 的形式来实现跨浏览器的链式调用
利用JS下的简单工厂模式,来将所有对于同一个DOM对象的操作指定同一个实例
aQuery().init().name()
分解
a = aQuery();
a.init();
a.name();
//链式调用,返回当前实例的this,从而又可以访问自己的原型
var aQuery = function(selector,context){
return new aQuery.prototype.init();
}
aQuery.prototype = {
init : function(){
this.age = 18;
return this;
},
name : function(){
this.age++;
return this;
}
}
console.log(aQuery.prototype.init().name()); //19
jQuery 插件接口:
jQuery 支持自己扩展属性,这个对外提供了一个接口,jQuery.fn.extend( )来对对象增加方法;
jQuery.extend 和 jQuery.fn.extend 其实是同指向同一方法的不同引用
jQuery.extend 对jQuery 本身的属性和方法进行了扩展
jQuery.fn.extend 对jQuery.fn 的属性和方法 进行了扩展
通过extend()函数可以方便快速的扩展功能,不会破坏jQuery 的原型结构
jQuery.extend = jQuery.fn.extend = function(){...} 这个是连等,也就是2个指向同一个函数,不过不怎么会实现不通过的功能呢? 这就是this 的力量了!
- jQuery.extend 调用的时候,this 是指向jQuery对象的,所以这里扩展在jQuery上
- jQuery.fn.extend 调用的时候,this 指向 fn 对象, jQuery.fn 和 jQuery.prototype 指向同一对象,扩展fn 就是扩展jQuery.prototype 原型对象
<script>
jQuery.extend = jQuery.fn.extend = function(){
/**
* target:被扩展的对象
* length:参数的数量
* deep:是否深度复制
*/
var options,name,src,copy,copyIsArray,clone,
target = arguments[0] || {},
length = arguments.length,
i = 1,
deep = false;
//target 为第一个参数,如果第一个参数是boolean类型的值,则把target 的值赋值给deep
if(typeof target === 'boolean'){
deep = target;
target = arguments[1] || {};
// 将i 赋值为2,跳过前两个参数,方便 后面的for循环逻辑衔接
i = 2;
}
// target 既不是对象也不是函数,则把target 设置为空对象
if(typeof target !== "object" && jQuery.isFunction(target)){
target = {};
}
//如果只有一个参数,则把jQuery 对象赋值给 target,即扩展到 jQuery 对象上
if(length === i){
target = this;
//i 减1,指向 被扩展对象
--i;
}
//开始遍历需要被扩展到target上的参数
for(;i < length;i++){
//处理第i个被扩展的对象
if(options = arguments[i] !== null){
//遍历第i 个对象所有可遍历的属性
for(name in options){
//根据被扩展对象的键 获得目标对象相应值,并赋值给src
src = target[name];
//得到被扩展对象的值
copy = options[name];
//此处不比较src 和 copy 的原因是 避免自引用 造成栈溢出
if(target === copy){
continue;
}
//如果是深拷贝 且被拷贝的属性值本身是个对象
if(deep && copy && (jQuery.isPlainObject(copy) || copyIsArray = jQuery.isArray(copy))){
//如果是数组
if(copyIsArray){
// 将copyIsArray 重新设置为false,为下次遍历做准备
copyIsArray = false;
clone = src && jQuery.isArray(src) ? src : [];
}else{
//判断 被扩展的对象中src 是不是纯对象
clone = src && jQuery.isPlainObject(src) ? src : {};
}
//递归调用extend 方法,继续深度遍历
target[name] = jQuery.extend(deep,clone,copy);
//如果不需要深度复制,则直接把copy
}else if(copy !== undefined){
target[name] = copy;
}
}
}
}
return target;
}
</script>
总结:
- 通过new jQuery.fn.init() 构建一个新的对象,拥有init构造器的prototype原型对象的方法
- 通过改变prototype指针的指向,让这个新的对象也指向了jQuery 类的原型prototype
- -