本篇继续丰富我的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框架也算具备可扩展性了!接下来就是丰富它!