插件写法(基于继承)
/** * @author: xiangliang.zeng * @description: * @Date: 2016/12/12 16:57 * @Last Modified by: xiangliang.zeng * @Last Modified time: 2016/12/12 16:57 */ (function(factory) { var root = (typeof self == "object" && self.self == self && self) || (typeof global == "object" && global.global == global && global); if (typeof define == "function" && define.amd) { define(['exports'], function(exports) { root.Example == factory(root, exports); }); } else if (typeof exports !== 'undefined') { factory(root, exports); } else { root.Example = factory(root, {}); } })(function(root, Example) { //initializing用于控制类的初始化,非常巧妙,请留意下文中使用技巧 //fnTest返回一个正则比表达式,用于检测函数中是否含有_super,这样就可以按需重写,提高效率。当然浏览器如果不支持的话就返回一个通用正则表达式 var initializing = false, fnTest = /xyz/.test(function() {xyz;}) ? /\b_super\b/ : /.*/; //所有类的基类Class,这里的this一般是window对象 Example.Class = function() {}; //对基类添加extend方法,用于从基类继承 Example.Class.extend = function(prop) { //保存当前类的原型 var _super = this.prototype; //创建当前类的对象,用于赋值给子类的prototype,这里非常巧妙的使用父类实例作为子类的原型,而且避免了父类的初始化(通过闭包作用域的initializing控制) initializing = true; var prototype = new this(); initializing = false; //将参数prop中赋值到prototype中,这里的prop中一般是包括init函数和其他函数的对象 for (var name in prop) { //对应重名函数,需要特殊处理,处理后可以在子函数中使用this._super()调用父类同名构造函数, 这里的fnTest很巧妙:只有子类中含有_super字样时才处理从写以提高效率 prototype[name] = typeof prop[name] == "function" && typeof _super[name] == "function" && fnTest.test(prop[name]) ? (function(name, fn) { return function() { //_super在这里是我们的关键字,需要暂时存储一下 var tmp = this._super; //这里就可以通过this._super调用父类的构造函数了 this._super = _super[name]; //调用子类函数 fn.apply(this, arguments); //复原_super,如果tmp为空就不需要复原了 tmp && (this._super = tmp); } })(name, prop[name]) : prop[name]; } //当new一个对象时,实际上是调用该类原型上的init方法,注意通过new调用时传递的参数必须和init函数的参数一一对应 function Class() { if (!initializing && this.init) { this.init.apply(this, arguments); } } //给子类设置原型 Class.prototype = prototype; //给子类设置构造函数 Class.prototype.constructor = Class; //设置子类的extend方法,使得子类也可以通过extend方法被继承 Class.extend = arguments.callee; return Class; } Example.Test1 = Example.Class.extend({ init: function() { this.name = 'lily'; this.age = 19; }, sayName: function() { console.log('name======' + this.name); }, sayAge: function() { console.log('age=====' + this.age); } }); Example.Test2 = Example.Test1.extend({ init: function() { this.same = 'face'; this._super(); // 继承了Test1的属性 }, saySame: function() { console.log('saySame====' + this.same); } }) return Example; }); var test1 = new Example.Test1(); test1.sayName(); var test2 = new Example.Test2(); test2.sayAge(); console.log(test1); console.log(test2);
写法二(个人感觉好一点):
/** * @author: xiangliang.zeng * @description: * @Date: 2016/12/12 16:57 * @Last Modified by: xiangliang.zeng * @Last Modified time: 2016/12/12 16:57 */ (function(factory) { var root = (typeof self == "object" && self.self == self && self) || (typeof global == "object" && global.global == global && global); if (typeof define == "function" && define.amd) { define(['exports'], function(exports) { root.Example == factory(root, exports); }); } else if (typeof exports !== 'undefined') { factory(root, exports); } else { root.Example = factory(root, {}); } })(function(root, Example) { //initializing用于控制类的初始化,非常巧妙,请留意下文中使用技巧 //fnTest返回一个正则比表达式,用于检测函数中是否含有_super,这样就可以按需重写,提高效率。当然浏览器如果不支持的话就返回一个通用正则表达式 var initializing = false, fnTest = /xyz/.test(function() {xyz;}) ? /\b_super\b/ : /.*/; //所有类的基类Class,这里的this一般是window对象 Example.Class = function() {}; //对基类添加extend方法,用于从基类继承 Example.Class.extend = function(prop) { //保存当前类的原型 var _super = this.prototype; //创建当前类的对象,用于赋值给子类的prototype,这里非常巧妙的使用父类实例作为子类的原型,而且避免了父类的初始化(通过闭包作用域的initializing控制) initializing = true; var prototype = new this(); initializing = false; //将参数prop中赋值到prototype中,这里的prop中一般是包括init函数和其他函数的对象 for (var name in prop) { //对应重名函数,需要特殊处理,处理后可以在子函数中使用this._super()调用父类同名构造函数, 这里的fnTest很巧妙:只有子类中含有_super字样时才处理从写以提高效率 prototype[name] = typeof prop[name] == "function" && typeof _super[name] == "function" && fnTest.test(prop[name]) ? (function(name, fn) { return function() { //_super在这里是我们的关键字,需要暂时存储一下 var tmp = this._super; //这里就可以通过this._super调用父类的构造函数了 this._super = _super[name]; //调用子类函数 fn.apply(this, arguments); //复原_super,如果tmp为空就不需要复原了 tmp && (this._super = tmp); } })(name, prop[name]) : prop[name]; } //当new一个对象时,实际上是调用该类原型上的init方法,注意通过new调用时传递的参数必须和init函数的参数一一对应 function Class() { if (!initializing && this.init) { this.init.apply(this, arguments); } } //给子类设置原型 Class.prototype = prototype; //给子类设置构造函数 Class.prototype.constructor = Class; //设置子类的extend方法,使得子类也可以通过extend方法被继承 Class.extend = arguments.callee; return Class; } return Example; }); Example.Test1 = Example.Class.extend({ init: function() { this.name = 'lily'; this.age = 19; }, sayName: function() { console.log('name======' + this.name); }, sayAge: function() { console.log('age=====' + this.age); } }); Example.Test2 = Example.Test1.extend({ init: function() { this.same = 'face'; this._super(); }, saySame: function() { console.log('saySame====' + this.same); } }) var test1 = new Example.Test1(); test1.sayName(); var test2 = new Example.Test2(); test2.sayAge(); console.log(test1); console.log(test2);
优化之后:
/** * @author: xiangliang.zeng * @description: * @Date: 2016/12/12 16:57 * @Last Modified by: xiangliang.zeng * @Last Modified time: 2016/12/12 16:57 */ (function(factory) { "use strict"; var root = (typeof self === "object" && self.self === self && self) || (typeof global === "object" && global.global === global && global); if (typeof define === "function" && define.amd) { define(['exports'], function(exports) { root.Example === factory(root, exports); }); } else if (typeof exports !== 'undefined') { factory(root, exports); } else { root.Example = factory(root, {}); } })(function(root, Example) { //initializing用于控制类的初始化,非常巧妙,请留意下文中使用技巧 //fnTest返回一个正则比表达式,用于检测函数中是否含有_super,这样就可以按需重写,提高效率。当然浏览器如果不支持的话就返回一个通用正则表达式 var initializing = false, fnTest = /xyz/.test(function() {return xyz;}) ? /\b_super\b/ : /.*/; //所有类的基类Class,这里的this一般是window对象 Example.Class = function() {}; //对基类添加extend方法,用于从基类继承 Example.Class.extend = function(prop) { //保存当前类的原型 var _super = this.prototype; //创建当前类的对象,用于赋值给子类的prototype,这里非常巧妙的使用父类实例作为子类的原型,而且避免了父类的初始化(通过闭包作用域的initializing控制) initializing = true; var prototype = new this(); initializing = false; // - 则重新定义此方法 function fn(name, fun) { return function() { // 将实例方法_super保护起来。 // 个人觉得这个地方没有必要,因为每次调用这样的函数时都会对this._super重新定义。 var tmp = this._super; // 在执行子类的实例方法name时,添加另外一个实例方法_super,此方法指向父类的同名方法 this._super = _super[name]; // 执行子类的方法name,注意在方法体内this._super可以调用父类的同名方法 var ret = fun.apply(this, arguments); this._super = tmp; // 返回执行结果 return ret; }; } //将参数prop中赋值到prototype中,这里的prop中一般是包括init函数和其他函数的对象 for (var name in prop) { if (prop.hasOwnProperty(name)) { //对应重名函数,需要特殊处理,处理后可以在子函数中使用this._super()调用父类同名构造函数, 这里的fnTest很巧妙:只有子类中含有_super字样时才处理从写以提高效率 prototype[name] = typeof prop[name] === "function" && typeof _super[name] === "function" && fnTest.test(prop[name]) ? fn(name, prop[name]) : prop[name]; } } //当new一个对象时,实际上是调用该类原型上的init方法,注意通过new调用时传递的参数必须和init函数的参数一一对应 function Class() { if (!initializing && this.init) { this.init.apply(this, arguments); } } //给子类设置原型 Class.prototype = prototype; //给子类设置构造函数 Class.prototype.constructor = Class; //设置子类的extend方法,使得子类也可以通过extend方法被继承 Class.extend = arguments.callee; return Class; }; return Example; }); Example.Test1 = Example.Class.extend({ init: function() { this.name = 'lily'; this.age = 19; }, sayName: function() { console.log('name======' + this.name); }, sayAge: function() { console.log('age=====' + this.age); } }); Example.Test2 = Example.Test1.extend({ init: function() { this.same = 'face'; this._super(); }, saySame: function() { console.log('saySame====' + this.same); } }); var test1 = new Example.Test1(); test1.sayName(); var test2 = new Example.Test2(); test2.sayAge(); console.log(test1); console.log(test2);