想要学好一种语言,一定要了解其语言特性,因为我们 not just coding,js本身就是一种函数式的编程语言,它不与java或者其他面向对象一样拥有继承机制,但是我们可以利用其留下的prototype库进行曲线救国,这就不能不找一段经典的继承代码来研究一下了。以下是John Resig实现的经典继承实现代码,
// 自执行的匿名函数创建一个上下文,避免引入全局变量 (function() { // initializing变量用来标示当前是否处于类的创建阶段, var initializing = false, fnTest = /xyz/.test(function() { xyz; }) ? /\b_super\b/ : /.*/; // 基类构造函数 // 这里的this是window,所以这整段代码就向外界开辟了一扇窗户 - window.Class this.Class = function() { }; // 继承方法定义 Class.extend = function(prop) { var _super = this.prototype; // 通过将子类的原型指向父类的一个实例对象来完成继承 // - 注意:this是基类构造函数(即是Class) initializing = true; var prototype = new this(); initializing = false; for (var name in prop) { prototype[name] = typeof prop[name] == "function" && typeof _super[name] == "function" ? (function(name, fn) { return function() { var tmp = this._super; this._super = _super[name]; var ret = fn.apply(this, arguments); return ret; }; })(name, prop[name]) : prop[name]; } function Class() { // 在类的实例化时,调用原型方法init if (!initializing && this.init) this.init.apply(this, arguments); } // 子类的prototype指向父类的实例(完成继承的关键) Class.prototype = prototype; // 修正constructor指向错误 Class.constructor = Class; // 子类自动获取extend方法,arguments.callee指向当前正在执行的函数 Class.extend = arguments.callee; return Class; }; })(); var Person = Class.extend({ name:null, init:function(){ alert("init person"); }, alerting:function(){ alert("Person"); } }) var Student = Person.extend({ init:function(){ this._super(); alert("init"); }, alerting:function(){ this._super(); alert("Student"); } }) var p = new Student(); p.alerting();
if (!initializing && this.init) this.init.apply(this, arguments);
是否执行接下来声明一个prototype的变量,指向父类的一个实例,prototype = new this(),这句代码其实是将prototype的__proto__属性指向了父类的原型,接下来将initializing这个变量=false,是为了志明自执行方法中的return的Class这个类在后期实例化时进入if条件,例如Person就可以。相信大家都注意到了for循环中的代码,这就是继承机制的核心,看这个条件语句 typeof prop[name] == "function" && typeof _super[name] == "function" ,是说明如果prop对象中的name属性是一个function并且父类中存在同名的属性并且同样是function,那么执行下面这段代码:
(function(name, fn) { return function() { var tmp = this._super; this._super = _super[name]; var ret = fn.apply(this, arguments); return ret; }; })(name, prop[name])
这段代码是什么意思呢,这里面其实做了两件事情,首先将父类的同名方法赋给了this绑定的_super属性,然后就是将子类的方法绑定到this作用域执行,我们回过头来看看,这段代码是赋给了prototype[name],也就是子类的该同名方法,该方法必定是由定义该方法类的实例调用,因此this作用域指向了目标类的实例,此时,this作用域中还有一个_super属性,该属性此时需要注意的是该属性不是不变的,举例来说,var p = new Student()这个实例,当实例p调用init()函数时,this._super指向了Person的init(),当p调用alerting()时,this._super指向了Person的alerting(),当我们调用p.alerting()的时候,执行this._super()其实就是调用父类的同名方法,若typeof prop[name] == "function" && typeof _super[name] == "function"不为true的时候,那么prototype[name] = proto[name],这其中还有一点点不好理解,如上代码我改动一点点,将Person类中的alerting方法变成一个属性,那么这个条件也不成立,就执行prototype[‘alerting’]=proto['alerting'],这就有问题了,之前我们在prototype=new this()的时候,prototype的__proto__属性指向了父类的prototype,说明其中必定有父类中的alerting属性,这下prototype又有了一个alerting属性,那么我们的对象p在调用p.alerting()的时候调用的到底是alerting方法还是alerting属性呢,其实还是alerting()方法,因为首先对象p会在第一个__proto__属性中找alerting()方法,若找不到,则在下一层中寻找,若找到了该方法,就不会进入下一层的__proto__属性中继续寻找了,所以这里不会找到从父类继承下来的alerting属性,调用的只是自己的方法。到此为止,其实只是实现了将父类的方法属性继承到了我们自定义的局部变量prototype属性中,后面我们定义的Class方法,该方法不同于之前的Class方法,
function Class() { // 在类的实例化时,调用原型方法init if (!initializing && this.init) this.init.apply(this, arguments); }
我们将Class的prototype指向了我们定义的prototype变量,其实就是继承了父类的方法和属性,此时Class.constructor指向了prototype变量的构造函数,所以我们显式地改回来,Class.constructor=Class,并且使用Class.extend=arguments.callee将Class.extend方法指向了外层的Class.extend方法,方便我们进行深度扩展,最后将Class返回。
这个意思就是无论是Person或者是Student类,本质上都是Class.extend方法内部定义的Class方法(也就是类),只是我们将父类(外层的Class,也就是调用extend方法的类)和子类(也就是prop对象)的属性揉到了内部的Class类中,最后将Class返回给我们外部,就实现了继承了。
以上就是该继承机制的详解,不知道讲清楚了没有,若有疑问或者有不对之处,敬请指正。