0

0

JavaScript原型实现继承的方法介绍

青灯夜游

青灯夜游

发布时间:2020-11-19 18:10:30

|

3720人浏览过

|

来源于segmentfault

转载

JavaScript原型实现继承的方法介绍

在这篇文章中,我们将讨论原型以及如何在 JS 中使用它们进行继承。我们还将会看到原型方法与基于类的继承有何不同。

继承

继承是编程语言的一个显著特征,随着面向对象编程语言的引入而出现。这些语言大多是基于类的语言。在这里,类就像一个蓝图,对象是它的展现形式。就是说,要创建一个对象,首先我们必须创建一个类,然后我们可以从一个类创建任意数量的对象。

想象一下,我们有一个表示智能手机的类。这个类具有像其他智能手机一样的可以拍照、有GPS定位等功能。下面是使用 c++ 来描述这样的一个类:

class SmartPhone {
  public:
  void captureImages() {}
}

SmartPhone x;
x.captureImages()

我们创建了一个名为SmartPhone的类,它有一个名为capturePictures的方法用来拍照。

立即学习Java免费学习笔记(深入)”;

如果我们需要一个iPhone类,它可以捕捉图像和一些特殊的功能,比如面部ID扫描。下面是两种可能的解决方案:

  • 将捕获图像功能与其他常见的智能手机功能,以及iPhone的特定功能一起重写到一个新类中。但是这种方法需要更多的时间和精力,并且会引入更多的bug。

  • 重用SmartPhone类中的功能,这就是继承的作用,继承也是重用其他类/对象中功能的一种方式。

这里是我们如何从SmartPhone类中继承capturePictures方法,使用 c++ 实现如下:

class Iphone: public SmartPhone {
  public:
  void faceIDScan() {}
}

Iphone x

x.faceIDScan()

x.captureImages()

上面是一个简单的继承示例。 但是,它表明继承可以使我们以某种方式重用代码,从而使所生成的程序更不易出错,并且花费更少的时间进行开发。

以下是关于类的一些重要信息:

  • 继承该功能的类称为子类
  • 被继承的类称为父类
  • 一个类可以同时从多个类中继承
  • 我们可以具有多个继承级别。 例如,类C继承自类B,而类B继承自类A

值得注意的是,类本身并没有做任何事情。在从类创建对象之前,实际上没有完成任何工作。我们将看到它为什么不同于JavaScript。

原型是什么?

在 JS 中,所有对象都有一个特殊的内部属性,该属性基本上是对另一个对象的引用。 此引用取决于对象的创建方式。 在 ECMAScript/JavaScript规范中,它表示为[[Prototype]]

由于[[Prototype]]链接到一个对象,所以该对象有自己的[[Prototype]]引用。这就是建立原型链的方式。

这个[[Prototype]]链是 JS 中继承的构建块。

__proto__ 对象

为了访问对象的[[Prototype]],大多数浏览器都提供__proto__属性。访问方式如下:

obj.__proto__

需要注意的是,这个属性不是 ECMAScript 标准的一部分,它实际上是由浏览器实现的。

获取和设置原型方法

除了__proto__属性外,还有一种访问[[Prototype]]的标准方法:

Object.getPrototypeOf(obj);

对应的有个类似的方法来设置对象的[[Prototype]]

Object.setPrototypeOf(obj, prototype);

[[Prototype]].prototype属性

[[Prototype]] 只不过是一种用来表示物体原型的标准符号。 许多开发人员将其与.prototype属性混淆,这是完全不同的事情,接着我们来研究一下.prototype属性。

在 JS 中,有许多创建对象的方法。一种方法是使用构造函数,像这样使用new关键字来调用它:

function SmartPhone(os) {
  this.os = os
}

let phone = new SmartPhone('Android')

在控制台打印 phone 对象:

{
  os: "IPhone"
  __proto__{
    constructor: ƒ SmartPhone(os)
   __proto__: Object
  }
}

现在,如果我们希望在phone对象上有一些方法,我们可以在函数上使用.prototype属性,如下所示:

SmartPhone.prototype.isAndroid = function () {
  return this.os === 'Android' || 'android'
}

再次创建phone对象时,打印 phone 对象如下:

