一、jQuery无new化构建实例
1、$就是jQuery的别称
$就是jQuery的别称,它们指向同一个地址:
console.log($ === jQuery); // true
$和jQuery都是挂载在window上的,以便实现全局引用:
window.$ = window.jQuery = jQuery;
2、$()就是在创建jQuery的实例
(function(window){
var jQuery = function(){
// return 一个实例对象
};
window.$ = window.jQuery = jQuery;
})(window);
二、共享原型设计
jQuery的共享原型设计真的是一个非常棒的设计。
1、错误示范
按照平常的思维,一开始我们可能会想直接retur一个new jQuery():
var jQuery = function(){
return new jQuery();
};
但是这样函数不断地调用本身创建一个新实例,会造成死循环的问题,这样是不行的。
2、jQuery的共享原型设计
jQuery的共享原型是怎么设计的呢?先来看一张图:
$时,会去找到jQuery原型上的init方法,把init当做一个构造函数,然后返回init的实例对象。但我们实际是要创建jQuery对象,并访问到jQuery原型上扩展的属性和方法。所以jQuery是这样做的:将jQuery原型上的init构造函数和jQuery本身共享一个原型。
具体实现如下:
(function (window) {
var jQuery = function () {
// return new jQuery(); // 不合理的实例对象创建,会造成死循环
return new jQuery.prototype.init();
};
jQuery.prototype = {
init: function () {},
// 可以扩展其他方法
css: function () {},
};
jQuery.prototype.init.prototype = jQuery.prototype; // 共享原型对象
window.$ = window.jQuery = jQuery;
})(window);
三、jQuery的核心函数extend()
1、extend的功能
extend可以做任意对象的扩展,jQuery内部是使用extend方法来进行自身属性和方法扩展,我们也可以利用extend方法来自定义一些属性或者方法。
// 1、对对象进行扩展
$.extend({}, { name: 'vincent'});
// 2、对jQuery对象本身扩展属性或者方法
$.extend({
work: function () {
console.log('work');
},
});
$.work(); // work
// 3、给jQuery的实例对象扩展属性或者方法
$.fn.extend({
sex: '男'
});
console.log($().sex); // 男
2、jQuery的实例对象fn
给jQuery扩展一个实例对象fn,使它等于jQuery的原型:
jQuery.fn = jQuery.prototype = {
init: function () {},
};
共享原型也可以写成以下格式:
jQuery.fn.init.prototype = jQuery.fn;
3、extend的实现
$和$.fn都可以调用到extend这个方法,那么这个extend是同一个方法吗?是的,确实是的。在jQuery里面是这样实现的:
jQuery.fn.extend = jQuery.extend = function() {};
我们来分析一下,extend可以对一个对象进行扩展;也可以对jQuery本身或者jQuery的实例对象fn进行扩展。既然这样我们就可以通过extend传入的第一个参数进行处理了:
1、如果有多个对象,第一个参数是一个对象,那么我们可以从第二个参数开始遍历,把后面的对象扩展到第一个对象里面。
2、如果参数只有一个,并且是对象,那么就是给jQuery本身或者jQuery的实例对象fn进行扩展了。
代码实现:
jQuery.fn.extend = jQuery.extend = function () {
// console.log(arguments);
var target = arguments[0] || {}; // target是需要扩展的对象
var length = arguments.length;
var i = 1;
var option, name;
if (typeof target !== 'object') {
target = {};
}
// 如果参数的个数等于1时 要么给jQuery本身扩展方法,要么给jQuery的实例对象扩展方法
if (length === i) {
target = this; // this: Query本身 or jQuery的实例对象fn
i--;
}
for(; i < length; i++) {
if ((option = arguments[i]) !== null) {
for (name in option) {
target[name] = option[name];
}
}
}
return target; // 返回扩展的对象
};
以上代码简单地实现了extend的功能,但是这个只是浅拷贝,仅仅是浅拷贝是不够的,那么应该如何实现深拷贝呢?
我们调用extend时可以传入一个参数作为判断是否需要做深拷贝的操作:
var ret = { name: 'vincent', age: 18, list: { status: 'good'} };
var res = { list: { sex: '男' }};
// 第一个参数为true即要做深拷贝的操作
var obj = $.extend(true, {}, ret, res);
console.log(obj)
那么extend方法也要做相应的处理
var deep = false; // 定义一个deep来判断是深拷贝 or 浅拷贝
// 传入第一个参数如果为boolean,则是判断是否需要做深拷贝的操作
if (typeof target === 'boolean') {
deep = target; // 第一个参数为target
target = arguments[1]; // 第二个参数才是需要扩展的对象
i = 2; // i要加1
}
深拷贝的操作是只有对象或者数组才需要做的,所以我们可以给jQuery扩展判断类型的方法:
jQuery.extend({
// 类型检测
isObject: function(obj){
return toString.call(obj) === '[object Object]';
},
isArray: function(obj){
return toString.call(obj) === '[object Array]';
},
});
extend方法的完整实现如下:
jQuery.fn.extend = jQuery.extend = function () {
// console.log(arguments);
var target = arguments[0] || {}; // target是需要扩展的对象
var length = arguments.length;
var i = 1;
var deep = false; // 定义一个deep来判断是深拷贝 or 浅拷贝
var option, name, copy, src, copyIsArray, clone;
// 传入第一个参数如果为boolean,则是判断是否需要做深拷贝的操作
if (typeof target === 'boolean') {
deep = target; // 第一个参数为target
target = arguments[1]; // 第二个参数才是需要扩展的对象
i = 2; // i要加1
}
if (typeof target !== 'object') {
target = {};
}
// 如果参数的个数等于1时 要么给jQuery本身扩展方法,要么给jQuery的实例对象扩展方法
if (length === i) {
target = this; // this: Query本身 or jQuery的实例对象fn
i--;
}
// 浅拷贝 深拷贝
for (; i < length; i++) {
if ((option =arguments[i]) != null) { // i=1;对第一个对象扩展 第一个对象不要动;
for (name in option){
// target[name] = option[name];
// console.log(name);
copy = option[name]; // option 当前遍历对象的值
src = target[name]; // 第一个为 {} src为undefined
if (deep && ($.isObject(copy) || (copyIsArray = $.isArray(copy)))) {
// 深拷贝 copy需要是Object或者Array
if (copyIsArray){
copyIsArray = false;
clone = src && $.isArray(src) ? src : [];
} else {
clone = src && $.isObject(src) ? src : {};
}
// console.log(clone, copy);
target[name] = $.extend(deep, clone, copy); // 核心代码
} else if (copy != undefined) { // 浅拷贝
target[name] = copy;
}
}
}
}
return target; // 返回扩展的对象
};
核心代码是target[name] = $.extend(deep, clone, copy);这一句,jQuery的核心功能函数extend就实现啦。
四、完整代码
最后贴上这个实现的完整代码:
(function (window) {
var jQuery = function () {
// return new jQuery(); // 不合理的实例对象创建,会造成死循环
return new jQuery.prototype.init();
};
jQuery.fn = jQuery.prototype = {
init: function () {
},
// 可以扩展其他方法
css: function () {
}
};
// jQuery核心功能函数:extend,可以在外部或内部使用
// 细节:需要扩展的对象必须是Object
jQuery.fn.extend = jQuery.extend = function () {
// console.log(arguments);
var target = arguments[0] || {}; // target是需要扩展的对象
var length = arguments.length;
var i = 1;
var deep = false; // 定义一个deep来判断是深拷贝 or 浅拷贝
var option, name, copy, src, copyIsArray, clone;
// 传入第一个参数如果为boolean,则是判断是否需要做深拷贝的操作
if (typeof target === 'boolean') {
deep = target; // 第一个参数为target
target = arguments[1]; // 第二个参数才是需要扩展的对象
i = 2; // i要加1
}
if (typeof target !== 'object') {
target = {};
}
// 如果参数的个数等于1时 要么给jQuery本身扩展方法,要么给jQuery的实例对象扩展方法
if (length === i) {
target = this; // this: Query本身 or jQuery的实例对象fn
i--;
}
// 浅拷贝 深拷贝
for (; i < length; i++) {
if ((option =arguments[i]) != null) { // i=1;对第一个对象扩展 第一个对象不要动;
for (name in option){
// target[name] = option[name];
// console.log(name);
copy = option[name]; // option 当前遍历对象的值
src = target[name]; // 第一个为 {} src为undefined
if (deep && ($.isObject(copy) || (copyIsArray = $.isArray(copy)))) {
// 深拷贝 copy需要是Object或者Array
if (copyIsArray){
copyIsArray = false;
clone = src && $.isArray(src) ? src : [];
} else {
clone = src && $.isObject(src) ? src : {};
}
// console.log(clone, copy);
target[name] = $.extend(deep, clone, copy); // 核心代码
} else if (copy != undefined) { // 浅拷贝
target[name] = copy;
}
}
}
}
return target; // 返回扩展的对象
};
// 共享原型设计
// 调用$时,会去找到jQuery原型上的init方法,把init当做一个构造函数,然后返回init的实例对象
// jQuery原型上的init的构造函数跟jQuery本身共享一个原型
// jQuery.prototype.init.prototype = jQuery.prototype; // 共享原型对象
jQuery.fn.init.prototype = jQuery.fn;
// 给jQuery扩展
jQuery.extend({
// 类型检测
isObject: function(obj){
return toString.call(obj) === '[object Object]';
},
isArray: function(obj){
return toString.call(obj) === '[object Array]';
},
});
window.$ = window.jQuery = jQuery; // 要创建一个$实例
})(window);