Skip to content

kingrong's blog

JavaScript原型继承的方式探讨

关于

基于类和子类的继承方式是我们在别的语言如pythonJavaC++中接触到最多的一种模式,这种模式无论是对于创建子类还是创建对象来说都是十分方便的。JavaScript算是一个特例吧,它的基于原型链的方式很容易将人们搞的迷糊,对于很多接触过class继承方法的人来说都是很难接受的一个事情,其中就包括我,我也是花了较长的一个时间才接受摸清这个事情。

虽然说基于原型链来实现面向对象编程的JavaScript在代码上有一些混乱,但是如果我们认真琢磨还是不难发现其中的有迹可循的真相。以下是我对JavaScript原型继承的方式的一个终结,适合已经了解了一些JavaScript面向对象编程的童鞋阅读,如果有错误欢迎指正。

原型继承方式

JavaScript的标准一直在更新,新的标准出来也不是所有的厂商和浏览器都能够跟上,所以这就造成了一部分混乱,JS面向对象课程的内容有老的有新的,但没有全的。这也是我在学习中遇到的困境。对于JavaScript原型继承的方式,我个人总结主要有以下集中。

  • 基于Object.create()方式
  • 使用new关键字
  • 使用桥接函数
  • class关键字

基于Object.create()方式

这个方法是ES6标准新出的,所以使用的时候需要注意支持与否的问题。

假设我们有一个Student对象,有一个name属性,有一个hello方法:

function Student(props){
	this.name = props.name || "Unamed";
} 
Student.prototype.hello = function(){
	alert("Hello " + this.name + "!");
}

我们想要创建一个新的对象小学生PrimaryStudent继承自Student,使用Object.create()方法我们需要这么做

function PrimaryStudent(props){
	Student.call(this,props);
}
PrimaryStudent.prototype = Object.create(Student.prototype);
PrimaryStudent.prototype.constructor = PrimaryStudent;  //因为我们重写了PrimaryStudent的原型所以我们需要重新改回它的构造函数
var xiaohua = new PrimaryStudent({name:"xiaohua"});

这个时候我们看看xiaohua的原型链是不是符合我们的要求

xiaohua ---> PrimaryStudent.prototype ---> Student.prototype --->Object.prototype ---> null

从这个原型链我们就可以看出PrimaryStudent确实是继承自Student的。

xiaohua.name; //”xiaohua“
xiaohua.hello(); //”Hello xiaohua!“

使用new关键字

准确说这种方法和上面的方法本质上是一模一样的,区别在在于我们把PrimaryStudent.prototype = Object.create(Student.prototype);这一行替换为了PrimaryStudent.prototype = new Student({}),其他的代码是一样的。

这两种方法在原型链上都是一样的,调用起来也可以达到一样的效果。

但是我们注意到一个问题,使用这种方式的时候我们不得不在Student()中加上参数,因为这是该构造函数所必须要求的,这种导致一个问题,我们的PrimaryStudent.prototype的属性中多了一个name,冗杂的属性,这并不是我们所需要的。

PrimaryStudent.prototype.hasOwnProperty("name"); //true

我们可以使用接下来的方法来修复。

使用桥接函数

使用桥接函数的方法来自JSON的发明者道格拉斯,我觉得这个方法是上一个方法的改进,桥接函数的核心是空函数F(),创建一个空函数F之后,令F.prototype = Student.prototype,然后再从F上创建新对象并赋值给PrimaryStudent.prototype,最后再对PrimaryStudent的构造函数进行修正。据此我们得到的PrimaryStudent确实是继承自Student的,并且没有属性上的冗余。

function Student(props) {
	this.name = props.name || 'Unnamed';
}

Student.prototype.hello = function () {
 	alert('Hello, ' + this.name + '!');
 }
function PrimaryStudent(props){
    Student.call(this,props);
}
var F = function(){};
F.prototype = Student.prototype;
PrimaryStudent.prototype = new F();
PrimaryStudent.prototype.constructor = PrimaryStudent;

var xiaohua = new PrimaryStudent({name:"xiaohua"});

道格拉斯这个方法可以说是很机智了,我们且来看看xiaohua的原型链:

xiaohua ---> PrimaryStudent.prototype ---> Student.prototype ---> Object.prototype ---> null

符合我们的要求并且PrimaryStudent中没有冗余的属性。

PrimaryStudent.prototype.hasOwnProperty("name");  //false

我们发现这个继承的方法可以复用,所以我们可以写以用于继承的函数,专门用来连接“子”与“父”,在此我们却不来实现了,可以在参考链接找一找哦

Class继承

这种方式可谓是最贴近真实,子类与父类是我们在学习其他语言经常就能接触到的,使用这种方式完成继承的关系,是我们更容易接受的一件事情。但是JavaScript并没有实现真正的Class的概念,现实仍然是Class包装下的原型链,所以我们还是有必要学习Class出现之前的继承方式,更有利于我们理解JavaScript原型链的概念。

Class关键字是ES6标准新定义的,所以我们在使用的时候也还是要考虑到兼容性问题。

使用Class定义一个Student

Class Student(){
	constructor(props){
		this.name = props.name;
	}
	hello() {
		alert("Hello "+ this.name + "!");
	}
}

使用Class定义一个PrimaryStudent继承自Student

Class PrimaryStudent extends Student(){
	constructor(props){
		super(props);
	}
} 

Class的方法简直不能太好用!!!

结尾

以上便是我个人在学习过程中总结出来的一些JavaScript的继承的方式,希望能够帮到你,更希望你能够在评论区提出一些意见,感谢观看!

参考链接:


Comments