此篇由别的大神的博客与《javascript高级程序设计》一书整理而来
原博客地址:https://hyj1254.iteye.com/blog/628555
看到javascript高级程序设计的面向对象章节看到超类与子类这个概念词,不懂上度娘查了才知道是怎么一回事。
说到超类与子类,就不得不提起原型模式,原型对象,原型链与原型链继承了
在原型模式章节中,对于原型模式这样描述:“我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。如果按照字面意思来理解,那么prototype 就是通过调用构造函数而创建的那个对象实例的原型对象”
这一段话表明了:prototype由构造函数Object()创建,所以它本身也是一个Object实例,而任何Object实例都可以从Object.prototype中继承属性;于是prototype就也具有了这种能力;
而对于原型对象,书中描述如下:
“无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性包含一个指向prototype 属性所在函数的指针”
这段话 表明了:函数的prototype属性指向函数的原型对象,而原型对象又会指回创建的函数
(prototype属性以及constructor属性都是浏览器内部生成,外部不可直接访问)
鉴于此种特性,我们将另一个类型的实例赋值给一个函数的原型对象(fun1.prototype=new fun2()),这样原本存在于该实例的属性以及方法也存在于原型对象中了,这样层层递进就构成了原型链。
另一个很重要的信息:基于prototype的继承不仅仅局限于单一的prototype对象,访问沿着一条prototype链逐级向上执行:假设有个Complex实例,访问其中一属性,如果本身找不到,便访问Complex.prototype对象;还找不到即在Object实例中找不到,就接着访问Complex.prototype的上一级--Object.prototype。
这就解释了为什么JS对象都从Object对象中继承了方法:如自定义了Complex类,然后它的每个实例就都能访问toString()方法,因为它的prototype是Object实例,能访问Object.prototype。
这就是所谓“超类和子类”的核心。拿上例来说,它所表述的是,由于Complex.prototype默认是Object的实例(由Object()初始化),于是Complex便继承了Object(即可以访问Object和Object.prototype的所有属性)。
好戏登场了:只要把Complex.prototype的构造函数换成其他的,而不是默认的Object(),那Complex便成为了那个类的子类;这就实现了自己定义的超类和子类关系。当然Complex还是Object的子类,但那是因为那个类最终也是Object的子类。
举个栗子:
1 function Person(name){this.name=name;} 2 Person.prototype.say=function(){alert("hello");}; 3 function Man(){} 4 Man.prototype=new Person("Tom"); 5 6 var man=new Man(); 7 alert(man.name);//Tom,继承了Person实例的属性:name 8 man.say();//hello,继承了Person.prototype的属性:say 9 alert(man.hasOwnProperty('name'));//false,name在Man.prototype中
在上面的栗子中,Man是Person的子类
当然,这样写虽然能达到效果,但可能不符合“标准”,那就换成标准的:
1 function Person(name){this.name=name;} 2 Person.prototype.say=function(){alert(this.hasOwnProperty('name'));} 3 function Man(name){ 4 Person.call(this,name);//多了这一步,实现实例属性的继承,要先明白call的作用 } 5 Man.prototype=new Person();//继承Person.prototype的属性。 6 //-------------- 7 var man = new Man("Tom"); 8 alert(man.constructor==Person);//true 9 Man.prototype.constructor=Man;//将Man的构造函数指回Man 10 alert(man.name);//Tom 11 alert(man.hasOwnProperty('name'));//true,通过call方法,name已经成为man的实例属性。hasOwnProperty判断man本身是否具有name属性。 12 man.say();//true,继承了Person.prototype的属性,this指向man 13 alert(Man.prototype.constructor==Person);//false 14 alert(Man.prototype instanceof Person);//true
总结以上就是:Man是谁的子类就看Man.prototype是由谁的构造函数初始化的。只要把握了这点,继承关系可以随心所欲的构造。