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函数的实例。

听了描述,你可能会有点蒙,我们画图解释一下:

JavaScript继承

注:右侧的蓝色address下为__proto__,Constructor是一个指针,指向了构造的Person方法。

解释一段代码:

Student.prototype.study = function(){
    console.log('开始学习,别拦我!');
}

这是动态添加的方法,但是应为原型的替换,所以方法添加到了Person函数中。需要注意的是添加的方法,一定要在原型替换之后,不然会覆盖。

知识点:

  1. JavaScript的原型Prototype__proto__都存在于构造方法的实例中。
  2. 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的继承了,希望大家可以理解。---------