{
  os: "Android"
  __proto__{
    isAndroid: ƒ()
    constructor: ƒ SmartPhone(os)
   __proto__: Object
  }
}

我们可以在对象的[[Prototype]]中看到isAndroid()方法。

简而言之,.prototype属性基本上就像由给定的构造函数创建的[[Prototype]]对象的蓝图。 在.prototype属性/对象中声明的所有内容都会在对象的[[Prototype]]中弹出。

实上,如果将 SmartPhone.prototype 与phone 的[[Prototype]]进行比较,就会发现它们是相同的:

console.log(Object.getPrototypeOf(phone) === SmartPhone.prototype);
// true

值得注意的是,我们还可以在构造函数中创建方法:

function ObjectA() {
  this.methodA = function () {}
}

let firstObj = new ObjectA()
console.log(firstObj)

这种方法的问题是当我们初始化一个新对象时。所有实例都有自己methodA的副本。相反,当我们在函数的原型上创建它时,对象的所有实例只共享方法的一个副本,显然使用原型的方式效率会过高。

当我们访问属性时这里发生了什么?

当我们访问一个属性以获取它时,会发生以下情况:

飞书多维表格
飞书多维表格

表格形态的AI工作流搭建工具,支持批量化的AI创作与分析任务,接入DeepSeek R1满血版

下载

JS 引擎查找对象上的属性,如果找到了该属性,然后返回它。否则,JS 引擎将通过查看[[Prototype]]来检查对象的继承属性,如果找到该属性,则返回它,否则,它会查找 [[Prototype]][[Prototype]]。 找到属性或没有[[Prototype]]时,该链结束,这意味着我们已经到达原型链的末端。

当我们设置/创建属性时,JS 总是在对象本身上进行设置。 即使[[Prototype]]链上存在相同的属性,下面是一个例子:

function MyObject() {}
MyObject.prototype.propA = 10; // 在原型上创建属性

let myObject = new MyObject();
console.log(myObject.propA); // [[Prototype]]上的属性
// 10

myObject.propA = 20; // 对象的属性
console.log(myObject.propA);
// 20

在上面的示例中,我们创建了一个构造函数,该函数的[[Prototype]]上具有属性propA。 当我们尝试对其进行读取操作时,会在控制台中看到该值。 但是,当我们尝试在对象本身上设置相同的属性时;JS 使用给定值在对象上创建一个新属性。 现在,如果我们不能直接访问[[Prototype]]上的属性。

值得注意的是,普通对象的[[Prototype]]链的末尾是内置的Object.prototype。 这就是为什么大多数对象共享许多方法(例如toString())的原因。 因为它们实际上是在Object.prototype上定义的。

使用原型继承的各种方法

在 JS 中,无论我们如何创建对象,只有原型继承,但这些方式还有一些区别,来看看:

对象字面量

在JavaScript中创建对象的最简单方法是使用对象字面量:

let obj = {}

如果在浏览器的控制台中打印obj,我们将看到以下内容:

1.png

基本上,所有用文字面量创建的对象都继承了Object.prototype的属性。

需要注意的是__proto__对象引用了创建它的构造函数。 在这种情况下,constructor属性指向Object构造函数。

使用对象构造函数

另一种不太常见的创建对象的方法是使用对象构造函数。JS 提供了一个名为Object的内置构造函数方法来创建对象。

let obj = new Object();

这种方法的结果与对象字面量的方式相同。它从Object.prototype继承属性。因为我们使用Object作为构造函数。

Object.create 方法

使用此辅助方法,我们可以创建一个带有[[Prototype]]的对象,如下所示:

let SmartPhone = {
  captureImages: function() {}
}

let Iphone = Object.create(SmartPhone)

Iphone.captureImages()

这是在 JS 中使用继承的最简单方法之一。猜猜我们如何在没有任何[[Prototype]]引用的情况下创建对象?

构造方法

与 JS 运行时提供的对象构造函数相似。 我们还可以创建自己的构造函数,以创建适合我们需求的对象,如下所示:

function SmartPhone(os) {
  this.os = os;
}

SmartPhone.prototype.isAndroid = function() {
  return this.os === 'Android';
};

SmartPhone.prototype.isIOS = function() {
  return this.os === 'iOS';
};

现在,我们想创建一个iPhone类,它应该有'iOS'作为它 os 属性的值。它还应该有faceIDScan方法。

首先,我们必须创建一个Iphone构造函数,在其中,我们应该调用SmartPhone构造函数,如下所示:

function Iphone() {
   SmartPhone.call(this, 'iOS');
}

这会将Iphone构造函数中的this.os属性设置为’iOS‘

之所以调用SmartPhone.call方法,是因为我们需要更改 this 值以引用Iphone。 这类似于在面向对象的世界中调用父级的构造函数。

接下来的事情是,我们必须从SmartPhone构造函数继承方法。 我们可以在此处使用Object.create朋友,如下所示:

Iphone.prototype = Object.create(SmartPhone.prototype);

现在,我们可以使用.prototypeIphone添加方法,如下所示:

Iphone.prototype.faceIDScan = function() {};

最后,我们可以使用Iphone创建一个对象,如下所示:

let x = new Iphone();

// calling inherited method
console.log(x.isIOS()):
// true

ES6 class

使用ES6,整个过程非常简单。 我们可以创建类(它们与C ++或其他任何基于类的语言中的类不同,只是在原型继承之上的语法糖),然后从其他类派生新的类。

下面是我们如何在ES6中创建类:

class SmartPhone {
  constructor(os) {
    this.os = os;
  }
  isAndroid() {
    return this.os === 'Android';
  }
  isIos() {
    return this.os === 'iOS';
  }
};

现在,我们可以创建一个派生自SmartPhone的新类,如下所示:

class Iphone extends SmartPhone {
   constructor() {
     super.call('iOS');
   }
   faceIDScan() {}
}

我们不是调用SmartPhone.call,而是调用super.call。 在内部,JavaScript引擎会自动为我们执行此操作。

最后,我们可以使用Iphone创建一个对象,如下所示

let x = new Iphone();

x.faceIDScan();

// calling inherited method
console.log(x.isIos()):
// true

该ES6示例与先前的构造方法示例相同。 但是阅读和理解起来要干净得多。

原文:https://javascript.info/prototype-inheritance作者:Indermohan Sing

更多编程相关知识,请访问:编程课程!!

相关文章

java速学教程(入门到精通)
java速学教程(入门到精通)

java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
es6新特性
es6新特性

es6新特性有:1、块级作用域变量;2、箭头函数;3、模板字符串;4、解构赋值;5、默认参数;6、 扩展运算符;7、 类和继承;8、Promise。本专题为大家提供es6新特性的相关的文章、下载、课程内容,供大家免费下载体验。

106

2023.07.17

es6新特性有哪些
es6新特性有哪些

es6的新特性有:1、块级作用域;2、箭头函数;3、解构赋值;4、默认参数;5、扩展运算符;6、模板字符串;7、类和模块;8、迭代器和生成器;9、Promise对象;10、模块化导入和导出等等。本专题为大家提供es6新特性的相关的文章、下载、课程内容,供大家免费下载体验。

197

2023.08.04

JavaScript ES6新特性
JavaScript ES6新特性

ES6是JavaScript的根本性升级,引入let/const实现块级作用域、箭头函数解决this绑定问题、解构赋值与模板字符串简化数据处理、对象简写与模块化提升代码可读性与组织性。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

233

2025.12.24

go语言 面向对象
go语言 面向对象

本专题整合了go语言面向对象相关内容,阅读专题下面的文章了解更多详细内容。

58

2025.09.05

java面向对象
java面向对象

本专题整合了java面向对象相关内容,阅读专题下面的文章了解更多详细内容。

63

2025.11.27

go语言 面向对象
go语言 面向对象

本专题整合了go语言面向对象相关内容,阅读专题下面的文章了解更多详细内容。

58

2025.09.05

java面向对象
java面向对象

本专题整合了java面向对象相关内容,阅读专题下面的文章了解更多详细内容。

63

2025.11.27

go语言 面向对象
go语言 面向对象

本专题整合了go语言面向对象相关内容,阅读专题下面的文章了解更多详细内容。

58

2025.09.05

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

76

2026.03.11

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
React 教程
React 教程

共58课时 | 6万人学习

TypeScript 教程
TypeScript 教程

共19课时 | 3.4万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 3.6万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号