jQuery无new构建实例
无new化构建
在jQuery中 $符号就是jQuery的别称
$()就是创建了jQuery的实例对象
实现
(function(root){
function jQuery() {
return new jQuery.prototype.init();
};
// 在jQuery的原型上定义init方法
jQuery.prototype = {
init: function() {
},
css: function() {
// 在jQuery原型上扩展的属性也会被共享到init的原型上
}
}
// 把jQuery的原型共享给init的原型
jQuery.prototype.init.prototype = jQuery.prototype;
// 设置全局变量$和jQuery
root.$ = root.jQuery = jQuery;
})(this); // 把宿主对象window传进
共享原型设计
描述
如上, 在实现jQuery实例化的时候,我们直接调用$();
console.log($());
因为$()指向的是jQuery函数,所以相当于调用了jQuery函数;
// 设置全局变量$和jQuery;
// $和window.jQuery指向的都是jQuery函数
root.$ = root.jQuery = jQuery;
jQuery作为构造函数直接被调用效果和普通函数呗调用一样,
所以无法生成有效实例;
解决方案:
只能把jQuery函数的返回值设成构造函数的实例;
function jQuery() {
return new jQuery();
};
但是这样做是不行的,很明显会造成死循环
所以jQuery采用的共享原型的设计模式, 即;
-
在jQuery函数的prototype上定义了init方法
// 在jQuery的原型上定义init方法 jQuery.prototype = { init: function() { } }
-
把jQuery的原型共享给init
// 把jQuery的原型共享给init的原型 jQuery.prototype.init.prototype = jQuery.prototype;
-
通过$()调用jQuery函数时返回一个init的实例
function jQuery() { return new jQuery.prototype.init(); };
-
给jQuery的prototype扩展属性的时候,由于原型共享,
所以init的原型上也会具有该扩展属性
jQuery.prototype = { init: function() { }, css: function() { // 在jQuery原型上扩展的属性也会被共享到init的原型上 } } console.log($());
以上就是jQuery无new实例化的实现
原型扩展设计见下图:
extend函数源码解析
extend函数用法:
// 给任意对象扩展
var obj1 = { a:1, b:2 };
var obj2 = { c: 3 };
var res = $.extend(obj1, obj2);
console.log(res); // ===> { a:1, b:2, c: 3 }
console.log(obj1); // ===> { a:1, b:2, c: 3 }
// 给$(jQuery)进行扩展
$.extend({
myMethod: function() {
console.log('myMethod1');
}
})
$.myMethod(); // myMethod1;
// 给$(jQuery)的实例进行扩展
$.fn.extend({
myMethod: function() {
console.log('myMethod2');
}
})
$().myMethod(); // myMethod2;
extend函数用于给对象进行扩展,给jQuery提供了插件机制
- 可以给任意对象扩展
- 也可以给jQuery自身扩展
注:
// $.fn指向的就是$.prototype
jQuery.fn = jQuery.prototype = {
// ......
}
- extend再jQuery中之所以既能通过$.extend调用,
又能通过$().extend调用是因为在源码内部中实现了
jQuery.fn.extend = jQuery.extend = function() { // ...... }
extend实现与分析
// extend
jQuery.fn.extend = jQuery.extend = function() {
var target = arguments[0] || {}; // target赋值为第一个参数,也就是要扩展的对象
var length = arguments.length; // 获取参数的个数
var i = 1;
var deep = false;
var options, name, copy, src, copyIsArray, clone;
if(typeof target === 'boolean') { // 当传入第一个参数是boolean时
deep = target; // 把参数deep的值设置为target,即传入的第一个参数
target = arguments[1]; // 把target(需扩展的对象设置为第二个参数)
i = 2; // i = 2,以便之后从第三个参数开始遍历
};
if(typeof target !== 'object') { // 验证传入的参数为obj
target = {};
};
if(length === i) { // 如果只有一个参数,则extend方法为jQuery内部的扩展
target = this; // 把target的引用设置为this,指向$或者$()
i--;
};
// 浅拷贝
for( ;i < length; i++) { // boolean参数和所需要扩展的对象的属性无需遍历
if((options = arguments[i]) !==null) {
for(name in options) {
copy = options[name];
src = target[name];
// 如果需要深拷贝,并且options的属性对应的是对象或数组时
if(deep && (jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)))) {
if(copyIsArray) {
copyIsArray = false; //copyIsArray不重置会影响下一次循环的判断;
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.extend({
isPlainObject: function(obj) { // 判断传入参数的数据类型是否是obj
return toString.call(obj) === '[object Object]'
},
isArray: function(arr) { // 判断传入参数的数据类型是否是数组
return toString.call(arr) === '[object Array]'
}
});