一、基本概念:
JavaScript基于原型实现面向对象特性,在JS编程中,面向对象的继承是重点,本文将从简单开始介绍JS的继承。
二、继承方式:
1、借助构造函数实现继承:
function Parent1(){
this.name = 'parent1'
}
function Child1(){
Parent1.call(this)
this.type = 'child1'
}
父级构造函数在子级构造函数内使用call、apply方法,将自身的this指向子级实例,并执行,从而使得子级实例后拥有父级的属性;
简单,但是父级原型上的属性无法继承;
2、借助原型链继承:
function Parent2(){
this.name = 'parent2';
}
function Child2(){
this.type = 'Child2';
}
Child2.prototype = new Parent2();
console.log(new Child2());
Child2构造函数的属性prototype指向一个原型对象,可以任意更改,将 Parent2 的实例作为Child2的原型对象,于是,当 new Child2 后生成的实例, 其 __proto__ 指向的原型对象就是 Child2的prototype指向的Parent2实例
这样,子实例共享同一个继承的实例的属性,更改其中之一,会改变所有;没有隔离;
3、组合继承:
function Parent3(){
this.name = 'parent3';
this.play = [1,2,3,4];
}
function Child3(){
Parent3.call(this);
this.type = 'child3';
}
Child3.prototype = new Parent3();
var c31 = new Child3();
var c32 = new Child3();
c31.play.push(5);
console.log(c31, c32);
console.log(c31.constructor)
弥补了构造函数和原型链继承的缺点, 但是,父级构造函数体执行了两次,生成的实例无法判断实例源(实例的constructor属性指向父级的构造函数)。(详细见组合优化1)
4、组合继承的优化1:
function Parent4(){
this.name = 'parent4';
this.play = [1,2,3,4];
}
function Child4(){
Parent4.call(this);
this.type = 'child4';
}
Child4.prototype = Parent4.prototype;
var c41 = new Child4();
var c42 = new Child4();
c41.play.push(5);
console.log(c41, c42);
console.log(c41 instanceof Child4, c41 instanceof Parent4); // true true
console.log(c41.constructor); // 指向 Parent4
弥补了组合继承 执行 2次的缺点, 但是,生成的实例,使用instanceof 判断的时候,无法判断该对象是由Parent5实例化或Child5实例化 , 使用实例的constructor属性(实际为__proto__指向原型对象上的属性),指向 Parent5.... 于是无法判断该实例是由哪个构造函数实例化的;(组合继承都有这个缺点)
5、组合继承的优化2:
function Parent5(){
this.name = 'parent5';
this.play = [1,2,3,4];
}
function Child5(){
Parent5.call(this);
this.type = 'child5';
}
Child5.prototype = Object.create(Parent5.prototype); // 创建中间对象
Child5.prototype.constructor = Child5; // 中间对象的构造函数更改constructor
var c51 = new Child5();
var c52 = new Child5();
c51.play.push(5);
console.log(c51, c52);
console.log(c51 instanceof Child5, c51 instanceof Parent5);
console.log(c51.constructor);
弥补了前面继承方式的缺点;完美~~~~~
网友评论