一、jQuery的架构划分
- jQuery无new构建实例
- 共享原型设计
- extend源码解析
二、jQuery无new构建实例 以及 共享原型设计
结构如下:
(function(root) {
var jQuery = function() {
return new jQuery.prototype.init();
}
jQuery.prototype = {
init: function() {
},
css: function() {
}
}
jQuery.prototype.init.prototype = jQuery.prototype;
root.$ = root.jQuery = jQuery;
})(this);
值得注意:
- 以上就是jQuery最基本的架构了
- jQuery返回的是init属性,而不是new一个对象返回
- 如果返回的是一个new对象
(function(root){ var jQuery = function(){ return new jQuery() } })(this)
这样就是形成死循环,所以jQuery就想出了一个共享原型的设计
jQuery.prototype.init.prototype = jQuery.prototype;
这个就是共享原型设计
三、extend源码解析
1. extend的使用方法
- 给任意对象扩充
var obj = $.extend({},{name:"毛毛"}) eg:obj.name
- 给jQuery本身扩充方法和对象
$.extend({work:function(){ }}) eg:$.work()
- 也可以使用$.fn来扩充 ,对jQuery的prototype进行扩充
$.fn.extend({type:"小动物"}) eg:$().type
2. 为什么$.extend和$.fn.extend都可以扩充对象
只需赋一下值就行了
(function(root) {
var jQuery = function() {
return new jQuery.prototype.init();
}
jQuery.fn = jQuery.prototype = {
init: function() {
},
css: function() {
}
}
//extend
jQuery.fn.extend = jQuery.extend = function() {}
//共享原型对象
jQuery.fn.init.prototype = jQuery.fn;
root.$ = root.jQuery = jQuery;
})(this);
只需要
jQuery.fn = jQuery.prototype
就可以实现$.fn与$相同的功效了,唯一不同的是$.fn代表的是jQuery.prototype,而$代表的是jQuery
3. 利用extend对jQuery或者任意对象扩展
思路:获取extend中的参数,判断参数个数,如果参数只有一个对象,则就是给jQuery本身扩展;如果有不止一个对象,则将扩展参数赋值给第一个对象,实现给任意对象扩展。
代码如下:
jQuery.fn.extend = jQuery.extend = function(){
var target = arguments[0] || {}
var length = arguments.length
var i = 1
var option , name
if(typeof target !== "object"){
target = {}
}
//参数个数,只有一个就是给jQuery本身扩充
if(length === i){
target = this
i--
}
//拷贝参数给要赋值的对象(这里是浅拷贝)
for(;i<length;i++){
//将参数赋值给target
if((option = arguments[i] != null)){
for(name in option){
target[name] = option[name]
}
}
}
return target
}
到此结束,就实现了对jQuery本身或者给任意对象扩展,但是这里用的是浅拷贝,不是深拷贝
4.浅拷贝?深拷贝?
简单说,就是浅拷贝只是从表面上进行了复制替换,深拷贝则相当于深度遍历之后进行了复制替换
举个例行:
var a = {name:'apple',other:{work:'Doctor'}}
var b = {other:{like:'basketball'}}
将上述b的属性拷贝到a上
浅拷贝:
a = {name:'apple',other:{like:'basketball'}}
深拷贝:
a = {name:'apple',other:{work:'Doctor',like:'basketball'}}
5.优化一下,将浅拷贝替换成深拷贝
在jQuery中如果第一个参数设置为true,则默认进行深拷贝,如果设置为false或者不写则默认是浅拷贝
var obj = $.extend({},{name:"毛毛"})
这是浅拷贝
var obj = $.extend(true,{},{name:"毛毛"})
这是深拷贝
实现思路:就是第3节的代码块中,对参数判断时,对第一个参数判断是true还是false,如果为false,后面属性遍历不变。如果是true,后面属性遍历需要改为深拷贝的遍历。
jQuery.fn.extend = jQuery.extend = function() {
var target = arguments[0] || {};
var length = arguments.length;
var i = 1;
var deep = false; //确定是要浅拷贝还是深拷贝
var option, name,copy,src,copyIsArray,clone;
//如果深拷贝,target是第二个参数,从第三个参数开始扩展
if (typeof target === "boolean") {
deep = target;
target = arguments[1];
i = 2;
}
if (typeof target !== "object") {
target = {};
}
//如果没有指定扩展对象,则默认是jQuery本身,参数从i-1开始
if (length === i) {
target = this;
i--;
}
//浅拷贝 深拷贝
for (; i < length; i++) {
if ((option = arguments[i]) != null) {
for (name in option) {
copy = option[name];
src = target[name];
//深拷贝
if(deep && (jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)))){
//确保要传入的参数是数组或者对象
if(copyIsArray){
copyIsArray = false;
//深拷贝精髓
clone = src && jQuery.isArray(src) ? src : [];
} else {
clone = src && jQuery.isPlainObject(src) ? src : {};
}
//递归深度遍历
target[name] = jQuery.extend(deep, clone, copy);
//浅拷贝
} else if(copy != undefined){
target[name] = copy;
}
}
}
}
return target;
}
需要注意:
- jQuery.isPlainObject和jQuery.isArray都只是用jQuery扩充的类型判断,不需要太在意
- 深拷贝中有两处精髓
clone = src && jQuery.isArray(src) ? src : [];
clone = src && jQuery.isPlainObject(src) ? src : {};
这个就是实现深拷贝的精髓了target[name] = jQuery.extend(deep, clone, copy);
这个采用了递归的方式,也就是上面说的深度遍历的方式拷贝了所有的属性- 有时候想不明白,就举个例子调试着跑一遍,一步一步跑完了就明白了。
例如:
var a = {} , res = {name:'max',list:{age:10}}
$.extend(true,a,res)
这个就是给空对象a扩展对象res
①a.name没有值,赋值为a.name={}
②a.name浅拷贝赋值为max
③a.list没有值,赋值为a.list = {}
④a.list.age没有值,赋值为a.list.age = {}
⑤a.list.age浅拷贝,赋值为a.list.age = 10
⑥扩展结束
6. 完整的代码
(function(root) {
var jQuery = function() {
return new jQuery.prototype.init();
}
jQuery.fn = jQuery.prototype = {
init: function() {
},
css: function() {
}
}
//extend
jQuery.fn.extend = jQuery.extend = function() {
var target = arguments[0] || {};
var length = arguments.length;
var i = 1;
var deep = false;
var option, name,copy,src,copyIsArray,clone;
if (typeof target === "boolean") {
deep = target;
target = arguments[1];
i = 2;
}
if (typeof target !== "object") {
target = {};
}
//参数的个数 1
if (length === i) {
target = this;
i--;
}
//浅拷贝 深拷贝
for (; i < length; i++) {
if ((option = arguments[i]) != null) {
for (name in option) {
copy = option[name];
src = target[name];
if(deep && (jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)))){
if(copyIsArray){
copyIsArray = false;
clone = src && jQuery.isArray(src) ? src : [];
} else {
clone = src && jQuery.isPlainObject(src) ? src : {};
}
target[name] = jQuery.extend(deep, clone, copy);
} else if(copy != undefined){
target[name] = copy;
}
}
}
}
return target;
}
//共享原型对象
jQuery.fn.init.prototype = jQuery.fn;
jQuery.extend({
//类型检测
isPlainObject: function(obj){
return toString.call(obj) === "[object Object]";
},
isArray: function(obj){
return toString.call(obj) === "[object Array]";
}
});
root.$ = root.jQuery = jQuery;
})(this);