>  기사  >  웹 프론트엔드  >  JavaScript 프로토타입에서 상속을 구현하는 방법 소개

JavaScript 프로토타입에서 상속을 구현하는 방법 소개

青灯夜游
青灯夜游앞으로
2020-11-19 18:10:303482검색

JavaScript 프로토타입에서 상속을 구현하는 방법 소개

이 기사에서는 프로토타입과 이를 JS 상속에 사용하는 방법에 대해 설명합니다. 또한 프로토타입 메서드가 클래스 기반 상속과 어떻게 다른지 살펴보겠습니다.

상속

상속은 객체 지향 프로그래밍 언어의 도입과 함께 등장한 프로그래밍 언어의 특징입니다. 이러한 언어의 대부분은 클래스 기반 언어입니다. 여기서 클래스는 청사진과 같고 객체는 해당 표현입니다. 즉, 객체를 생성하려면 먼저 클래스를 생성해야 하며, 그런 다음 클래스에서 원하는 만큼의 객체를 생성할 수 있습니다.

스마트폰을 나타내는 클래스가 있다고 상상해 보세요. 이 클래스에는 다른 스마트폰과 마찬가지로 사진 촬영, GPS 위치 확인 등의 기능이 있습니다. 다음은 C++를 사용하는 이러한 클래스에 대한 설명입니다.

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

SmartPhone x;
x.captureImages()

저희는 사진 촬영을 위한 capturePictures라는 메서드가 있는 SmartPhone이라는 클래스를 만들었습니다. SmartPhone的类,它有一个名为capturePictures的方法用来拍照。

如果我们需要一个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]]

이미지와 얼굴 ID 스캔과 같은 일부 특수 기능을 캡처할 수 있는 iPhone 클래스가 필요한 경우. 가능한 두 가지 해결 방법은 다음과 같습니다.

  • 새 클래스에서 이미지 캡처 기능을 다시 작성합니다. 그러나 이 접근 방식에는 더 많은 시간과 노력이 필요하고 더 많은 버그가 발생합니다. 🎜
  • SmartPhone 클래스의 함수를 재사용합니다. 상속은 다른 클래스/객체의 함수를 재사용하는 방법이기도 합니다.
🎜다음은 C++를 사용하여 SmartPhone 클래스에서 capturePictures 메서드를 상속하는 방법입니다. 🎜
console.log(Object.getPrototypeOf(phone) === SmartPhone.prototype);
// true
🎜위는 간단한 상속입니다. 예 . 그러나 상속을 통해 결과 프로그램의 오류 가능성을 줄이고 개발 시간을 단축하는 방식으로 코드를 재사용할 수 있음을 보여줍니다. 🎜🎜다음은 클래스에 대한 몇 가지 중요한 정보입니다: 🎜
  • 기능을 상속하는 클래스를 하위 클래스라고 합니다.
  • 상속되는 클래스를 상위 클래스라고 합니다.
  • A 클래스는 동시에 여러 클래스에서 상속할 수 있습니다.
  • 여러 상속 수준을 가질 수 있습니다. 예를 들어 C 클래스는 B 클래스를 상속하고, B 클래스는 A 클래스를 상속합니다.
🎜클래스 자체는 아무것도 하지 않는다는 점에 주목할 필요가 있습니다. 클래스에서 객체가 생성될 때까지 실제로 작업이 수행되지 않습니다. JavaScript와 왜 다른지 살펴보겠습니다. 🎜🎜프로토타입이란 무엇인가요? 🎜🎜JS에서 모든 개체에는 기본적으로 다른 개체에 대한 참조인 특별한 내부 속성이 있습니다. 이 참조는 객체가 생성된 방식에 따라 달라집니다. ECMAScript/JavaScript 사양에서는 [[Prototype]]으로 표현됩니다. 🎜🎜[[Prototype]]이 개체에 연결되어 있으므로 개체에는 자체 [[Prototype]] 참조가 있습니다. 이것이 프로토타입 체인을 구축하는 방법입니다. 🎜🎜이 [[Prototype]] 체인은 JS 상속의 구성 요소입니다. 🎜🎜__proto__ 객체 🎜🎜객체의 [[Prototype]]에 액세스하기 위해 대부분의 브라우저는 __proto__ 속성을 ​​제공합니다. 액세스 방법은 다음과 같습니다. 🎜
function ObjectA() {
  this.methodA = function () {}
}

let firstObj = new ObjectA()
console.log(firstObj)
🎜참고 이 속성은 ECMAScript 표준의 일부가 아니며 실제로 브라우저에 의해 구현됩니다. 🎜🎜프로토타입 메서드 가져오기 및 설정🎜🎜 __proto__ 속성 외에도 [[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]]을 설정하는 방법: 🎜
let obj = {}
🎜[[Prototype]].prototype 속성 🎜🎜[ [Prototype]]은 객체의 프로토타입을 나타내는 데 사용되는 표준 기호에 지나지 않습니다. 많은 개발자가 이를 .prototype 속성과 혼동하는데 이는 완전히 다른 것입니다. .prototype 속성을 ​​살펴보겠습니다. 🎜🎜JS에서는 객체를 생성하는 방법이 다양합니다. 한 가지 방법은 생성자를 사용하는 것입니다. 다음과 같이 new 키워드를 사용하여 호출합니다. 🎜
let obj = new Object();
🎜콘솔에서 phone 개체를 인쇄합니다. 🎜
let SmartPhone = {
  captureImages: function() {}
}

let Iphone = Object.create(SmartPhone)

Iphone.captureImages()
🎜이제 원하는 경우 phone 개체에는 몇 가지 메서드가 있으며 다음과 같이 함수의 .prototype 속성을 ​​사용할 수 있습니다. 🎜
function SmartPhone(os) {
  this.os = os;
}

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

SmartPhone.prototype.isIOS = function() {
  return this.os === 'iOS';
};
🎜phone 개체 만들기 다시 phone 개체를 인쇄하면 다음과 같습니다. 🎜
function Iphone() {
   SmartPhone.call(this, 'iOS');
}
🎜 개체의 [[Prototype]]에서 isAndroid() 메서드를 볼 수 있습니다. 🎜🎜간단히 말하면, .prototype 속성은 기본적으로 주어진 생성자에 의해 생성된 [[Prototype]] 객체에 대한 청사진과 같습니다. .prototype 속성/객체에 선언된 모든 항목은 객체의 [[Prototype]]에 나타납니다. 🎜🎜실제로 SmartPhone.prototype을 휴대폰의 [[Prototype]]과 비교해 보면 둘이 동일하다는 것을 알 수 있습니다. 🎜
Iphone.prototype = Object.create(SmartPhone.prototype);
🎜 주목할 가치가 있습니다. 생성자에서 메소드를 생성할 수도 있습니다: 🎜
function ObjectA() {
  this.methodA = function () {}
}

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

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

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

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

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,我们将看到以下内容:

JavaScript 프로토타입에서 상속을 구현하는 방법 소개

基本上,所有用文字面量创建的对象都继承了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

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

위 내용은 JavaScript 프로토타입에서 상속을 구현하는 방법 소개의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 segmentfault.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제