继承

作者: 你喜欢吃青椒吗_c744 | 来源:发表于2019-08-06 11:49 被阅读0次

原型链继承

function Animal() {

  this.name = 'animal';

  this.type = ['pig', 'cat'];

}

// 为父类添加共有方法

Animal.prototype.greet = function(sound) {

  console.log(sound);

}

// 声明子类

function Dog() {

  this.name = 'dog';

}

// 继承父类

Dog.prototype = new Animal();
var dog = new Dog();

dog.greet('汪汪');  //  "汪汪"

console.log(dog.type); // ["pig", "cat"]

在上面中,创建了一个构造函数Animal,并且它的原型中还有一个greet方法。接着又让Dog.prototype成为Animal的实例,意味着Dog.prototype会继承Animal原型的方法greet,同时dog又是Dog的实例,意味着dog会继承Dog.prototype的方法,所以dog才有greet的方法。

//原型链继承
Animal.prototype --> Dog.prototype --> dog

构造函数继承

基本思想:即在子类型构造函数的内部调用超类型构造函数(父亲辈,爷爷辈)

function Father(){
    this.colors = ["red","blue","green"];
}
function Son(){
    Father.call(this);//继承了Father,且向父类型传递参数
}
var instance1 = new Son();
instance1.colors.push("black");
console.log(instance1.colors);//"red,blue,green,black"

var instance2 = new Son();
console.log(instance2.colors);//"red,blue,green" 可见引用类型值是独立的

很明显,借用构造函数一举解决了原型链的两大问题:

其一, 保证了原型链中引用类型值的独立,不再被所有实例共享;

其二, 子类型创建时也能够向父类型传递参数

但是。函数复用不可用,加重内存负担。构造函数的技术也很少单独使用。

组合继承(原型链继承+构造函数继承)

// 声明父类   

function Animal(color) {    

  this.name = 'animal';    

  this.type = ['pig','cat'];    

  this.color = color;   

}     

// 添加共有方法  

Animal.prototype.greet = function(sound) {    

  console.log(sound);   

}     

// 声明子类   

function Dog(color) { 

  // 构造函数继承    

  Animal.apply(this, arguments);   

}   

// 类式继承

Dog.prototype = new Animal();   

var dog = new Dog('白色');   

var dog2 = new Dog('黑色');     

dog.type.push('dog');   

console.log(dog.color); // "白色"

console.log(dog.type);  // ["pig", "cat", "dog"]

console.log(dog2.type); // ["pig", "cat"]

console.log(dog2.color);  // "黑色"

dog.greet('汪汪');  // "汪汪"

原型链继承可以拿到父构造函数的属性和方法;构造函数继承可以修改父构造函数的内容。它综合了类式继承和构造函数继承的优点,同时去除了缺陷。

我们访问到的引用类型(比如上面的type)其实是通过apply复制到子类中的,所以不会发生共享。

这种组合继承也是有点小缺陷的,那就是它调用了两次父类的构造函数。

寄生组合式继承(据说是最好用的继承方式)

前面讲过,组合继承是 JavaScript 最常用的继承模式; 不过, 它也有自己的不足. 组合继承最大的问题就是无论什么情况下,都会调用两次父类构造函数: 一次是在创建子类型原型的时候, 另一次是在子类型构造函数内部. 寄生组合式继承就是为了降低调用父类构造函数的开销而出现的 .

其背后的基本思路是: 不必为了指定子类型的原型而调用超类型的构造函数

function Animal(color) {

  this.color = color;

  this.name = 'animal';

  this.type = ['pig', 'cat'];

}

Animal.prototype.greet = function(sound) {

  console.log(sound);

}

function Dog(color) {

  Animal.apply(this, arguments);

  this.name = 'dog';

}

/* 注意下面两行 */

Dog.prototype = Object.create(Animal.prototype);

Dog.prototype.constructor = Dog;

Dog.prototype.getName = function() {

  console.log(this.name);

}

var dog = new Dog('白色');   

var dog2 = new Dog('黑色');     

dog.type.push('dog');   

console.log(dog.color);   // "白色"

console.log(dog.type);   // ["pig", "cat", "dog"]

console.log(dog2.type);  // ["pig", "cat"]

console.log(dog2.color);  // "黑色"

