上面的代码中我曾有“走火入魔”的说法,并不是对作者的亵渎, 只是觉得作者对JavaScript中的一个重要准则(通过自执行的匿名函数创建作用域) 运用的有点过头。
value = (function(m) { return function() { return ancestor[m].apply(this, arguments) }; })(property).wrap(method);
其实这段代码和下面的效果一样:
value = ancestor[property].wrap(method);
我们把wrap函数展开就能看的更清楚了:
value = (function(fn, wrapper) { var __method = fn; return function() { return wrapper.apply(this, [__method.bind(this)].concat($A(arguments))); } })(ancestor[property], method);
可以看到,我们其实为父类的函数ancestor[property]通过自执行的匿名函数创建了作用域。 而原作者是为property创建的作用域。两则的最终效果是一致的。
我们对Prototypejs继承的重实现
分析了这么多,其实也不是很难,就那么多概念,大不了换种表现形式。
下面我们就用前几章我们自己实现的jClass来实现Prototypejs形式的继承。
// 注意:这是我们自己实现的类似Prototypejs继承方式的代码,可以直接拷贝下来使用 // 这个方法是借用Prototypejs中的定义 function argumentNames(fn) { var names = fn.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1].replace(/\s+/g, '').split(','); return names.length == 1 && !names[0] ? [] : names; } function jClass(baseClass, prop) { // 只接受一个参数的情况 - jClass(prop) if (typeof (baseClass) === "object") { prop = baseClass; baseClass = null; } // 本次调用所创建的类(构造函数) function F() { // 如果父类存在,则实例对象的baseprototype指向父类的原型 // 这就提供了在实例对象中调用父类方法的途径 if (baseClass) { this.baseprototype = baseClass.prototype; } this.initialize.apply(this, arguments); } // 如果此类需要从其它类扩展 if (baseClass) { var middleClass = function() {}; middleClass.prototype = baseClass.prototype; F.prototype = new middleClass(); F.prototype.constructor = F; } // 覆盖父类的同名函数 for (var name in prop) { if (prop.hasOwnProperty(name)) { // 如果此类继承自父类baseClass并且父类原型中存在同名函数name if (baseClass && typeof (prop[name]) === "function" && argumentNames(prop[name])[0] === "$super") { // 重定义子类的原型方法prop[name] // - 这里面有很多JavaScript方面的技巧,如果阅读有困难的话,可以参阅我前面关于JavaScript Tips and Tricks的系列文章 // - 比如$super封装了父类方法的调用,但是调用时的上下文指针要指向当前子类的实例对象 // - 将$super作为方法调用的第一个参数 F.prototype[name] = (function(name, fn) { return function() { var that = this; $super = function() { return baseClass.prototype[name].apply(that, arguments); }; return fn.apply(this, Array.prototype.concat.apply($super, arguments)); }; })(name, prop[name]); } else { F.prototype[name] = prop[name]; } } } return F; };
调用方式和Prototypejs的调用方式保持一致:
var Person = jClass({ initialize: function(name) { this.name = name; }, getName: function() { return this.name; } }); var Employee = jClass(Person, { initialize: function($super, name, employeeID) { $super(name); this.employeeID = employeeID; }, getEmployeeID: function() { return this.employeeID; }, getName: function($super) { return "Employee name: " + $super(); } }); var zhang = new Employee("ZhangSan", "1234"); console.log(zhang.getName()); // "Employee name: ZhangSan"
经过本章的学习,就更加坚定了我们的信心,像Prototypejs形式的继承我们也能够轻松搞定。
以后的几个章节,我们会逐步分析mootools,Extjs等JavaScript类库中继承的实现,敬请期待。
您可能感兴趣的文章: