利用prototype实现JavaScript继承

时间:2021-11-03 17:56:20

本文主要是讲解如何通过prototype实现JavaScript继承,并逐步循序渐进,最终实现一个继承的通用函数。

1.首先我们定义一个父类Father,如下所示:

function Father(){
this.name = "姓名";
this.age = "20";
this.setName = function(str){this.name=str};
this.getName = function(){return this.name;};
}
Father.prototype.setAge = function(age){this.age = age;};
Father.prototype.getAge = function(){return this.age;};
console.log(new Father());
运行结果如下:
Father
  1. age"20"
  2. getNamefunction (){return this.name;}
  3. name"姓名"
  4. setNamefunction (str){this.name=str}
  5. __proto__Father
    1. constructorfunction Father(){
    2. getAgefunction (){return this.age;}
    3. setAgefunction (age){this.age = age;}
    4. __proto__Object

可以看到Father具有age、name两个属性以及getName、setName、getAge、setAge四个方法,但是要注意的是setAge、getAge方法是放在Father.prototype中的,prototype中的方法是公共方法,也就是说在创建新的实例时,prototype中的内容不会随着新实例的创建而占用更多的内存,所以公共方法最好放到prototype中。而实例中的setName与getName方法是在实例中的,每当新实例创建时,每个新实例都会额外占用内存来存储这两个方法。


2.使用call(context,para1...)尝试实现继承

代码如下:

function Sun1(){Father.call(this);}console.log(new Sun1())

运行结果如下:

Sun1
  1. age"20"
  2. getNamefunction (){return this.name;}
  3. name"姓名"
  4. setNamefunction (str){this.name=str}
  5. __proto__Sun1
    1. constructorfunction Sun1(){
    2. __proto__Object
由上可知,在子类的构造函数中通过调用Father.call(this)只能使得子类具有父类构造器中写明的属性与方法,但是不能继承父类的prototype中的内容getAge与setAge。


3.尝试使用apply(context,arguments)尝试实现继承

代码如下:

function Sun2(){Father.apply(this,arguments);}console.log(new Sun2());

运行结果如下:

Sun2
  1. age"20"
  2. getNamefunction (){return this.name;}
  3. name"姓名"
  4. setNamefunction (str){this.name=str}
  5. __proto__Sun2
    1. constructorfunction Sun2(){
    2. __proto__Object
由上可知,在子类的构造函数中通过调用Father.apply(this,arguments)也只能使得子类具有父类构造器中写明的属性与方法,但是不能继承父类的prototype中的内容getAge与setAge。


4.通过更改prototype实现继承

代码如下:

function Sun3(){}Sun3.prototype = new Father();Sun3.prototype.constructor = Sun3;console.log(new Sun3());

运行结果如下:

Sun3
  1. __proto__Father
    1. age"20"
    2. constructorfunction Sun3(){}
    3. getNamefunction (){return this.name;}
    4. name"姓名"
    5. setNamefunction (str){this.name=str}
    6. __proto__Father
      1. constructorfunction Father(){
      2. getAgefunction (){return this.age;}
      3. setAgefunction (age){this.age = age;}
      4. __proto__Object
由上可知,我们将子类的prototype属性的值设置为父类的一个实例,就可以使得子类继承了父类的所有属性与方法,需要注意的是,代码Sun3.prototype.constructor = Sun3的意思是将子类的prototype.constructor重新设置为子类的构造器函数。任何一个prototype对象都有一个constructor属性,指向它的构造函数。也就是说,Sun3.prototype 这个对象的constructor属性,是指向Sun3的。代码Sun3.prototype = new Father()删除了这个Sun3.prototype对象原来的值,所以新的prototype对象没有constructor属性,所以我们必须手动加上去,否则后面的"继承链"会出问题。这一点很重要,要切记。


5.继承函数的实现

既然我们知道了如何通过prototype来实现继承,那么我们就可以将该实现封装成一个函数,使得代码可重用,代码如下:

function inherit(child,parent){child.prototype = new parent();child.prototype.constructor = child;}function Sun4(){}inherit(Sun4,Father);console.log(new Sun4());
运行结果如下:

  1. __proto__Father
    1. age"20"
    2. constructorfunction Sun4(){}
    3. getNamefunction (){return this.name;}
    4. name"姓名"
    5. setNamefunction (str){this.name=str}
    6. __proto__Father
      1. constructorfunction Father(){
      2. getAgefunction (){return this.age;}
      3. setAgefunction (age){this.age = age;}
      4. __proto__Object
我们将继承封装成inherit函数,要想实现两个类的继承关系时,把这两个类作为参数传递进去即可。


6.面向对象的通用继承的实现

虽然上文已经通过inherit函数实现了继承函数,但是在使用时每次都要把父子两个类传递进去,还不够面向对象,为了更好的模拟面向对象的特性,我们再进一步进行改进,代码如下:

Function.prototype.method = function(funcName,func){this.prototype[funcName] = func;};Function.prototype.inherit = function(parent){this.prototype = new parent();this.prototype.constructor = this;};function Sun5(){}Sun5.inherit(Father);console.log(new Sun5());

运行结果如下:

Sun5
  1. __proto__Father
    1. age"20"
    2. constructorfunction Sun5(){}
    3. getNamefunction (){return this.name;}
    4. name"姓名"
    5. setNamefunction (str){this.name=str}
    6. __proto__Father
      1. constructorfunction Father(){
      2. getAgefunction (){return this.age;}
      3. setAgefunction (age){this.age = age;}
      4. __proto__Object
Function.prototype.method:此函数是为构造器的prototype附加函数的简单方式。这一特殊的子句能够工作是因为所有的构造器都是函数,故能获得新的方法"method"。 

Function.prototype.inherit:这一函数能用来实现单父继承。由于inherit方法是在Function的原型中的,所以使得所有的子类都能有原生的使用该inherit方法,只需要传递父类的构造函数即可。

注意:在inherit函数中调用parent 的构造函数,没有给它传递参数。这在原型链中是标准做法。要确保构造函数没有任何参数。

如果父类的构造函数中有参数,那该怎么办呢?可以这样实现,代码如下:

function Father(name,age){this.name = name;this.age = age;this.setName = function(str){this.name=str};this.getName = function(){return this.name;};}Father.prototype.setAge = function(age){this.age = age;};Father.prototype.getAge = function(){return this.age;};console.log(new Father());Function.prototype.method = function(funcName,func){this.prototype[funcName] = func;};Function.prototype.inherit = function(parent){this.prototype = new parent();this.prototype.constructor = this;};function Sun6(name,age){Father.call(this,name,age);}Sun6.inherit(Father);console.log(new Sun6("姓名",20));

运行结果如下:

Sun6
  1. age20
  2. getNamefunction (){return this.name;}
  3. name"姓名"
  4. setNamefunction (str){this.name=str}
  5. __proto__Father
    1. ageundefined
    2. constructorfunction Sun6(name,age){
    3. getNamefunction (){return this.name;}
    4. nameundefined
    5. setNamefunction (str){this.name=str}
    6. __proto__Father

这里通过call与prototype的共同使用才实现了继承。

参考:

http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance.html

http://www.w3school.com.cn/js/pro_js_inheritance_implementing.asp