dog.greet('汪汪');  //  "汪汪"

在上面的例子中,我们并不像构造函数继承一样直接将父类Animal的一个实例赋值给Dog.prototype,而是使用Object.create()进行一次浅拷贝,将父类原型上的方法拷贝后赋给Dog.prototype,这样子类上就能拥有了父类的共有方法,而且少了一次调用父类的构造函数。

这里还需注意一点,由于对Animal的原型进行了拷贝后赋给Dog.prototype,因此Dog.prototype上的constructor属性也被重写了,所以我们要修复这一个问题:

Dog.prototype.constructor = Dog;

extends继承

Class和extends是在ES6中新增的,Class用来创建一个类,extends用来实现继承

//原先的写法
function Student(name) {
    this.name = name;
}

Student.prototype.hello = function () {
    alert('Hello, ' + this.name + '!');
}
//用新的class关键字来编写Student
class Student {
    constructor(name) {
        this.name = name;
    }

    hello() {
        alert('Hello, ' + this.name + '!');
    }
}
//继承
class PrimaryStudent extends Student {
    constructor(name, grade) {
        super(name); // 记得用super调用父类的构造方法!
        this.grade = grade;
    }

    myGrade() {
        alert('I am at grade ' + this.grade);
    }
}

注意PrimaryStudent的定义也是class关键字实现的,而extends则表示原型链对象来自Student。子类的构造函数可能会与父类不太相同,例如,PrimaryStudent需要namegrade两个参数,并且需要通过super(name)来调用父类的构造函数,否则父类的name属性无法正常初始化。

PrimaryStudent已经自动获得了父类Studenthello方法,我们又在子类中定义了新的myGrade方法。

子类必须在constructor方法中调用super方法。如果不调用super方法,子类就得不到this对象

ES6引入的class和原有的JavaScript原型继承有什么区别呢?实际上它们没有任何区别,class的作用就是让JavaScript引擎去实现原来需要我们自己编写的原型链代码。简而言之,用class的好处就是极大地简化了原型链代码。

参考文章

js原型链继承,借用构造函数继承,组合继承,寄生式继承,寄生组合继承

廖雪峰的个人网站-class继承

JavaScript实现继承的方式

JS原型链与继承别再被问倒了

相关文章

  • 继承 继承

    属性拷贝 继承不单单能通过原型链实现,也能通过其他方式实现,属性拷贝就是其中一种方法。 通过属性拷贝也能实现继承子...

  • 继承(单继承,多继承)

    将共性的内容放在父类中,子类只需要关注自己特有的内容 python中所有的内容都是对象,所有的对象都直接或间接继承...

  • js继承方式

    类式继承 构造函数继承 组合继承 类式继承 + 构造函数继承 原型式继承 寄生式继承 寄生组合式继承 寄生式继承 ...

  • Python-学习之路-08 OOP -02

    单继承和多继承 单继承:每个类只能继承一个类 多继承:每个类可以继承多个类 单继承的多继承的优缺点 菱形继承/钻石...

  • 原型相关(二)

    1.继承 继承方式:接口继承(只继承方法签名)实现继承(继承实际的方法)ECMAScript只支持实现继承,并且主...

  • 继承

    继承的引入和概述 继承案例和继承的好处 继承的弊端 Java中继承的特点 继承的注意实现和什么时候使用继承 继承中...

  • Java面向对象三大特性之继承

    继承 一、继承的特点 Java只支持单继承单继承 多继承 单继承、多继承优缺点①单继承优点:提高了代码的复用性,让...

  • 7、面向对象的程序设计3(《JS高级》笔记)

    三、继承 许多OO语言都支持两种继承方式:接口继承和实现继承。接口继承只继承方法签名,而实现继承则继承实际方法。由...

  • 【重学前端】JavaScript中的继承

    JavaScript中继承主要分为六种:类式继承(原型链继承)、构造函数继承、组合继承、原型式继承、寄生式继承、寄...

  • js之继承

    文章主讲 JS 继承,包括原型链继承、构造函数继承、组合继承、寄生组合继承、原型式继承、 ES6 继承,以及 多继...

网友评论

      本文标题:继承

      本文链接:https://www.haomeiwen.com/subject/wsxidctx.html