JavaScript中继承的实现方式

1 构造函数式继承

构造函数式继承就是在子类中执行父类的构造函数,并为其传递参数,这个过程可以使用call()函数来实现。

示例代码:定义Person类(父类),它有自己的属性name和age,还有自己的方法getName()和getAge()

function Person(name, age) {
    this.name = name; // 初始化姓名和年龄
    this.age = age;
}
Person.prototype.getName = function () { // 获得姓名
    console.log("我的名字" + this.name);
}
Person.prototype.getAge = function () { // 获得年龄
    console.log("我的年龄:" + this.age);
}

接下来定义子类Student,在子类中调用父类的构造函数,获得父类的属性和方法:

function Student(name, age, grade) {
    Person.call(this, name, age); // 将参数传递给父类
    this.grade = grade; // 还可以定义自己的属性
}

但是这样的方式,当初始化子类的对象后,该对象无法获取父类的方法getName()和getAge(),因为父类的方法在原型链上,使用call()方法只获取到了父类本身的环境,并没有获取到父类的原型

let s1 = new Student("张三", 20, 100);
s1.getName(); // TypeError: s1.getName is not a function
s1.getAge(); // TypeError: s1.getAge is not a function

2 类式继承(原型式继承)

构造函数继承无法继承父类在原型链上的方法,那么类式继承便可以使用父类的方法了。

示例代码:定义Person类(父类),它有自己的属性name和age,还有自己的方法getName()和getAge()

function Person(name, age) {
    this.name = name; // 初始化姓名和年龄
    this.age = age;
}
Person.prototype.getName = function () { // 获得姓名
    console.log("我的名字" + this.name);
}
Person.prototype.getAge = function () { // 获得年龄
    console.log("我的年龄:" + this.age);
}

接下来定义子类Student,子类的原型指向了父类的实例对象,子类的构造函数再指回自己:

function Student(grade) {
    this.grade = grade;
}
Student.prototype = new Person(); // 子类的原型指向父类的实例对象
Student.prototype.constructor = Student; // 子类原型的构造函数再指回子类

类式继承也有一些问题:
①类的原型对象上出现了父类构造函数中的属性和方法,不仅多余,而且是错的。

let s1 = new Student("张三", 20, 100); // 实例化子类
console.log(s1);

在这里插入图片描述
②多执行了一次构造函数。如果有很多次继承,那么就会不停地执行构造函数,对资源是一种浪费。

③无法复用构造函数中存储属性的逻辑。子类的属性的初始化与父类没有关系。如果不在子类中初始化name和age,那么就获取不到具体的name和age值,而是显示undefined。

function Student(grade) {
    this.grade = grade;
}
Student.prototype = new Person(); // 子类的原型指向父类的实例对象
Student.prototype.constructor = Student; // 子类原型的构造函数再指回子类

let s1 = new Student("张三", 20, 100);
s1.getName(); // 我的名字undefined
s1.getAge(); // 我的名字undefined

3 组合式继承

组合式继承就是构造函数继承和类式继承的结合,它的问题就是类式继承拥有的问题。

示例代码:定义Person类(父类),它有自己的属性name和age,还有自己的方法getName()和getAge()

function Person(name, age) {
    this.name = name; // 初始化姓名和年龄
    this.age = age;
}
Person.prototype.getName = function () { // 获得姓名
    console.log("我的名字" + this.name);
}
Person.prototype.getAge = function () { // 获得年龄
    console.log("我的年龄:" + this.age);
}

将构造函数式继承和类式继承结合起来实现子类:

function Student(name, age, grade) {
    Person.call(this, name, age);
    this.grade = grade;
}
Student.prototype = new Person(); // 子类的原型指向父类的实例对象
Student.prototype.constructor = Student; // 子类原型的构造函数再指回子类

它的问题如下:
①类的原型对象上出现了父类的构造函数和方法
②多执行了一次构造函数

4 寄生式继承

在一个继承方法中,创建一个寄生类,让寄生类的原型,等于父类的原型,再实例化寄生类,赋值给子类的原型,这叫做寄生式继承。

寄生类代码如下:

