您的位置:首页 > Web前端 > JavaScript

【JavaScript】实现继承的方式

2018-03-27 11:45 453 查看
引言:面向对象的编程语言都具继承这一机制,而 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方式
获取
父类的属性
,又通过
类式继承
方式获取
父类的方法


优点是:它既可以保证属性的私用又能继承父类的方法

缺点是:它的类式继承和构造函数继承使其两次创建属性,不符合继承的特有的优点.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: