本篇继续丰富我的jquery框架,利用extend来扩展!
扩展的优点
不必每次增加新的方法都直接往jQuery或者jQuery.fn追加.
jQuery.fn.extend({
fun1: function() {
console.log('this is an extend function');
}
})
通过这样的方法就能为jQuery扩展一个名叫fun1()的方法.然后能直接调用它
$('div').fun1(); //this is an extend function
extend()方法的基本思想就是把指定对象的方法都复制给jQuery或者jQuery.prototype对象!
jQuery.extend = jQuery.fn.extend = function( obj ) {
for ( var name in obj ) {
this[ name ] = obj[ name ];
}
return this;
}
知识拓展
jQuery中的extend()方法远比上面几行代码复杂,强大!下面是官网提供的API.
jQuery.extend( target, [ object1 ], [ objectN ] );
//example
var object1 = {
apple: 0,
banana: {weight: 52, price: 100},
cherry: 97
};
var object2 = {
banana: {price: 200},
durian: 100
};
jQuery.extend( object1, object2 );
//result
var object1 = {
apple: 0,
banana: {weight: 52, price: 100},
durian: 100,
cherry: 97
}
target:属性合并后的集合.
object[1...n]:待合并的属性,若target没有则直接加入,否则重写(覆盖).
1.若有新属性合并进来,则破坏了target原来的结构.
jQuery.extend( { }, target, [ object1 ], [ objectN ] ); //合并后的结果将输入到{ }中.
//example
var defaults = { validate: false, limit: 5, name: "foo" };
var options = { validate: true, name: "bar" };
var merge = jQuery.extend( { }, defaults, options );
//result
/*
var defaults = { validate: false, limit: 5, name: "foo" };
var options = { validate: true, name: "bar" };
var merge = {"validate":true,"limit":5,"name":"bar"};
*/
2. 若extend()接受的参数是唯一的.则合并到jQuery的环境中去.
jQuery.extend( target );
//test
jQuery.extend({
test: function() {
console.log( 'I'm an example' );
}
})
jQuery.test(); //I'm an example
带选项的深度extend()方法
jQuery.extend( [ deep ], target, object1, [ objectN ] );
deep(boolean): 若取值true,将会递归的合并(深度copy).
其它的参数含义如上.
何为深度copy?
//example
var object1 = {
apple: 0,
banana: {weight: 52, price: 100},
cherry: 97
};
var object2 = {
banana: {price: 200},
durian: 100
};
jQuery.extend( true, object1, object2 );
//result
/*
var object1 = {
apple: 0,
banana: {weight: 52, price: 200},
cherry: 97,
durian: 100
}
*/
深度copy:为每个子对象递归调用extend()方法!
源码解析
jQuery.extend = jQuery.fn.extend = function() {
/*
* options: 复制操作的对象及object[1...n]
* name: object[1...n]里面的每项
* src: 目标对象
* copy: 复制的项目,也是递归copy的操作对象
* copyIsArray: copy的对象是数组
* clone: 需要递归copy的目标对象
* */
var options, name, src, copy, copyIsArray, clone,
//复制操作的目标对象
target = arguments[0] || {},
i = 1,
length = arguments.length,
//是否进行深度copy
deep = false;
//若传递的第一个参数是boolean(false,true),则处理是否深度copy
if ( typeof target === "boolean" ) {
deep = target;
//第二个参数变成copy的目标对象
target = arguments[1] || {};
//掠过boolean值
i = 2;
}
// 若目标对象为字符串则置空对象target
if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
target = {};
}
//唯一参数的时候,目标对象是当前环境this
if ( length === i ) {
target = this;
--i;
}
//处理需要进行copy的对象
for ( ; i < length; i++ ) {
//只对非空对象进行处理
if ( (options = arguments[ i ]) != null ) {
for ( name in options ) {
//相当于hash查找target中是否有name这一项,没有则加入,有则重写
src = target[ name ];
copy = options[ name ];
//阻止死循环发生
if ( target === copy ) {
continue;
}
//若copy是一个对象或者是一个数组,则进行递归的copy
if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
if ( copyIsArray ) {
copyIsArray = false;
//相当于上面的target = {}; 定义进行递归copy的目标对象
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;
};
发现的问题
源码中有一点值得注意的:
// 若目标对象为字符串则置空对象target
if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
target = {};
}
其实不能只单纯的理解为目标对象为字符串!分析源码不难发现.这里的条件是:
target不是"object",并且target也不是"function "!怎么说呢?如下:
var object1 = {
name: 'Poised-flw',
age: 20
}
//Number
var test1 = jQuery.extend( 123, object1 ); //Object {name: "Poised-flw", age: 20}
//String
var test2 = jQuery.extend( 'Poised-flw', object1 ); //Object {name: "Poised-flw", age: 20}
//Array
var test3 = jQuery.extend( [ 1, 3, 4 ], object1 ); //Object {name: "Poised-flw", age: 20}
//...more
//以上的用法其实就相当于
var test = jQuery.extend( { }, object1 );
话说还有个更奇妙的地方.当typeof target是"function"的时候会产生什么样的结果?
var object1 = {
name: 'Poised-flw',
age: 20,
test: 'haha'
}
var fun = function() {
console.log('I'm a function');
}
var result = jQuery.extend( fun, object1 );
/*something interesting*/
typeof result; //"function"
result(); //'I'm a function'
result['name']; //""
result['age']; //20
result.test; //"haha"
函数还能这么用,这玩意不多见...井底之蛙了...@^@
不过还有个问题呀...^_^
result.name; //"" 为什么不是"Poised-flw"
result['name']; //"" 为什么不是"Poised-flw"
以下是解释:
function.name
name属性返回一个function的名字,当function是一个匿名函数的时候返回一个空串(Chrome & Opera & Firefox & Safari),IE不支持此属性(undefined)!
//example 1
function doSomething() {
console.log(doSomething.name); //expcept IE: "doSomething", but for IE: undefined
}
//example 2
var fun = function() {
console.log(fun.name); //except IE: "", and for ie: undefined
}
注意:name属性是只读属性,并不允许修改!就像length属性一样.
//the name property
var fun = function() {
//...
}
//the same with length
console.log(fun.name); //""
fun.name = "Poised-flw"; //"Poised-flw"
console.log(fun.name); //""
//the length property
console.log( (function() {}).length ); //0
console.log( (function(a, b) {}).length ); //2
//for firefox. function.length does not include the "rest parameter"
/*
About rest parameter:
以...开始的参数,代表一个数组.范围是从0到数组的长度
function(a, b, ...Args) { //... }
*/
console.log( (function(a, ...Args) {}).length ); //1
//notice! arguments.length将返回实际传入函数的参数个数
/*
* function f( a, b, c, d ) {
* console.log( f.length );
* console.log( arguments.length );
* }
* f( 1, 2, 3 ); //4, 3
*/
到此自己的jquery框架也算具备可扩展性了!接下来就是丰富它!