【JavaScript】实现继承的方式

本文深入探讨JavaScript中的多种继承方式,包括类式继承、构造函数继承和组合继承等。通过具体的代码示例,详细分析每种继承方式的工作原理、优缺点及其应用场景。

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

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

缺点是:它的类式继承和构造函数继承使其两次创建属性,不符合继承的特有的优点.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值