JavaScript中的继承

继承

继承:就是让一个对象可以具有另一个对象的特性。
每个构造函数都是一个类,可以实例化出很多具体的对象。

写一个兔子类,写一个鸡类


function Rubbit(){ // 兔子的构造函数
    this.name = "兔子";
    this.tui = "4条";
    this.eat = "能吃";
    this.pao = "能跑"
}

function Ji(){ // 鸡的构造函数
	this.name = "鸡"
	this.tui = "2条";
    this.eat = "能吃";
    this.pao = "能跑";
}

从上面代码中能看出来,兔子类和鸡类,有一些共同的特征:eat 和 pao这两个属性相同。其实,这是每个动物都能有的特征。为了节省代码,也为了描述他们的关系,我们可以写一个动物类,让兔子类和鸡类都具备动物的一些特征,也就是将它们相同的属性都拿出来放到一个动物类中。

那么,怎么才能让一个对象能访问另一个对象的属性呢? - -继承

有以下四种方式继承。

-------- 1、原型继承 -------

访问一个对象的属性或方法的时候,先在当前对象上找,找不到,就去这个对象的原型(对象)上找,找到了就能访问了。
上面我们说到,兔子的原型上如果能有 eat和pao 的属性的话,就好了,使用兔子直接就能访问原型上的 eat和pao了 。

通过一个例子来说明:


function Animal(){
    this.name = "动物";
    this.attribute = "能动";
}
var animal = new Animal(); // 实例化动物类,得到动物对象

function Dog(){
    this.jiao = "汪汪";
}

function Pig(){
    this.jiao = "哼哼";
}

Dog.prototype = animal; // 将狗类的原型赋值为动物对象
Dog.prototype.constructor = Dog; // 手动给新的原型添加constructor属性指回构造函数
var dog = new Dog();
console.log(dog.name); // 访问狗对象的name属性 - 动物

Pig.prototype = animal; // 将猪类的原型赋值为动物对象
Pig.prototype.constructor = Pig; // 手动给新的原型添加constructor属性指回构造函数
var pig = new Pig();
console.log(pig.name); // 访问猪对象的name属性 - 动物

通过上面的代码可以看出,将两者共同的属性放在一个动物类中,并将其的原型赋值为动物对象,这样狗类和猪类就都有了Animal动物类的两个属性了,然后可以通过 对象.属性 访问到。

注意!!!可以看出,我们更改了构造函数原本就自带的原型prototype,而这个prototype属性天生自带一个属性constructor,指向构造函数,一旦使用原型继承,这个原型上没有constructor属性了。我们有一个解决办法,就是使用原型继承的时候,应该手动添加constructor属性,让新的原型也指向构造函数,如上代码。

-------2、上下文调用模式继承 / 借用函数继承-------
把父类构造函数体借用过来使用一下。


function Animal(){
    this.eat = "能吃";
    this.pao = "能跑";
}
Animal.prototype.dong = function(){
    console.log("能动");
}

function Dog(){
	 /*
    上下文调用模式:
        call
        apply
        bind
    共同点就是能改变this的指向
    call怎么改变?
        可以调用函数,顺便将里面的this改成目标参数
    */
	Animal.call(this); // 将父类借用一下,并将函数的this原来是父类改变成子类,这里的this指的是new Dog的对象dog
	// 将Animal中的this改成了dog
    // 将Animal进行调用 - 执行里面的代码了,this.eat  ====>   dog.eat
    // this.pao  ===>   d.pao
  	this.name = "狗";
}

var dog = new Dog();
console.log(dog); // 包含了Animal中的两个属性
dog.dong(); // 调用dong方法,报错不存在这个方法

这种继承方式主要就是通过this指向来调用父类的属性和方法,那为什么调用父类原型上的dong方法会报错呢?
这就要说到借用函数继承的一个缺点了:
如果Animal这个构造函数,里面只绑定了属性,方法在Animal的原型上,但是这种继承方式不能继承原型上的方法/属性,也就是原型上的方法和属性都不能继承。

------3、组合继承------
就是把 原型继承 和 借用函数继承 两个方式组合在一起,可以解决我们上面说的借用函数继承的缺点。
那么怎么结合?


function Animal(){
    this.eat = "能吃";
    this.pao = "能跑";
}
Animal.prototype.dong = function(){
    console.log("能动");
}

function Dog(){
    // 使用借用函数继承模式
    Animal.apply(this);
    this.name = "狗";
}

// 使用原型继承
Dog.prototype = new Animal();
Dog.prototype.constructor = Dog; // 手动给新的原型添加constructor属性指回构造函数
var dog = new Dog();
console.log(dog);
dog.dong(); // 调用dong方法,也能访问了

也就是讲两种继承方式都用上,就可以解决借用函数继承的缺点

---------- 4、es6的继承 ----------

在es6之前,可以说没有类这个概念,es6中新增了定义类(构造函数的方式)

语法:类 extends 父类{ }

我们讲一下在es6中定义一个类

class Animal{
    
}
var animal = new Animal();

这种方式定义的类,也必须和new配合使用。

如果在实例化需要传递参数的话,就在这个类中,写一个函数,名字必须是 constructor


class Animal{
    constructor(name){
        this.name = name;
    }
}
var animal = new Animal("动物");
console.log(animal); // Animal {name: "动物"}

要给类添加方法,就像写constructor一样写一个函数,这种写法和我们以前给构造函数的原型上添加方法是一样的。

class Animal{
    constructor(name){
        this.name = name;
    }
    dong(){
        console.log("能动");
    }
}
var animal = new Animal("动物");
console.log(animal); // Animal {name: "动物"}
animal.dong(); // 能动

这种方式的类继承很容易,而且是固定语法:

class 子类 extends 父类{
    constructor(){
		super();
	}
}

例:


class Animal{ // 父类
    constructor(name){
        this.eat = "能吃";
        this.pro = "能跑"
        this.name = name;
    }
    dong(){
        console.log("能动");
    }
}

class Rubbit extends Animal{ // 子类 - extends关键字并不能执行父类的constructor
    constructor(name){
        // 如果父类有constructor,子类这里(constructor里面)必须调用一个叫做super的方法
        super(name); // 这是在执行父类的constructor
        this.tui = "4条";
    }
    sport(){
        console.log("跑的特别快");
    }
}
var r = new Rubbit("兔子");
console.log(r);

这就是es6的继承方式。

总结:继承就是让一个类拥有另一个类的属性和方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值