引言:面向对象的编程语言都具继承这一机制,而 JavaScript 是基于原型(Prototype)面向对象程序设计,所以它的实现方式也是基于原型(Prototype)实现的。
实现继承有多种方式,下面我们还是以Person和Student来分析
function Person() {
}
function Student() {
}
Student.prototype = Person.prototype; // 我们可不可用这种方式呢?这种方法是错误的:因为子类Student有自己的一些方法
//,如果通过这样赋值,改变Student的同时也改变了Person。
Student.prototype = new Person(); //这种方式是可以实现的,但是调用构造函数有时候也是有问题的,比如要传进Person一个name和age
//,这里的Student是个类,还没实例化,这时候有些奇怪了,传什么都不是。
Student.prototype = Object.create(Person.prototype); //相对来说这中方式是比较理想的,这里我们创建了一个空的对象
//,并且对象的原型指向Person.prototype,这样我们既保证了继承了Person.prototype上的方法,并且Student.prototype又有自己空的对象。
//但是Object.create是ES5以后才有的
JavaScript 继承:
- 类式继承
- 构造函数继承
- 组合继承
|| 类式继承
//声明父类
function SuperClass(){
//值类型
this.superValue = true;
//引用类型
this.book = ['c','java','htnl']
}
//为父类添加方法
SuperClass.prototype.getSuperValue =function(){
return this.superValue;
}
//声明子类
function SubClass(){
this.subValue = false;
}
//继承父类
SubClass.prototype = new SuperClass();
//为子类添加方法
SubClass.prototype.getSubValue = function(){
return this.subValue;
}
//测试
var a = new SubClass();
var b = new SubClass();
console.log(a.getSubValue()); //false
console.log(a.getSuperValue()); //true
console.log(a.book);//["c", "java", "htnl"]
console.log(b.book);//["c", "java", "htnl"]
a.book.push('css');
console.log(a.book);//["c", "java", "htnl", "css"]
console.log(b.book);//["c", "java", "htnl", "css"]
console.log(a.getSuperValue())//true
console.log(b.getSuperValue())//true
a.superValue = 'a';
console.log(a.getSuperValue())//a
console.log(b.getSuperValue())//true
console.log(a.getSubValue())//false
console.log(b.getSubValue())//false
a.subValue = 'sub';
console.log(a.getSubValue())//sub
console.log(b.getSubValue())//false
a.book = ['1','2','3'];
console.log(a.book);//['1','2','3']
console.log(b.book);//["c", "java", "htnl", "css"]
类式继承
通过 子类原型 继承 父类的实例 实现继承.
子类实例的原型都是父类实例,它的属性共用(值类型私用,引用类型共用或私用)。
类式继承 继承的是父类原型上的方法和属性(方法和属性都在proto原型链上,所以共用,导致一改全改) 。
|| 构造函数继承
function SuperClass(name){
this.name =name;
this.book = ['c','java','htnl']
this.getBook =function(){
return this.book;
}
}
//为父类添加方法
SuperClass.prototype.getName =function(){
return this.name;
}
//声明子类
function SubClass(name){
SuperClass.call(this,name) //子类通过 SuperClass.call(this,name) 将子类中的变量在父类中执行了一遍,由于父类中是给 this 绑定属性的,因此子类自然也就继承了父类的共有属性.
}
var a = new SubClass('demongao');
var b = new SubClass('gsc');
a.book.push("cccc");
console.log(a.book,a.name); //["c", "java", "htnl", "cccc"] "demongao"
console.log(b.book,b.name); //["c", "java", "htnl"] "gsc"
console.log(a.getBook()); //["c", "java", "htnl", "cccc"]
console.log(a.getName()) //TypeError
构造函数
通过 call()
或 apply()
的使用,改变函数的作用环境。
call()
和 apply()
作用是一样的,唯一的区别在于:
- apply() 接受的是一个包含多个参数的数组(或类数组对象)
call() 接受的是一个参数列表。
由于这种类型的继承没有涉及原型prototype,所以父类的原型方法自然不会被子类继承,而如果想被子类继承就必须要放在构造函数中,这样创建出来的每个实例都会单独拥有一份而不能共用,这就违背了代码复用的原则
构造函数 继承的是父类的共有属性,通过 call 将属性绑定到子类上。
|| 组合继承
//声明父类
function SuperClass(name){
//值类型共有属性
this.name = name;
//引用类型共有属性
this.books = ["html","css","JavaScript"];
}
//父类原型共有方法
SuperClass.prototype.getName = function () {
console.log(this.name);
}
//声明子类
function SubClass(name,time){
SuperClass.call(this,name);
this.time =time;
}
//类式继承 子类原型继承父类
SubClass.prototype = new SuperClass();
SubClass.prototype.getTime = function(){
console.log(this.time);
组合继承
通过 构造函数
继承 子类
, 通过 call方式
获取 父类的属性
,又通过 类式继承
方式获取 父类的方法
。
优点是:它既可以保证属性的私用又能继承父类的方法
缺点是:它的类式继承和构造函数继承使其两次创建属性,不符合继承的特有的优点.