JS 控件 jQuery扩展函数
概述:
jQuery 作为一个轻量级框架,插件格式的控件使用起来非常方便,但是我们基于jQuery写一套控件,需要我们扩展一些函数,便于控件的继承和扩展。
扩展函数:
1. augment(Function r,Object s1): 将指定的方法或属性放到构造函数的原型链上, 函数支持多于2个变量,后面的变量同s1一样将其成员复制到构造函数的原型链上。
2. cloneObject(Object obj): 拷贝对象(深拷贝)
jQuery 有一个clone方法,但是只支持HTMLElement,而我们在很多时候需要的是深拷贝对象。
3. guid(prefix): 生成唯一的id。
用于控件生成时,需要用id管理控件
4. extend(Function subclass,Function superclass,Object overrides): 实现类的继承
这个方法是继承的核心方法,在前面讲过,具体的细节查看前面的博客。
5. merge(Object obj1,Object obj2....): 将多个对象的属性复制到一个新的对象上,如果第一个参数是true,那么实现的是深拷贝。
6. mix():封装 jQuery.extend 方法,将多个对象的属性merge到第一个对象中。
7. mixin(Function c,Array mixins,Array attrs): 将其他类作为扩展,集成到指定的类上面。
8. substitue(String str,Object o,[RegExp regexp]) : 替换字符串中的字段,用于简单模板
上面是为了实现控件继承而增加的一些帮助方法,还有一些辅助类的方法,具体代码如下:
2 {
3 /**
4 * 将指定的方法或属性放到构造函数的原型链上,
5 * 函数支持多于2个变量,后面的变量同s1一样将其成员复制到构造函数的原型链上。
6 * @param {Function} r 构造函数
7 * @param {Object} s1 将s1 的成员复制到构造函数的原型链上
8 * @example
9 * BUI.augment(class1,{
10 * method1: function(){
11 *
12 * }
13 * });
14 */
15 augment : function(r,s1){
16 if(!$.isFunction(r))
17 {
18 return r;
19 }
20 for (var i = 1; i < arguments.length; i++) {
21 BUI.mix(r.prototype,arguments[i].prototype || arguments[i]);
22 };
23 return r;
24 },
25 /**
26 * 拷贝对象
27 * @param {Object} obj 要拷贝的对象
28 * @return {Object} 拷贝生成的对象
29 */
30 cloneObject : function(obj){
31 var result = $.isArray(obj) ? [] : {};
32
33 return BUI.mix(true,result,obj);
34 },
35 /**
36 * 抛出错误
37 */
38 error : function(msg){
39 throw msg;
40 },
41 /**
42 * 实现类的继承,通过父类生成子类
43 * @param {Function} subclass
44 * @param {Function} superclass 父类构造函数
45 * @param {Object} overrides 子类的属性或者方法
46 * @return {Function} 返回的子类构造函数
47 * 示例:
48 * @example
49 * //父类
50 * function base(){
51 *
52 * }
53 *
54 * function sub(){
55 *
56 * }
57 * //子类
58 * BUI.extend(sub,base,{
59 * method : function(){
60 *
61 * }
62 * });
63 *
64 * //或者
65 * var sub = BUI.extend(base,{});
66 */
67 extend : function(subclass,superclass,overrides, staticOverrides){
68 //如果只提供父类构造函数,则自动生成子类构造函数
69 if(!$.isFunction(superclass))
70 {
71
72 overrides = superclass;
73 superclass = subclass;
74 subclass = function(){};
75 }
76
77 var create = Object.create ?
78 function (proto, c) {
79 return Object.create(proto, {
80 constructor: {
81 value: c
82 }
83 });
84 } :
85 function (proto, c) {
86 function F() {
87 }
88
89 F.prototype = proto;
90
91 var o = new F();
92 o.constructor = c;
93 return o;
94 };
95 var superObj = create(superclass.prototype,subclass);//new superclass(),//实例化父类作为子类的prototype
96 //superObj1 = new superclass();//作为superclass属性
97 subclass.prototype = BUI.mix(superObj,subclass.prototype); //指定子类的prototype
98 //superObj1.constructor = superclass;
99 subclass.superclass = create(superclass.prototype,superclass);
100 //subclass.prototype.constructor = subclass;
101 BUI.mix(superObj,overrides);
102 BUI.mix(subclass,staticOverrides);
103 return subclass;
104 },
105 /**
106 * 生成唯一的Id
107 * @method
108 * @param {String} prefix 前缀
109 * @default 'ks-guid'
110 * @return {String} 唯一的编号
111 */
112 guid : (function(){
113 var map = {};
114 return function(prefix){
115 prefix = prefix || BUI.prefix + GUID_DEFAULT;
116 if(!map[prefix]){
117 map[prefix] = 1;
118 }else{
119 map[prefix] += 1;
120 }
121 return prefix + map[prefix];
122 };
123 })(),
124 /**
125 * 判断是否是字符串
126 * @return {Boolean} 是否是字符串
127 */
128 isString : function(value){
129 return typeof value === 'string';
130 },
131 /**
132 * 判断是否数字,由于$.isNumberic方法会把 '123'认为数字
133 * @return {Boolean} 是否数字
134 */
135 isNumber : function(value){
136 return typeof value === 'number';
137 },
138 /**
139 * 控制台输出日志
140 * @param {Object} obj 输出的数据
141 */
142 log : function(obj){
143 if(win.console && win.console.log){
144 win.console.log(obj);
145 }
146 },
147 /**
148 * 将多个对象的属性复制到一个新的对象
149 */
150 merge : function(){
151 var args = $.makeArray(arguments);
152 args.unshift({});
153 return $.extend.apply(null,args);
154
155 },
156 /**
157 * 封装 jQuery.extend 方法,将多个对象的属性merge到第一个对象中
158 * @return {Object}
159 */
160 mix : function(){
161 return $.extend.apply(null,arguments);
162 },
163 /**
164 * 创造顶层的命名空间,附加到window对象上,
165 * 包含namespace方法
166 */
167 app : function(name){
168 if(!window[name]){
169 window[name] = {
170 namespace :function(nsName){
171 return BUI.namespace(nsName,window[name]);
172 }
173 };
174 }
175 return window[name];
176 },
177 /**
178 * 将其他类作为mixin集成到指定类上面
179 * @param {Function} c 构造函数
180 * @param {Array} mixins 扩展类
181 * @param {Array} attrs 扩展的静态属性,默认为['ATTRS']
182 * @return {Function} 传入的构造函数
183 */
184 mixin : function(c,mixins,attrs){
185 attrs = attrs || [ATTRS];
186 var extensions = mixins;
187 if (extensions) {
188 c.mixins = extensions;
189
190 var desc = {
191 // ATTRS:
192 // HTML_PARSER:
193 }, constructors = extensions['concat'](c);
194
195 // [ex1,ex2],扩展类后面的优先,ex2 定义的覆盖 ex1 定义的
196 // 主类最优先
197 $.each(constructors, function (index,ext) {
198 if (ext) {
199 // 合并 ATTRS/HTML_PARSER 到主类
200 $.each(attrs, function (i,K) {
201 if (ext[K]) {
202 desc[K] = desc[K] || {};
203 // 不覆盖主类上的定义,因为继承层次上扩展类比主类层次高
204 // 但是值是对象的话会深度合并
205 // 注意:最好值是简单对象,自定义 new 出来的对象就会有问题(用 function return 出来)!
206 BUI.mix(true,desc[K], ext[K]);
207 }
208 });
209 }
210 });
211
212 $.each(desc, function (k, v) {
213 c[k] = v;
214 });
215
216 var prototype = {};
217
218 // 主类最优先
219 $.each(constructors, function (index,ext) {
220 if (ext) {
221 var proto = ext.prototype;
222 // 合并功能代码到主类,不覆盖
223 for (var p in proto) {
224 // 不覆盖主类,但是主类的父类还是覆盖吧
225 if (proto.hasOwnProperty(p)) {
226 prototype[p] = proto[p];
227 }
228 }
229 }
230 });
231
232 $.each(prototype, function (k,v) {
233 c.prototype[k] = v;
234 });
235 }
236 return c;
237 },
238 /**
239 * 生成命名空间
240 * @param {String} name 命名空间的名称
241 * @param {Object} baseNS 在已有的命名空间上创建命名空间,默认“BUI”
242 * @return {Object} 返回的命名空间对象
243 * @example
244 * BUI.namespace("Grid"); // BUI.Grid
245 */
246 namespace : function(name,baseNS){
247 baseNS = baseNS || BUI;
248 if(!name){
249 return baseNS;
250 }
251 var list = name.split('.'),
252 //firstNS = win[list[0]],
253 curNS = baseNS;
254
255 for (var i = 0; i < list.length; i++) {
256 var nsName = list[i];
257 if(!curNS[nsName]){
258 curNS[nsName] = {};
259 }
260 curNS = curNS[nsName];
261 };
262 return curNS;
263 },
264 prefix : 'ks-',
265 /**
266 * 替换字符串中的字段.
267 * @param {String} str 模版字符串
268 * @param {Object} o json data
269 * @param {RegExp} [regexp] 匹配字符串的正则表达式
270 */
271 substitute: function (str, o, regexp) {
272 if (!BUI.isString(str)
273 || !$.isPlainObject(o)) {
274 return str;
275 }
276
277 return str.replace(regexp || /\\?\{([^{}]+)\}/g, function (match, name) {
278 if (match.charAt(0) === '\\') {
279 return match.slice(1);
280 }
281 return (o[name] === undefined) ? '' : o[name];
282 });
283 },
284 /**
285 * 使第一个字母变成大写
286 * @param {String} s 字符串
287 * @return {String} 首字母大写后的字符串
288 */
289 ucfirst : function(s){
290 s += '';
291 return s.charAt(0).toUpperCase() + s.substring(1);
292 },
293 /**
294 * 页面上的一点是否在用户的视图内
295 * @param {Object} offset 坐标,left,top
296 * @return {Boolean} 是否在视图内
297 */
298 isInView : function(offset){
299 var left = offset.left,
300 top = offset.top,
301 viewWidth = BUI.viewportWidth(),
302 wiewHeight = BUI.viewportHeight(),
303 scrollTop = BUI.scrollTop(),
304 scrollLeft = BUI.scrollLeft();
305 //判断横坐标
306 if(left < scrollLeft ||left > scrollLeft + viewWidth){
307 return false;
308 }
309 //判断纵坐标
310 if(top < scrollTop || top > scrollTop + wiewHeight){
311 return false;
312 }
313 return true;
314 },
315 /**
316 * 页面上的一点纵向坐标是否在用户的视图内
317 * @param {Object} top 纵坐标
318 * @return {Boolean} 是否在视图内
319 */
320 isInVerticalView : function(top){
321 var wiewHeight = BUI.viewportHeight(),
322 scrollTop = BUI.scrollTop();
323
324 //判断纵坐标
325 if(top < scrollTop || top > scrollTop + wiewHeight){
326 return false;
327 }
328 return true;
329 },
330 /**
331 * 页面上的一点横向坐标是否在用户的视图内
332 * @param {Object} left 横坐标
333 * @return {Boolean} 是否在视图内
334 */
335 isInHorizontalView : function(left){
336 var viewWidth = BUI.viewportWidth(),
337 scrollLeft = BUI.scrollLeft();
338 //判断横坐标
339 if(left < scrollLeft ||left > scrollLeft + viewWidth){
340 return false;
341 }
342 return true;
343 },
344 /**
345 * 获取窗口可视范围宽度
346 * @return {Number} 可视区宽度
347 */
348 viewportWidth : function(){
349 return $(window).width();
350 },
351 /**
352 * 获取窗口可视范围高度
353 * @return {Number} 可视区高度
354 */
355 viewportHeight:function(){
356 return $(window).height();
357 },
358 /**
359 * 滚动到窗口的left位置
360 */
361 scrollLeft : function(){
362 return $(window).scrollLeft();
363 },
364 /**
365 * 滚动到横向位置
366 */
367 scrollTop : function(){
368 return $(window).scrollTop();
369 },
370 /**
371 * 窗口宽度
372 * @return {Number} 窗口宽度
373 */
374 docWidth : function(){
375 var body = document.documentElement || document.body;
376 return $(body).width();
377 },
378 /**
379 * 窗口高度
380 * @return {Number} 窗口高度
381 */
382 docHeight : function(){
383 var body = document.documentElement || document.body;
384 return $(body).height();
385 }
386
387 });