function inherit(child, parent) { // 定义寄生类
    function F() {
        this.constructor = child; // 修改构造函数
    };
    F.prototype = parent.prototype; // 寄生类的原型等于父类的原型
    child.prototype = new F(); // 寄生类示例为子类复制
    return child;
}

那么接下来使用一下寄生类,首先定义父类和子类,并且将子类和父类传入寄生类:

// 父类
function Person(name, age) {
    this.name = name; // 初始化姓名和年龄
    this.age = age;
}
Person.prototype.getName = function () { // 获得姓名
    console.log("我的名字" + this.name);
}
Person.prototype.getAge = function () { // 获得年龄
    console.log("我的年龄:" + this.age);
}

// 子类
function Student(name, age, grade) {
    this.name = name;
    this.age = age;
    this.grade = grade;
}

inherit(Student, Person); // 继承

这时创建一个子类的实例对象,查看子类的实例对象,我们发现它解决了组合式继承的问题:①类的原型对象上出现了父类的构造函数和方法;②多执行了一次构造函数

let s1 = new Student("张三", 20, 100);
console.log(s1);

在这里插入图片描述
但是它的问题是无法复用构造函数中存储属性的逻辑。与类式继承第③条问题相同。

5 寄生组合式继承

寄生组合式继承就是综合使用构造函数式继承和寄生式继承。在子类中调用一下父类的构造函数。

示例代码:

function inherit(child, parent) { // 定义寄生类
    function F() {
        this.constructor = child; // 修改构造函数
    };
    F.prototype = parent.prototype; // 寄生类的原型等于父类的原型
    child.prototype = new F(); // 寄生类示例为子类复制
    return child;
}

function Person(name, age) {
    this.name = name; // 初始化姓名和年龄
    this.age = age;
}
Person.prototype.getName = function () { // 获得姓名
    console.log("我的名字" + this.name);
}
Person.prototype.getAge = function () { // 获得年龄
    console.log("我的年龄:" + this.age);
}

function Student(name, age, grade) {
    Person.call(this, name, age);
    this.grade = grade;
}

inherit(Student, Person);

寄生组合式继承是ES6之前最好的继承方式了,ES6出现之后使用class关键字来声明一个类,用extends关键字来继承父类。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JavaScript 实现继承方法有以下几种方式: 1. 原型继承:利用原型链实现继承。通过将父类的实例作为子类的原型,子类就可以访问到父类的属性和方法。例如: ```javascript function Parent() { this.name = 'parent'; } Parent.prototype.sayName = function() { console.log(this.name); } function Child() {} Child.prototype = new Parent(); var child = new Child(); child.sayName(); // 输出:parent ``` 2. 构造函数继承:利用 call 或 apply 方法将父类构造函数的作用域赋给子类。这种方式可以实现继承。例如: ```javascript function Parent(name) { this.name = name; } function Child(name) { Parent.call(this, name); } var child = new Child('child'); console.log(child.name); // 输出:child ``` 3. 组合继承:结合原型继承和构造函数继承,既可以继承父类的属性和方法,又可以实现子类实例的独立。例如: ```javascript function Parent(name) { this.name = name; } Parent.prototype.sayName = function() { console.log(this.name); } function Child(name, age) { Parent.call(this, name); this.age = age; } Child.prototype = new Parent(); Child.prototype.constructor = Child; var child = new Child('child', 18); console.log(child.name); // 输出:child console.log(child.age); // 输出:18 child.sayName(); // 输出:child ``` 4. 原型式继承:利用 Object.create() 方法创建一个新对象,以某个对象为原型,然后再对新对象进行修改。例如: ```javascript var parent = { name: 'parent', sayName: function() { console.log(this.name); } }; var child = Object.create(parent, { name: { value: 'child' } }); child.sayName(); // 输出:child ``` 5. 寄生式继承:与原型式继承类似,但是在增强对象的方法时使用了一个函数封装。例如: ```javascript var parent = { name: 'parent', sayName: function() { console.log(this.name); } }; function createChild(original) { var child = Object.create(original); child.sayName = function() { console.log('hello, ' + this.name); } return child; } var child = createChild(parent); child.sayName(); // 输出:hello, parent ``` 需要注意的是,在实现继承时应该注意避免出现属性和方法的重复定义,以及避免在父类的原型上修改引用类型的属性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值