首先贴出 DC 的 Object.create(),这是理解 js 创建对象的关键。如下:
if (!Object.create) { /** * @ref https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/create */ Object.create = function (o) { if (arguments.length > 1) { throw new Error('Object.create implementation only accepts the first parameter.'); } function F() { } F.prototype = o; return new F(); }; }然后,贴出我写的 $$.define/ $$.mix。如下:
/** * 定义一个类并让它继承于某个父类。 * @param {Function/[]/Object} fatherClass 父类函数引用 * @param {Function} classBody 子类函数体 * @param {Object} args 构造器参数,当前仅单参数 */ $$.define = function (fatherClass, classBody, args) { if (fatherClass instanceof Array) { return $$.mix.apply(this, arguments); } // 如果从 Object 继承 则分配一个虚拟的父类。 classBody.prototype = fatherClass === Object ? function () { this.constructor = function () { } } : fatherClass.prototype; var _pro = args ? new classBody(args) : new classBody; if (_pro.constructor) { _pro.constructor.prototype = _pro; return _pro.constructor; } else { function F() { } // 不提供构造器,设置一个空的。 F.prototype = _pro; return F; } } /** * 混入 * 参见: * 《JS OO继承、多态一法》 http://blog.csdn.net/zhangxin09/article/details/6226279 * 《透视Ext JS 4类背后的机制与特点(上)》http://blog.csdn.net/zhangxin09/article/details/6197408 * @param {Function []} _Class 父类 * @param {Object} sub 子类 * @param {Array} args 可选的参数列表 */ $$.mix = function (fatherClasses, classBody, args) { var _pro = args ? new classBody(args) : new classBody; _pro.constructor.prototype = _pro; for (var _fatherClass, _newSubClass, i = 0, j = fatherClasses.length; i < j; i++) { fatherClasses[i].prototype.constructor.call(_pro); } return _pro.constructor; }
主要理解两点:
- prototype = new Function,必须以“对象/函数”二义性的写法写出对象 prototype;
- 构造器同一闭包下的写法。做法就是 new 了 prototype 之后,用其 constructor:Function 作构造器并赋予“类”返回。
举一个例子如下,某组件的写法:
$$.Component = function(){ } /** * @param {Object} defaultCfg * @return {Object} */ $$.Component.loadCfg = function (defaultCfg, instanceCfg){ instanceCfg = instanceCfg || new Object; // 新建一个配置容器 for(var i in defaultCfg){ if(!instanceCfg[i]){ instanceCfg[i] = defaultCfg[i]; // 读取默认值 } } return instanceCfg; } $$.Component.prototype = new function(){ var getEl = document.getElementById; this.getEl = $$.Component.getEl = function(cfg){ var el; if(cfg instanceof HTMLElement){ el = cfg; }else if(typeof cfg == 'string'){ el = getEl(cfg); }else if(cfg.el || cfg.container){ return arguments.callee(cfg.el || cfg.container); } if(!el){ console.dir(cfg); throw '没有指定的元素!'; } return el; } } /** * @class $$.dhtml.ExpandBox * 可折叠的一个容器。结构如下: * <container> * <p> * </p> * <mask></mask> * </container> */ $$.dhtml.ExpandBox = $$.define($$.Component, function(){ this.constructor = function(el){ this.container = el.container; // p 即 contetnt 区域是必须的 // mask 是可选的,视乎你在 HTML 有否安排 mask tag this.mask = this.container.querySelector('.x-contMask'); this.el = el; this.btn = el.toogleBtn; if(!this.btn){ throw '找不到按钮!'; } this.init(); } this.showMoreText = "显示更多"; this.closeText = '收 起'; this.init = function (){ this.p = this.container.querySelector('p'); if(!this.p){ throw '正文内容必须具备,请设置好元素!'; } this.colHeight = this.p.clientHeight; /* 此处的 80 是与 css 设置的一样 */ if(!this.colHeight){ throw '不能获取原始高度'; } this.btn.onClk(toggleHandler.bind(this)); this.el.toggleHandler && this.btn.onClk(this.el.toggleHandler.bind(this)); } var fxFn = $$.dhtml.Fx.slide; var dTime = 150; function toggleHandler(e){ var btn = e.currentTarget; if( btn.innerHTML == this.closeText){ // fxFn(this.p, 'height', this.p.scrollHeight, this.colHeight, dTime); move(this.p, this.p.scrollHeight, this.colHeight); btn.innerHTML = this.showMoreText; this.mask && this.mask.addClass('x-contMask_bg'); this.currentState = 'closed'; }else{ // fxFn(this.p, 'height', this.colHeight, this.p.scrollHeight, dTime); move(this.p, this.colHeight, this.p.scrollHeight); btn.innerHTML = this.closeText; this.currentState = 'opened'; this.mask && this.mask.removeClass('x-contMask_bg'); } } /** * 缓动公式 * @param {Element} el * @param {Number} s 初始值 * @param {Number} v 目标值 */ function move(el, s, v){ var style = el.style; function fx(){ // debugger; // console.log(v); /*用来保存上一次的左标*/ var old = style.height || s || 0; var x = window.parseInt(old); style.height = x + 0.800 * (v - x) + 'px'; /*如果和上一次左边的坐标相等则清除定时器*/ if(style.height === old)window.clearInterval(si); } var si = window.setInterval(fx, 20); } });