先引一段原文,可略过..
.....Object is the superclass of all the built-in classes, and all classes inherit a few basic methods from Object. Recall that objects inherit properties from the prototype object of their constructor. How do they also inherit properties from the Object class? Remember that the prototype object is itself an object; it is created with the Object( ) constructor. This means the prototype object itself inherits properties from Object.prototype! Prototype-based inheritance is not limited to a single prototype object; instead, a chain of prototype objects is involved. Thus, a Complex object inherits properties from Complex.prototype and from Object.prototype. When you look up a property in a Complex object, the object itself is searched first. If the property is not found, the Complex.prototype object is searched next. Finally, if the property is not found in that object, the Object.prototype object is searched.
这段话前半部分描述了一个重要信息:prototype由构造函数Object()创建,所以它本身也是一个Object实例,而任何Object实例都可以从Object.prototype中继承属性;于是prototype就也具有了这种能力。
这就引出了另一个重要的信息:基于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的子类。
举例说明(以Person和Man为例):
function Person(name){this.name=name;} Person.prototype.say=function(){alert("hello");} function Man(){} Man.prototype=new Person("Tom"); //------------ var man = new Man(); alert(man.name);//Tom,继承了Person实例的属性:name man.say();//hello,继承了Person.prototype的属性:say alert(man.hasOwnProperty('name'));//false,name在Man.prototype中
完工了,Man已经成为了Person的子类。
当然,这样写虽然能达到效果,但可能不符合“标准”,那就换成标准的:
function Person(name){this.name=name;} Person.prototype.say=function(){alert(this.hasOwnProperty('name'));} function Man(name){ Person.call(this,name);//多了这一步,实现实例属性的继承,要先明白call的作用 } Man.prototype=new Person();//继承Person.prototype的属性。 //-------------- var man = new Man("Tom"); alert(man.constructor==Person);//true Man.prototype.constructor=Man;//将Man的构造函数指回Man alert(man.name);//Tom alert(man.hasOwnProperty('name'));//true,通过call方法,name已经成为man的实例属性。hasOwnProperty判断man本身是否具有name属性。 man.say();//true,继承了Person.prototype的属性,this指向man alert(Man.prototype.constructor==Person);//false alert(Man.prototype instanceof Person);//true
这就是“标准”的做法。当然也可以突发奇想,让Man只继承Person.prototype的属性,而不包括Person的实例属性。
function Person(name){this.name=name;} Person.prototype.say=function(){alert('hello');} function Man(){} Man.prototype=Person.prototype; var man = new Man(); alert(man.name);//undefined,没有继承Person实例的属性 man.say();//hello,继承了Person.prototype的属性:say alert(Man.prototype.constructor==Object);//true alert(Man.prototype instanceof Object);//true
总之,Man是谁的子类就看Man.prototype是由谁的构造函数初始化的。只要把握了这点,继承关系可以随心所欲的构造。
但注意,试图通过把子类的prototype的constructor设为父类的构造函数是不行的。因为prototype并没有被父类的构造函数初始化,它还是由Object()初始化,只是它的constructor属性值改为父类了。 当子类实例访问"父类"prototype内的同名方法时,并不会到"父类"里查找,而是查找Object及Object.prototype。
Man.prototype.constructor=Person;//这样达不到继承的目的
另外说说call的作用(apply也一样):
function Man(name){Person.call(this,name);}
调用Person的构造函数Person,call的第一个参数是绑定Person函数的临时对象,后面的参数是要传给Person的。此例中this即Man实例绑定了Person函数,并调用它。然后person方法内的this指向的就不再是Person实例了,而是Man实例,最终效果就好像它是Man的构造函数一样:所有Man实例就都有了Person实例的属性。