javaScript面向对象类与继承

本文是学习慕课网上课程前端跳槽面试必备技巧的学习笔记,便于之后复习。

本文从以下方面介绍类与继承:

  1. 类的声明以及实例化
  2. 实现继承的几种方式

1.类的声明以及实例化

类的声明通常有两种方式:

<script type="text/javascript">
	/*
	 类的声明
	 */
	function Animal1 () {
		this.name = 'name';
	}
	/*
	 ES6中类的声明
	 */
	class Animal2 {
		constructor () {
			this.name = 'name';
		}
	}
	/*
	 实例化类的对象
	 */ 
	var a1 = new Animal1();
	var a2 = new Animal2();
	console.log(a1, a2);
</script>

使用new运算符进行实例化;

2.实现继承的几种方式

借助构造函数实现继承

这种继承的方法有一个缺点,它只是把父类中的属性继承了,但父类的原型中的属性继承不了。

具体例子如下:

<script type="text/javascript">
        function Parent1() {
		this.name = 'parent1';
	}
	// 在父类的原型上定义say方法
	Parent1.prototype.say = function () {};
	 
	function Child1() {
		// 将父级构造函数的this指向子构造函数的实例上去 这样父级构造函数的属性在子级也会拥有
		Parent1.call(this); // apply 和 call都行 
		this.type = 'child1';
	}
	child1 = new Child1();
	console.log(child1);
	// 调用父类原型上的方法 会报错
	child1.say();
</script>

输出结果:

        在子类的函数体里执行父级的构造函数,同时改变函数运行的上下文环境(也就是this的指向),使this指向Child1这个类,从而导致了父类的属性都会挂载到子类这个类上去,如此便实现了继承。但是它只是把父类中的属性继承了,但父类的原型中的属性继承不了。如上图child1不能继承父类原型上的say()方法。

借助原型链实现继承

<script type="text/javascript">
	function Parent2 () {
		this.name = 'parent2';
		this.play = [1,2,3];
	}
	function Child2 () {
		this.type = 'child2';
	}
	//通过把Child2的原型指向Parent2来实现继承
	Child2.prototype = new Parent2();

	var s1 = new Child2();
	var s2 = new Child2();
	console.log(s1, s2);

</script>

可以看到在Child2的实例的__proto__的属性中有Parent2的属性,由此实现了Child2从Parent2的继承。

但是原型链存在一定的不足:

在上部分代码中加入以下代码:

console.log(s1.play, s2.play); // s1,s2都是1,2,3
s1.play.push(4); // s1中加入4
console.log(s1.play, s2.play); // s1,s2都会变成1,2,3,4 因为s1.__proto__ === s2.__proto__ 为true

运行结果如图所示:

我们只改了s1这个实例的属性,却发现Child2的其他实例的属性都一起改变了,因为s1修改的是它原型的属性,原型的属性修改,所有继承自该原型的类的属性都会一起改变,因此Child2的实例之间并没有隔离开来,这显然不是我们想要的。

组合继承方式

组合方式就是前两种方法组合而成的,上面两种方式都有不足,这种方式就解决了上面两种方式的不足。

<script type="text/javascript">
	function Parent3 () {
		this.name = 'parent3';
		this.play = [1, 2, 3];
	}
	function Child3 () {
		Parent3.call(this);	// 子类里执行父类构造函数 执行一次父类
		this.type = 'child3';
	}
	Child3.prototype = new Parent3(); // 子类原型指向父类  执行一次父类

	var s3 = new Child3();
	var s4 = new Child3();
	console.log(s3.play, s4.play); // s3,s4都是1,2,3
	s3.play.push(4); // s3中加入4
	console.log(s3.play, s4.play); // s3变成1,2,3,4,s4是1,2,3

</script>

修改某个实例的属性,并不会引起父类的属性的变化。

这种方式的继承把构造函数和原型链的继承的方式的优点结合起来,并弥补了二者的不足,功能上已经没有缺点了。

但这种方法仍不完美,因为创建一个子类的实例的时候,父类的构造函数执行了两次。

组合继承方式优化1

组合方式中为了解决借助构造函数继承(也就是本文中第一种)的缺点,父类的原型中的属性继承不了,所以才把子类的原型指向了父类。但是父类的属性,在子类已经中已经存在了,子类只是缺少父类的原型中的属性,所以,根据这一点,我们做出优化。

<script type="text/javascript">
	function Parent4 () {
		this.name = 'parent4';
		this.play = [1, 2, 3];
	}
	function Child4 () {
		Parent4.call(this);	// 执行一次父类
		this.type = 'child4';
	}
	// 父类原型对象赋予子类的原型对象
	Child4.prototype = Parent4.prototype; // 优化部分

	//以下代码为测试部分
	var s5 = new Child4();
	var s6 = new Child4();
	console.log(s5.play, s6.play); // s5,s6都是1,2,3
	s5.play.push(4); // s3中加入4
	console.log(s5.play, s6.play); // s5变成1,2,3,4,s6是1,2,3
	// 输出结果均为 true 无法确定s5是由父类实例化还是子类实例化
	console.log(s5 instanceof Child4, s5 instanceof Parent4);
	// s5构造器指向了Parent4 而不是Child4
	console.log(s5.constructor);

</script>

可以看到s5是new Child4()出来的,但是他的constructor却是Parent4.

这是因为Child4这个类中并没有构造函数,它的构造函数是从原型链中的上一级拿过来的,也就是Parent4。

组合方式继承优化2:

<script type="text/javascript">
	function Parent5 () {
		this.name = 'parent5';
		this.play = [1, 2, 3];
	}
	function Child5 () {
		Parent5.call(this);
		this.type = 'child5';
	}
	// 通过Object.create创建一个中间对象 中间对象的原型对象是父类的原型对象
	// 此时s7.constructor还是指向Parent5 因为会顺着原型链一往上找
	Child5.prototype = Object.create(Parent5.prototype); 
	// 把Child5的原型的构造函数指向自己 在优化1中加这句话也不行,因为这样就把父类和子类原型对象均改为子类的原型对象,无法区分父类实例的原型对象
	Child5.prototype.constructor = Child5;
	
	//以下代码是测试部分
	var s7 = new Child5();
	console.log(s7 instanceof Child5, s7 instanceof Parent5);
	// s7构造器指向了Child5
	console.log(s7.constructor);

</script>

本例中通过把子类的原型指向Object.create(Parent5.prototype),实现了子类和父类构造函数的分离,但是这时子类中还是没有自己的构造函数,所以紧接着又设置了子类的构造函数,由此实现了完美的组合继承。

结束语:

并没有直接把最完美的继承直接写出来,而是由浅入深循序渐进的来介绍的。

类的继承就告一段落了,这部分内容确实不好理解,有点难懂。结合代码部分有助于理解。

猜你喜欢

转载自blog.csdn.net/lxkll/article/details/88226050