jQuery.extend扩展利器
来源:互联网 发布:知乎 东风 编辑:程序博客网 时间:2024/05/29 18:42
js面向对象
js是一种弱语言,对变量类型的校验是由变量的值确定。js对面向对象的封装特性由函数来实现,它充当编译语言中的class,当然在es6.0以上就开始使用class来定义类。那么js中的function所构造出的obj对象,任然可以动态扩展。这种不确定性、动态扩展性和封装性是js灵活、面向对象的体现。
var var1 = 10;console.log(typeof var1); // "number"var1 = true;console.log(typeof var1); // "boolean"var1 = {};console.log(typeof var1); // "object"var1 = function(){};console.log(typeof var1); // "function"//封装一个类function myClass(n, a) { this.name = n; this.age = a;};//每个函数都带了一个原型prototype,下面这种会覆盖了prototypeMyClass.prototype = { constructor: MyClass, //修正myClass.constructor的指向,防止this.constructor()构造出错 getName: function() { return this.name; }, getAge: function() { return this.age; } ... ... }//我们都知道编译语言中class有静态方法,那么js中class的静态方法呢?myClass.getClassName = function() { return "myClass";};console.log(myClass.getClassName()); // "myClass"//此外,我们都知道,静态方法是class所有,对象不应该具有访问权限。var myObj = new MyClass("vzou", 18);console.log(myObj.getClassName()); // Uncaught TypeError: myObj.getClassName is not a functionconsole.log(myObj.getName()); // "vzou"//同理,有MyClass不具有myObj的方法的访问权限console.log(myClass.getName()); //Uncaught TypeError: myClass.getName is not a function
动态扩展
在java、C#和C++等编译语言中,一个类创建后便不能进行扩展(当然除了子类扩展、接口扩展和反射扩展),但js可以动态的进行扩展。js中给class扩展包括对象属性扩展和类属性扩展,分别对应与function.prototype和function。此外,class对象实例obj还可以扩展本obj专属属性。
//如何给MyClass扩展对象方法?通过添加extend方法,来将aruments参数扩展到prototype中//1、将简单obj扩展到MyClass.prototype上,简单obj是指obj的属性的值不是object MyClass.prototype.extend = function(obj) { //处理下obj,如果传进来的不是object时,直接return null; if(typeof obj !== "object") return null; //foreach可以遍历object对象属性, this指代当前对象,即this = MyClass.prototype,将obj中的所有属性都扩展到this中 for(var k in obj) this[k] = obj[k]; }; MyClass.prototype.extend({ sayHaha: function() { alert("haha"); }, sayHehe: function() { alert("hehe"); } }); var obj1 = new MyClass(); obj1.sayHaha(); //haha//工具方法,合并对象MyClass.extend(arg1, arg2, ....) /** *根据传入参数个数,来决定是扩展MyClass还是扩展第一个参数 */ MyClass.extend = function() { //i从1开始, target表示被合并的对象arguments[0] var i = 1, length = arguments.length, target = arguments[0] || {}, //name属性名,options=arguments[i], src=target[name], copyt=options[name] name, options, copy; //如果传入的参数个数为1,那么表示扩展this if(i == arguments.length) { target = this; } //这里要区分出arguments[i]中的属性是object还是简单属性(string,boolean,number,function) for(; i < arguments.length; i++) { options = arguments[i]; //当扩展对象是null或undefined时,直接过滤掉 if(!options) continue; for(var name in options) { copy = options[name]; //当扩展对象的属性为undefined时,直接过滤掉,为什么这么做呢? //因为copy为undefined意味着options中未定义该属性。比如var obj = {}, obj["name"]的值为undefined if(copy !== undefined) { target[name] = copy; } } } //将扩展后的对象返回 return target; };
上面是将一个简单object对象扩展到MyClass对象属性。复杂的object对象:
var obj = { person: { name: "v_vzou", age: 19, getName: function() { return this.name; } otherObj:{... ...} } }
怎么做?方案1:要是this[k]也有一个extend就可以解决。但是这种想法太烂;方案2:有个工具可以将this[k]和obj[k]进行合并,这不就是MyClass.extend的功能吗?将MyClass.prototype.extend和MyClass.extend结合使用来扩展复杂对象。
//2、将复杂obj扩展到MyClass.prototype MyClass.prototype.extend = function(obj) { //标识obj的属性是否为object var isObj = false; if(typeof obj !== "object") return false; for(var k in obj) { isObj = typeof obj[k] === "object"; if(!isObj) { this[k] = obj[k]; } else { //怎么做? //方案1:要是this[k]也有一个extend就可以解决。但是这种想法太烂 //方案2:有个工具可以将this[k]和obj[k]进行合并,这不就是extend的功能吗? MyClass.extend(this[k], obj[k]); } } };//如果MyClass.prototype中已有一个属性person,它是一个object MyClass.prototype.person = { grade: 2, hometown: "jiangxi" };//给MyClass.prototype扩展复杂对象MyClass.prototype.extend({ sayHello: function() { alert(this.person.hometown); }, person: { hometown: "fuzhou" }}); obj = new MyClass();obj.sayHello(); //fuzhou
简单对象和复杂对象通过extend进行扩展是否可以控制,对于extend方法中进行参数判断便可区分。
//用一个开关来控制复制对象的方式MyClass.extend = MyClass.prototype.extend = function() { //i从1开始, target表示被合并的对象arguments[0] var i = 1, length = arguments.length, target = arguments[0] || {}, //name属性名,options=arguments[i], src=target[name], copy=options[name], deep=false name, options, src, copy, deep = false; //如果第一个参数为深度复制开关,那么将target右移 if(typeof target === "boolean") { deep = target; target = arguments[1]; //此时target需要扩展是下标为2的参数,也就是跳过boolean、 i = 2; } //这里处理递归MyClass.extend(deep, target[name], copy)中target[name]是非object对象时问题 //extend(false, {...})、extend(true, 1, {...})、extend(false, "string", {...}) //obj为string、boolean、number时都不能添加属性。当参数不是object时,则人为的将target改变成object if(typeof target !== "object" && !isFunction(target)) { target = {}; } //如果传入的参数个数为1,那么表示扩展this if(i == arguments.length) { target = this; //此时需要减1,为啥呢? //如果i == 1,那么此时extend({...}),length == 1,此时要扩展this,那么下边for循环从arguments第一个元素开始 //如果i == 2,那么此时extend(true, {...}), length == 2, 此时要扩展this,那么下边for循环从arguments第二个元素开始 i--; } //这里要区分出arguments[i]中的属性是object还是简单属性(string,boolean,number,function) for(; i < arguments.length; i++) { options = arguments[i]; //当扩展对象是null或undefined时,直接过滤掉 if(!options) continue; for(var name in options) { copy = options[name]; src = target[name]; //如果是deep为true,表明需要进行复杂对象扩展,此时也要判断copy和src是否为object //为什么只判断copy呢?因为如果target为非object,此时全覆盖方式target[name]=copy; ////if条件成立:deep=true, isNotNullOrEmpty(copy)和isPlainObj(copy) if(deep && copy && isPlainObj(copy)) { //这里为什么要接收返回值?这里extend是三个参数,说明是将copy扩展到target[name]上 //如果target[name]不是object,此时在extend里面必须处理,否则报错 target[name] = MyClass.extend(deep, src, copy); } else if(copy !== undefined) { //当扩展对象的属性为undefined时,直接过滤掉,为什么这么做呢? //因为copy为undefined意味着options中未定义该属性。比如var obj = {}, obj["name"]的值为undefined target[name] = copy; } } } //将扩展后的对象返回 return target; } //判断一个对象是否为Object构造对象,如{},new Object(),而 new MyClass()则不是 function isPlainObj(obj) { //typeof obj为"object"时,obj可能会哪几种情况: //1、{}和new Object(); 2、new 函数; 3、DOM对象(Document、HTMLElement); 4、Window。当为1时,返回true,其他的返回false; //非object、obj.nodeType(1~12)说明为HTMLElement和window都不是plain Object if(typeof obj !== "object" || obj.nodeType || isWindow(obj)) return false; //new 函数也不是plain Object,此时obj肯定有contructor属性 //函数对象必有constructor构造器和prototype原型,但是有constructor和prototype是否就一定是函数对象呢? if(obj.contsructor) return false; return true; } //判断obj是否为window对象 function isWindow(obj) { //剔除null异常,window.window这个属性成为判断window对象标识 return obj != null && obj === obj.window; } //判断传入对象是否为函数 function isFunction(obj) { return type(obj) === "function"; } //判断obj是否为数组 function isArray(obj) { if(Array.isArray) return Array.isArray(obj); return type(obj) == "array"; } //返回对象obj类型字符, undefined == null 为true function type(obj) { //这里用String包装obj是因为在IE8以下中, //Object.prototype.toString.call(null)返回"[object Object]" //Object.prototype.toString.call(undefined)返回"[object Object]" return obj == null ? String(obj) : class2type[Object.prototype.toString.call(obj)]; } //先将class的toString返回结果建立一个Map,然后有toString检索出该class的简写type class2type = { //这里绑定Undefined、Null没什么意义,在type函数中检测不完全 //"[object Undefined]": "undefined", //"[object Null]": "null", "[object Boolean]": "boolean", "[object Number]": "number", "[object String]": "string", "[object Function]": "function", "[object Array]": "array", "[object Date]": "date", "[object RegExp]": "regexp", "[object Object]": "object" };
js数组
数组是一种特殊的对象,通过int类型下标进行值访问,typeof [] === “object”。js中数组是线性可变长度、类型不定的有序列表,区别于编译语言java、c++和C#等数组是定长、类型一直的有限列表。那么对于数组这种特殊的对象,需要在extend进行特殊处理。
var arr = []; //arr = new Array() 或 Array()//往数组中添加元素,此时数组是一个容器arr[0] = 0; arr[1] = 1; ... ...//往数组中添加属性arr.name = "NumberArray";arr.countOne = function() { return this[0]; };for(var name in arr) console.log(name);//0、1、...、name、countOne
extend
extend可以扩展框架本身,也可以作为对象扩展工具,但它内部实现包含诸多的规则,其中个关于数组Array和Plain Object就是jQuery中特殊处理的对象。
//特殊处理数组对象,当为深度扩展时,extend(true, obj1, obj2, ...)MyClass.extend = MyClass.prototype.extend = function() { var i = 1, length = arguments.length, target = arguments[0] || {}, //copyIsArray用于标识target[name]是否为数组,clone用于src是否为object的判断结果值 name, options, src, copy, deep = false, copyIsArray = false, clone; if(typeof target === "boolean") { deep = target; target = arguments[1]; i = 2; } if(typeof target !== "object" && !isFunction(target)) { target = {}; } if(i == arguments.length) { target = this; i--; } for(; i < arguments.length; i++) { options = arguments[i]; if(!options) continue; for(var name in options) { copy = options[name]; src = target[name]; //if条件成立:deep=true, isNotNullOrEmpty(copy)和isPlainObj(copy) //数组对象也可以拥有复杂属性,此时isPlainObj返回false;所以必须检测obj是否为数组 if(deep && copy && (isPlainObj(copy)) || //copyIsArray放到这里进行赋值好处是懒解析,因为只有deep && copy为true、isPlainObj为false才会解析copyIsArray (copyIsArray = isArray(copy))) { //isPlainObj、isArray这两者要区分开来,所以有if...else... if(copyIsArray) { copyIsArray = false; //置回初始状态 //这样为了避免copy是数组,而src不是,就是只能扩展json格式 clone = src && isArray(src) ? src : {}; } else { //这里杜绝src为非object,而是string、boolean、number,Function构造来的object、HTMLElement、window、RegExp、Date //这里是为了避免copy是plain Object,而src不是,就是只能扩展json格式 clone = src && isPlainObj(src) ? src : {}; } target[name] = MyClass.extend(deep, src, copy); } else if(copy !== undefined) { target[name] = copy; } } } return target;}
this引用
对象是引用类型,对象可以具有属性,而js中属性可以是基本类型、引用类型,具体类型是在解析器中进行判断。js动态扩展固然带来灵活性,但是也带来了无限循环的潜在性。
//简单对象person刚开始定义了两个属性name、agevar person = { name: "vzou", age: 18};//... ... 中间穿插了很多的逻辑代码;然后动态添加了属性me,此时指向本身person.me = person; //或者this//那么MyClass.extend(true, person, person.me),则会造成无限循环。这也是在编译语言中可以智能检测到的循环引用问题。//处理自身属性引用自身,然后进行扩展自身的无限循环问题,为了避免循环引用,则在extend中进行剔除MyClass.extend = MyClass.prototype.extend = function() { var i = 1, length = arguments.length, target = arguments[0] || {}, //copyIsArray用于标识target[name]是否为数组,clone用于src是否为object的判断结果值 name, options, src, copy, deep = false, copyIsArray = false, clone; if(typeof target === "boolean") { deep = target; target = arguments[1]; i = 2; } if(typeof target !== "object" && !isFunction(target)) { target = {}; } if(i == arguments.length) { target = this; i--; } for(; i < arguments.length; i++) { options = arguments[i]; if(!options) continue; for(var name in options) { copy = options[name]; src = target[name]; if(copy === target) continue; if(deep && copy && (isPlainObj(copy)) || (copyIsArray = isArray(copy))) { if(copyIsArray) { copyIsArray = false; clone = src && isArray(src) ? src : {}; } else { clone = src && isPlainObj(src) ? src : {}; } target[name] = MyClass.extend(deep, src, copy); } else if(copy !== undefined) { target[name] = copy; } } } return target;}
结论
jQuery.extend = jQuery.fn.extend扩展函数可以给jQuery框架扩展功能,也可以扩展某个对象,这给jQuery框架带来了巨大的灵活性。那么extend函数内部的扩展过程,其实根据js动态扩展特性而来。翻看源码带来的感受是,plain Object、函数对象、HTMLElement、Window和数组等js对象都分类进行处理,深度扩展不只是简单的对象属性的覆盖,而是有plain object和Array对象进行区分扩展。
- jQuery.extend扩展利器
- jQuery extend 扩展方法
- jquery extend jquery插件扩展
- jquery扩展:$.extend()和$.fn.extend()
- JQuery的extend扩展方法
- JQuery的extend扩展详解
- JQuery的extend扩展方法
- JQuery的extend扩展方法
- jQuery 的 extend 扩展方法
- JQuery的extend扩展方法
- jQuery扩展-jquery.fn.extend与jquery.extend
- jQuery.extend()、jQuery.fn.extend()扩展方法详解
- jQuery.extend()、jQuery.fn.extend()扩展方法详解
- jquery扩展之jQuery.fn.extend
- jQuery.extend( object ); 扩展jQuery对象本身
- 【jquery扩展】jquery扩展方法extend(),以及扩展插件
- jquery源码分析之扩展函数 extend, $.extend
- jQuery扩展插件方法 $.extend()和$.fn.extend()
- GNU汇编伪指令操作
- 机器人的运动范围
- Android混淆注意事项
- JAVA中,如在构造函数中定义数组的大小
- ORACLE重建控制文件
- jQuery.extend扩展利器
- Tomcat 或者 Nginx 配置 Https
- VS2015套接字编程时error C4996错误处理
- Android layout系列-autolayout
- UVa 11581
- C#阶乘递归算法
- 毕业两年
- wait 、notify 、join、yield
- 探秘MATLAB求FFT,计算能量谱