JavaScript继承
JavaScript的继承
前言:
在后端开发语言中(面向对象程序设计语言,如:C#,Java等)中,这些都是语言都有继承的概念,它们都是为多态服务的,但是在JavaScript中是没有多态的概念的。
JavaScript继承的目的
- JavaScript的继承,其目的是为了代码的重用,即减少代码的冗余,它跟强类型语言(C#,java)不是一个概念。
- JavaScript的继承没有专门的用来继承的语法(运算符,关键字等)。
- JavaScript的继承只是通过一些手段来模拟继承的概念。
JavaScript的三种继承形式
①原型继承 ②构造函数的继承 ③组合继承。
一:原型继承
例子:Student函数对Person函数里方法的继承。
注:我会把这个代码段拆分成两个部分,这样好理解一点。
代码段一:Person()
函数部分
<script>
function Person(name,age,gender){
this.name = name;
this.age = age;
this.gender = gender;
}
Person.prototype.eat = functio(){
console.log('人都可以吃饭');
}
Person.prototype.sleep = function(){
console.log('睡着了,zzzz');
}
var james = new Person('james',22,'男');
james.eat();
james.sleep();
解释:Person函数里有三个成员name,age,gender,并且在Person函数里构造了两个方法eat方法和sleep方法。
代码段二:Student()
函数部分
function Student(sId,address){
this.sId = sId;
this.address = address;
}
Student.prototype = new Person('jerry',28,'男');
Student.prototype.study = function(){
console.log('开始学习,别拦我!');
}
var lm = new Student('李明',18,'男','164456123','未知之地');
lm.eat();
lm.study();
lm.sleep();
lm.name = '黎明';
console.log(lm.name);
console.log(lm.__proto__.name);、
console.dir(lm);
</script>
解释:Student函数里有两个成员sId,address,其本身就有一个默认的原型对象,但是我们new了一个新的Person对象,对其做了一个原型替换。
思考:为什么做了原型替换后,Student就具有了Person的实例(成员)?
原因:
①本身Person函数的原型链
__proto__
以及原型prototype
是指向其原型上的方法,Student函数也是一样的。②在进行了原型替换后,Student的原型
prototype
指向了Person的成员。③接着我们声明了一个变量lm,然后对Student函数进行了实例化,所以lm有两个字段***sId,address***,但是在这个实例中的原型链
__proto__
指向了Person函数的实例。
听了描述,你可能会有点蒙,我们画图解释一下:
注:右侧的蓝色address下为
__proto__
,Constructor是一个指针,指向了构造的Person方法。
解释一段代码:
Student.prototype.study = function(){
console.log('开始学习,别拦我!');
}
这是动态添加的方法,但是应为原型的替换,所以方法添加到了Person函数中。需要注意的是添加的方法,一定要在原型替换之后,不然会覆盖。
知识点:
- JavaScript的原型
Prototype
和__proto__
都存在于构造方法的实例中。 - JavaScript代码找的顺序:代码在执行时会先去找
Prototype
然后去寻找__proto__
二:构造函数的继承
例子:
<script>
//构造函数继承
function Person(name,age,gender){
this.name = name;
this.age = age;
this.gender = gender;
}
Person.prototype.eat = functio(){
console.log('人都可以吃饭');
}
Person.prototype.sleep = function(){
console.log('睡着了,zzzz');
}
function Student(name,age,gender,sId,address){
//方法借用(不懂的可以去看我上一篇博客,call()和apply()用法)
Person.call(this,name,age,gender);
this.sId = sId;
this.address = address;
}
var lm = new Student('李明',18,'男','164456123','未知之地');
lm.eat();
</script>
构造函数的继承很是简单,只是使用了一个方法借用。不会方法借用的可以去看我的博客JavaScript里call()和apply()的用法
但是无论是原型继承还是构造函数继承都有问题:
- 通过原型继承,解决了能得到基类所有成员的问题,包括原型上定义的方法,但是它会造成基类的字段成员是被子类的所有实例对象共享。
- 通过构造函数继承,解决了字段属性是被共享的问题,但是没法访问到基类的原型对象中定义的方法。
三:组合继承
我们前面说原型继承和构造函数继承都有各自的问题,但是我么也看出来了,两者的问题是互补的,所以如果把原型继承和构造函数继承放到一起,那么问题解决了,所以出现了
组合继承,其实可以理解为是原型继承和构造继承的组合体。
例子:
<script>
//组合继承
function Person(name,age,gender){
this.name = name;
this.age = age;
this.gender = gender;
}
Person.prototype.eat = functio(){
console.log('人都可以吃饭');
}
Person.prototype.sleep = function(){
console.log('睡着了,zzzz');
}
//
学生类
function Student(name,age,gender,sId,address){
//方法借用
Person.call(this,name,age,gender);
this.sId = sId;
this.address = address;
}
Student.prototype = new Person();
var lzl = new Student('林志玲',28,'女','164456789','台湾');
console.dir(lzl);
</script>
上面的代码,相必不用我解释了,这综合了原型继承和构造函数继承,此时的lzl也可以调用eat和sleep方法。
如果要添加新的成员,也是可以动态添加,但是依然要注意,要在原型替换之后。
如:添加一个study方法:
Student.prototype.study = function(){
console.log('good good study');
}
lzl.study();
--------以上就是JavaScript的继承了,希望大家可以理解。---------