-
通过使用父构造的原型对象实现继承
function Person(name, age){ this.name = name; this.age = age; this.run = function(){ console.log(this.name, ' is runing'); } } Person.prototype.eat = function(){ console.log(this.name, ' is eating') } function Student(name, age, sex){ this.name = name; this.age = age; this.sex = sex; } Student.prototype = Person.prototype; let student = new Student('wang',12,'mail'); student.eat(); // wang is eating student.run(); // undefined 即 run方法未定义直接使用父构造函数原型对象的方法有一个弊端,不能访问父构造函数实例上的方法,因为构造函数本身没有被实例化
-
通过父构造函数实例实现继承
function Person(name, age){ this.name = name; this.age = age; this.run = function(){ console.log(this.name, ' is runing'); } } Person.prototype.eat = function(){ console.log(this.name, ' is eating') } function Student(name, age, sex){ this.name = name; this.age = age; this.sex = sex; } Student.prototype = new Person('wang', 12); Student.prototype.say = function(){ console.log(this.name, ' is saying') } let student = new Student('li', 33, 'femail') student.eat(); // li is eating student.run(); // li is runing student.say(); // li is saying let student2 = new Student('jiang',32, 'mail'); student2.__proto__ === student.__proto__ // true 这里为true说明并没有重复实例化Person对象从继承的功能实现上来说,并没有什么问题,问题在于这种写法太过于蹩脚,并且无法调用父类的构造方法。
-
借用构造函数
function Person(name, age){ this.name = name; this.age = age; this.run = function(){ console.log(this.name, ' is runing'); } } Person.prototype.eat = function(){ console.log(this.name, ' is eating') } function Student(name, age, sex){ Person.call(this,name,age) this.sex = sex; } Student.prototype.say = function(){ console.log(this.name, ' is saying') } let student = new Student('li', 33, 'femail') student.eat(); // Error: student.eat is not a function student.run(); // li is runing student.say(); // li is saying这种方式不能调用父构造函数的原型上定义的方法,因此,我们需要对此作出改进
function Student(name, age, sex){ Person.call(this,name,age) this.sex = sex; } Student.prototype = Person.prototype; Student.prototype.say = function(){ console.log(this.name, ' is saying') } let student = new Student('li', 33, 'femail') student.eat(); // li is eating student.run(); // li is runing student.say(); // li is saying这种方式保证了继承功能的完整性,似乎完美解决了继承的问题,但是还存在一个问题,Student.prototype的构造函数指向发生了改变,此时指向了Person构造函数,因此我们需要将构造函数重定向。
function Student(name, age, sex){ Person.call(this,name,age) this.sex = sex; } Student.prototype = Person.prototype; Student.prototype.constructor = Student; Student.prototype.say = function(){ console.log(this.name, ' is saying') } let student = new Student('li', 33, 'femail') student.eat(); // li is eating student.run(); // li is runing student.say(); // li is sayingOK, 我们似乎又一次完美实现了JavaScript利用原型实现继承的问题。但是还没有,因为Student.prototype === Person.prototype,我们更改了Student.prototype的原型对象指向的构造函数,那么Person.prototype指向的构造函数也发生了改变,所以我们还需要做一点操作。
function Student(name, age, sex){ Person.call(this,name,age) this.sex = sex; } Student.prototype = Object.create(Person.prototype); Student.prototype.constructor = Student; Student.prototype.say = function(){ console.log(this.name, ' is saying') } let student = new Student('li', 33, 'femail') student.eat(); // li is eating student.run(); // li is runing student.say(); // li is saying Student.prototype === Person.prototype这样,Person.prototype 和 Student.prototype都会指向各自的constructor,并且对各自的原型对象进行修改,都互不影响。
至此,才算完美的实现了JavaScript利用原型对象实现继承的问题。
补充:如果我们的需求只是想继承父构造函数通过原型共享出来的方法,那么做法如下
function Person (name, age) { this.name = name; this.age = age; this.run = function(){ console.log(this.name, ' is runing'); } } Person.prototype.eat = () => {console.log('i can eat anything')} function Student(course){ this.course = course; } Student.prototype = Person.prototype; Student.prototype.constructor= Student; let student = new Student('语文'); student.eat() // 输出: i can eat anything