在getId方法中之所以直接使用this就能访问到构造函数Employee,是因为getId这个方法是添加到构造函数上的,所以当调用Employee.getId()时,getId方法里面的this指向的就是Employee这个函数对象。
第二版在第一版的基础上,实现继承关系的构建部分:
- Hide code var Class = (function () { var hasOwn = Object.prototype.hasOwnProperty; //用来判断是否为Object的实例 function isObject(o) { return typeof (o) === 'object'; } //用来判断是否为Function的实例 function isFunction(f) { return typeof (f) === 'function'; } //简单复制 function copy(source) { var target = {}; for (var i in source) { if (hasOwn.call(source, i)) { target[i] = source[i]; } } return target; } function ClassBuilder(options) { if (!isObject(options)) { throw new Error('Class options must be an valid object instance!'); } var instanceMembers = isObject(options) && options.instanceMembers || {}, staticMembers = isObject(options) && options.staticMembers || {}, extend = isObject(options) && isFunction(options.extend) && options.extend, prop; //表示要构建的类的构造函数 function TargetClass() { if (extend) { //如果有要继承的父类 //就在每个实例中添加baseProto属性,以便实例内部可以通过这个属性访问到父类的原型 //因为copy函数导致原型链断裂,无法通过原型链访问到父类的原型 this.baseProto = extend.prototype; } if (isFunction(this.init)) { this.init.apply(this, arguments); } } //添加静态成员,这段代码需在原型设置的前面执行,避免staticMembers中包含prototype属性,覆盖类的原型 for (prop in staticMembers) { if (hasOwn.call(staticMembers, prop)) { TargetClass[prop] = staticMembers[prop]; } } //如果有要继承的父类,先把父类的实例方法都复制过来 extend && (TargetClass.prototype = copy(extend.prototype)); //添加实例方法 for (prop in instanceMembers) { if (hasOwn.call(instanceMembers, prop)) { TargetClass.prototype[prop] = instanceMembers[prop]; } } TargetClass.prototype.constructor = TargetClass; return TargetClass; } return ClassBuilder })();
这一版关键的部分在于:
this.baseProto主要目的就是为了让子类的实例能够有一个属性可以访问到父类的原型,因为后面的继承方式是复制方式,会导致原型链断裂。有了这一版之后,就可以加入Manager类来演示效果了:
- Hide code var Employee = Class({ instanceMembers: { init: function (name, salary) { this.name = name; this.salary = salary; //调用静态方法 this.id = Employee.getId(); }, getName: function () { return this.name; }, getSalary: function () { return this.salary; }, toString: function () { return this.name + '\'s salary is ' + this.getSalary() + '.'; } }, staticMembers: { idCounter: 1, getId: function () { return this.idCounter++; } } }); var Manager = Class({ instanceMembers: { init: function (name, salary, percentage) { //借用父类的init方法,实现属性继承(name, salary) Employee.prototype.init.apply(this, [name, salary]); this.percentage = percentage; }, getSalary: function () { return this.salary + this.salary * this.percentage; } }, extend: Employee }); var e = new Employee('jason', 5000); var m = new Manager('tom', 8000, 0.15); console.log(e.toString()); //jason's salary is 5000. console.log(m.toString()); //tom's salary is 9200. console.log(e.constructor === Employee); //true console.log(m.constructor === Manager); //true console.log(e.id); //1 console.log(m.id); //2
不过在Manager内部,调用父类的方法时还是apply借用的方式,所以在最后一版里面,需要把它变成我们期望的this.base的方式,反正原理前面也已经了解了,无非是在方法同名的时候,对实例方法加一个代理而已,实现如下:
- Hide code var Class = (function () { var hasOwn = Object.prototype.hasOwnProperty; //用来判断是否为Object的实例 function isObject(o) { return typeof (o) === 'object'; } //用来判断是否为Function的实例 function isFunction(f) { return typeof (f) === 'function'; } //简单复制 function copy(source) { var target = {}; for (var i in source) { if (hasOwn.call(source, i)) { target[i] = source[i]; } } return target; } function ClassBuilder(options) { if (!isObject(options)) { throw new Error('Class options must be an valid object instance!'); } var instanceMembers = isObject(options) && options.instanceMembers || {}, staticMembers = isObject(options) && options.staticMembers || {}, extend = isObject(options) && isFunction(options.extend) && options.extend, prop; //表示要构建的类的构造函数 function TargetClass() { if (extend) { //如果有要继承的父类 //就在每个实例中添加baseProto属性,以便实例内部可以通过这个属性访问到父类的原型 //因为copy函数导致原型链断裂,无法通过原型链访问到父类的原型 this.baseProto = extend.prototype; } if (isFunction(this.init)) { this.init.apply(this, arguments); } } //添加静态成员,这段代码需在原型设置的前面执行,避免staticMembers中包含prototype属性,覆盖类的原型 for (prop in staticMembers) { if (hasOwn.call(staticMembers, prop)) { TargetClass[prop] = staticMembers[prop]; } } //如果有要继承的父类,先把父类的实例方法都复制过来 extend && (TargetClass.prototype = copy(extend.prototype)); //添加实例方法 for (prop in instanceMembers) { if (hasOwn.call(instanceMembers, prop)) { //如果有要继承的父类,且在父类的原型上存在当前实例方法同名的方法 if (extend && isFunction(instanceMembers[prop]) && isFunction(extend.prototype[prop])) { TargetClass.prototype[prop] = (function (name, func) { return function () { //记录实例原有的this.base的值 var old = this.base; //将实例的this.base指向父类的原型的同名方法 this.base = this.baseProto[name]; //调用子类自身定义的实例方法,也就是func参数传递进来的函数 var ret = func.apply(this, arguments); //还原实例原有的this.base的值 this.base = old; return ret; } })(prop, instanceMembers[prop]); } else { TargetClass.prototype[prop] = instanceMembers[prop]; } } } TargetClass.prototype.constructor = TargetClass; return TargetClass; } return ClassBuilder })();
核心部分是: