JavaScript实现继承的7种方式总结

yizhihongxing

当需要实现JavaScript继承时,可以使用以下七种方式:

一、原型链继承

  1. 将父类的实例作为子类的原型
  2. 优点:父类的属性和方法能够被继承
  3. 缺点:
    • 无法传递参数
    • 所有子类实例共享父类引用类型属性,容易影响其他子类实例

示例代码:

// 父类
function Parent (name) {
  this.name = name;
}

// 父类的方法
Parent.prototype.sayName = function() {
  console.log('My name is ' + this.name);
};

// 子类
function Child() {}

// 子类的原型指向父类的实例,实现继承
Child.prototype = new Parent('Jack');

// 子类的实例
var child = new Child();
child.sayName(); // 输出"My name is Jack"

二、借用构造函数继承(经典继承)

  1. 在子类构造函数中调用父类构造函数,使用apply或call改变this的指向
  2. 优点:
    • 可继承父类的属性和方法
    • 父类的引用类型属性不会在子类间共享
  3. 缺点:子类不能访问到父类原型上的方法

示例代码:

// 父类
function Parent(name){
  this.name = name;
  this.colors = ['red', 'green', 'blue'];
}

// 子类,继承父类的colors属性
function Child(name, age){
  Parent.call(this, name);
  this.age = age;
}

// 子类实例
var child1 = new Child('A', 18);
var child2 = new Child('B', 20);

// 修改child1的引用类型属性,不影响child2
child1.colors.push('yellow');
console.log(child1.colors); // 输出 ["red", "green", "blue", "yellow"]
console.log(child2.colors); // 输出 ["red", "green", "blue"]

三、组合继承

  1. 结合原型链继承和构造函数继承的优点
  2. 父类构造函数继承属性,子类的原型继承方法
  3. 缺点:构造函数执行两次,父类指定的属性和方法会被子类实例化两次

示例代码:

// 父类
function Parent(name){
  this.name = name;
  this.colors = ['red', 'green', 'blue'];
}

// 父类的方法
Parent.prototype.sayName = function() {
  console.log('My name is ' + this.name);
};

// 子类,借用构造函数继承父类的属性,使用原型链继承父类的方法
function Child(name, age){
  Parent.call(this, name);
  this.age = age;
}

Child.prototype = new Parent();

// 子类的方法
Child.prototype.sayAge = function() {
  console.log('I am ' + this.age + ' years old.');
};

// 子类的实例
var child1 = new Child('A', 18);
var child2 = new Child('B', 20);

// 修改child1的引用类型属性,不影响child2
child1.colors.push("yellow");
console.log(child1.colors); // 输出 ["red", "green", "blue", "yellow"]
console.log(child2.colors); // 输出 ["red", "green", "blue"]

child1.sayName(); // 输出 "My name is A"
child1.sayAge();  // 输出 "I am 18 years old."

四、原型式继承

  1. 利用原型可以指向一个对象的特点,复制原对象,返回一个新的对象
  2. 缺点:引用类型会被子类共享

示例代码:

// 原对象
var person = {
  name: "Tom",
  colors: ["red", "blue", "green"]
};

// 继承方法
function object(o){
  function F(){}
  F.prototype = o;
  return new F();
}

// 子类,通过复制原对象实现继承
var child1 = object(person);
var child2 = object(person);

// 修改child1的引用类型属性,影响child2
child1.colors.push("yellow");
console.log(child1.colors); // 输出 ["red", "blue", "green", "yellow"]
console.log(child2.colors); // 输出 ["red", "blue", "green", "yellow"]

五、寄生式继承

  1. 在原型式继承的基础上,增强对象,返回一个新的对象
  2. 缺点:引用类型会被子类共享

示例代码:

// 原对象
var person = {
  name: "Tom",
  colors: ["red", "blue", "green"]
};

// 继承方法
function object(o){
  function F(){}
  F.prototype = o;
  return new F();
}

// 增强对象方法
function createAnother(o){
  var clone = object(o);
  clone.sayName = function(){
    console.log("My name is " + this.name);
  }
  return clone;
}

// 子类,通过增强对象的方法实现继承
var child1 = createAnother(person);
var child2 = createAnother(person);

// 修改child1的引用类型属性,影响child2
child1.colors.push("yellow");
console.log(child1.colors); // 输出 ["red", "blue", "green", "yellow"]
console.log(child2.colors); // 输出 ["red", "blue", "green", "yellow"]

child1.sayName(); // 输出 "My name is Tom"

六、寄生组合式继承

  1. 在组合继承的基础上,将父类原型实例化的过程插入到创建子类的构造函数中
  2. 缺点:无

示例代码:

// 父类
function Parent(name){
  this.name = name;
  this.colors = ["red", "blue", "green"];
}

// 父类的方法
Parent.prototype.sayName = function(){
  console.log("My name is " + this.name);
}

// 继承方法
function object(o){
  function F(){}
  F.prototype = o;
  return new F();
}

function inheritPrototype(subType, superType){
  var prototype = object(superType.prototype);      // 创建父类原型的一个副本
  prototype.constructor = subType;                   //这个副本的构造器指向子类,这样就可以继承父类原型中的方法了。
  subType.prototype = prototype;                     // 修正子类的原型对象
}

// 子类,通过组合式继承实现继承
function Child(name, age){
  Parent.call(this, name);
  this.age = age;
}

inheritPrototype(Child, Parent);

// 子类的方法
Child.prototype.sayAge = function(){
  console.log("I am " + this.age + " years old.");
}

// 子类的实例
var child1 = new Child("Tom", 18);
var child2 = new Child("Jerry", 20);

// 修改child1的引用类型属性,不影响child2
child1.colors.push("yellow");
console.log(child1.colors); // 输出 ["red", "blue", "green", "yellow"]
console.log(child2.colors); // 输出 ["red", "blue", "green"]

child1.sayName(); // 输出 "My name is Tom"
child1.sayAge();  // 输出 "I am 18 years old."

七、ES6 class 继承

  1. 使用关键字class和关键字extends实现继承
  2. 缺点:需先精通面向对象编程,但是它简化了构造函数继承和原型继承的写法,且具有清晰、美观的语法

示例代码:

// 父类
class Parent{
  constructor(name){
    this.name = name;
  }

  // 父类的方法
  sayName(){
    console.log("My name is " + this.name);
  }
}

// 子类,通过ES6 class实现继承
class Child extends Parent{
  constructor(name, age){
    super(name);
    this.age = age;
  }

  // 子类的方法
  sayAge(){
    console.log("I am " + this.age + " years old.");
  }
}

// 子类的实例
var child1 = new Child("Tom", 18);
var child2 = new Child("Jerry", 20);

// 修改child1的引用类型属性,不影响child2
child1.colors.push("yellow");
console.log(child1.colors); // 输出 ["red", "blue", "green", "yellow"]
console.log(child2.colors); // 输出 ["red", "blue", "green"]

child1.sayName(); // 输出 "My name is Tom"
child1.sayAge();  // 输出 "I am 18 years old."

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:JavaScript实现继承的7种方式总结 - Python技术站

(0)
上一篇 2023年6月26日
下一篇 2023年6月26日

相关文章

  • Android简单使用PopupWindow的方法

    Android简单使用PopupWindow的方法攻略 PopupWindow是Android中常用的弹出窗口控件,可以在屏幕上方或下方显示一个浮动的视图。下面是使用PopupWindow的详细步骤和示例说明。 步骤一:创建PopupWindow布局文件 首先,我们需要创建一个PopupWindow的布局文件。在res/layout目录下创建一个名为popu…

    other 2023年8月25日
    00
  • 用PHP的socket实现客户端到服务端的通信实例详解

    标题:用PHP的socket实现客户端到服务端的通信实例详解 正文: 简介 在网络通信中,Socket是一种基于TCP/IP协议进行通信的一种方式,常用于实现网络通信的功能。在 PHP 中,我们可以使用 Socket 扩展库来实现 Socket 的通信,从而提供了一种实现客户端和服务端之间通信的方法。 步骤 1.创建Socket: 在使用 Socket 进行…

    other 2023年6月27日
    00
  • Kotlin面向对象知识点讲解

    Kotlin面向对象知识点讲解 在Kotlin中,面向对象编程是一个非常重要的概念。本文将介绍Kotlin中的面向对象基础知识,及其用法和示例。 定义类 在Kotlin中,我们可以通过使用class关键字来定义一个类。下面是定义一个名为Person的类的示例: class Person(val name: String, var age: Int) { fu…

    other 2023年6月26日
    00
  • MyBatis-plus实现逆向生成器

    MyBatis-plus实现逆向生成器攻略 简介 MyBatis-plus是一个强大的Java持久层框架,提供了逆向生成器(Reverse Engineering Generator)功能,可以根据数据库表结构自动生成实体类、Mapper接口、Service接口、Controller等代码,极大地提高了开发效率。 使用步骤 以下是使用MyBatis-plus…

    other 2023年10月12日
    00
  • vue工程师必会封装的埋点指令思路知识总结

    下面是关于“vue工程师必会封装的埋点指令思路知识总结”的攻略。 什么是埋点? 在网络应用中,为了更好的分析用户行为和优化用户体验,通常会在应用中插入一些代码(通常是JavaScript代码),以记录用户的一些操作和行为。这个过程就是所谓的埋点。 为什么需要使用埋点? 使用埋点,可以帮助我们更好的了解用户的行为,提高产品体验和效果。比如,我们可以统计用户的浏…

    other 2023年6月25日
    00
  • mysql中如何判断当前是字符 mysql判断字段中有无汉字

    在MySQL中,可以使用正则表达式来判断当前字段中是否包含汉字,具体操作如下: 安装MySQL正则表达式插件 由于MySQL中默认不支持使用正则表达式,因此需要安装相应的插件。可以通过以下命令进行安装: sudo apt-get install libmysqlclient-dev libmysqludf-regexp-dev 加载正则表达式插件 安装完成后…

    other 2023年6月25日
    00
  • C++类继承之子类调用父类的构造函数的实例详解

    C++类继承之子类调用父类的构造函数的实例详解 在C++的类继承中,派生类可以通过调用基类的构造函数来完成对基类部分的初始化,这个过程叫做子类调用父类的构造函数。本文将会详细讲解如何在C++继承中实现子类调用父类的构造函数,包括示例说明和实际应用场景。 子类如何调用父类的构造函数 子类调用父类的构造函数一般通过子类的初始化列表来完成。子类初始化列表中使用基类…

    other 2023年6月26日
    00
  • Java基础之不简单的数组

    Java基础之不简单的数组:完整攻略 1. 数组的定义 Java中的数组是一种数据结构,用于存储相同类型的数据。数组定义时需要指定数据类型和长度,数组长度不能被改变。 // 定义int类型长度为3的数组 int[] nums = new int[3]; // 定义String类型长度为2的数组 String[] names = new String[2]; …

    other 2023年6月25日
    00
合作推广
合作推广
分享本页
返回顶部