>웹 프론트엔드 >프런트엔드 Q&A >10분 안에 JavaScript 상속을 이해해 보세요.

10분 안에 JavaScript 상속을 이해해 보세요.

WBOY
WBOY앞으로
2022-01-18 17:47:091244검색

이 기사는 프로토타입 체인 상속, 빌린 생성자 상속, 결합 상속 및 다중 상속을 포함하여 JavaScript의 상속에 대한 관련 지식을 제공합니다.

10분 안에 JavaScript 상속을 이해해 보세요.

프로토타입 체인 상속

Principle

핵심은 프로토타입 객체를 다시 작성하고 이를 새로운 유형의 인스턴스로 바꾸는 것입니다. 다음 코드에서는 원래 SuperType 인스턴스 객체에 있던 속성과 메서드가 이제 SubType.prototype에도 존재합니다.

Implementation

function Super(){
    this.value = true;
}
Super.prototype.getValue = function(){
    return this.value
}
function Sub(){};
// Sub继承了Super
Sub.prototype = new Super();
Sub.prototype.constructor = Sub;
const ins = new Sub();
console.log(ins.getValue()); // true

Sub는 Super를 상속하며 상속은 Super 인스턴스를 생성하고 인스턴스를 Sub.prototype에 할당하여 달성됩니다. 원래 Super 인스턴스에 존재했던 모든 속성과 메서드는 이제 Sub.prototype에도 존재합니다. 그림과 같습니다.

10분 안에 JavaScript 상속을 이해해 보세요.

위 그림에서 볼 수 있듯이 Sub가 기본적으로 제공하는 프로토타입은 사용되지 않지만 새로운 프로토타입이 제공됩니다. 이 새로운 프로토타입은 Super의 인스턴스입니다. 따라서 새 프로토타입은 Super 인스턴스의 속성과 메서드를 가질 뿐만 아니라 Super의 프로토타입을 가리킵니다. 최종 결과는 다음과 같습니다.

ins=>Sub的原型=>Super的原型

getValue() 메서드는 여전히 Sub.prototype에 있지만 value 속성은 Sub.prototype에 있습니다. 이는 value가 인스턴스 속성이고 getValue()가 프로토타입 메서드이기 때문입니다. Sub.prototype은 이제 Super의 인스턴스이므로 값은 해당 인스턴스에 있습니다.

또한 ins.constructor는 이제 Super를 가리킨다는 점에 유의하세요. 이는 Sub.prototype의 원래 생성자가 다시 작성되었기 때문입니다.

단점

  • 개인 프로토타입 속성은 인스턴스에서 공유됩니다.

  • 하위 유형의 인스턴스를 생성할 때 매개변수를 상위 유형의 생성자에 전달할 수 없습니다.

프로토타입 체인의 주요 문제 상속: 비공개 프로토타입 속성은 인스턴스에 의해 공유되므로 속성이 프로토타입 객체가 아닌 생성자에서 정의됩니다. 프로토타입을 통해 상속이 구현되면 프로토타입 인스턴스는 다른 클래스의 인스턴스가 됩니다. 따라서 원래 인스턴스 속성은 자연스럽게 현재 프로토타입 속성이 되었습니다.

function Super(){
    this.colors = ['red','green','blue'];
}
Super.prototype.getValue = function(){
    return this.colors
}
function Sub(){};
//Sub继承了Super
Sub.prototype = new Super();
const ins1 = new Super();
ins1.colors.push('black');
console.log(ins1.colors);//['red','green','blue','black'];
const ins2 = new Sub();
console.log(ins2.colors);//['red','green','blue','black'];

프로토타입 체인의 두 번째 문제는 하위 유형의 인스턴스를 생성할 때 매개변수를 상위 유형의 생성자에 전달할 수 없다는 것입니다. 실제로 모든 인스턴스에 영향을 주지 않고 상위 유형의 생성자에 매개변수를 전달할 수 있는 방법은 없습니다. 참조 유형 값을 포함하는 프로토타입 속성이 모든 인스턴스에서 공유된다는 문제와 함께 실제로 프로토타입 체인 상속은 단독으로 사용되는 경우가 거의 없습니다. 때로는 부모 클래스의 메서드를 재정의하거나 부모 클래스에 없는 메서드를 추가해야 하는 경우도 있습니다. 그러나 무슨 일이 있어도 프로토타입에 메서드를 추가하는 코드는 프로토타입을 대체하는 명령문 뒤에 배치되어야 합니다.

function Super() {
    this.colors = ['red', 'green', 'blue'];
}
Super.prototype.getValue = function() {
    return this.colors
}
function Sub() {
    this.colors = ['black'];
};
//Sub继承了Super
Sub.prototype = new Super();
//添加父类已存在的方法,会重写父类的方法
Sub.prototype.getValue = function() {
    return this.colors;
}
//添加父类不存在的方法
Sub.prototype.getSubValue = function(){
    return false;
}
const ins = new Sub();
//重写父类的方法之后得到的结果
console.log(ins.getValue()); //['black']
//在子类中新定义的方法得到的结果
console.log(ins.getSubValue());//false
//父类调用getValue()方法还是原来的值
console.log(new Super().getValue());//['red', 'green', 'blue']

빌린 생성자 상속

Principle빌린 생성자(가끔 의사 클래스 상속 또는 클래식 상속이라고도 함). 이 기술의 기본 아이디어는 매우 간단합니다. 즉, 하위 클래스 생성자 내부에서 상위 클래스 생성자를 호출하는 것입니다. 함수는 특정 환경에서 코드를 실행하는 객체일 뿐이므로 apply() 및 call() 메서드를 사용하여 새로 생성된 객체에서도 생성자를 실행할 수 있다는 점을 잊지 마세요.

구현

function Super() {
    this.colors = ['red', 'green', 'blue'];
}
Super.prototype.getValue = function(){
    return this.colors;
}
function Sub(){
//继承了Super
Super.call(this);//相当于把构造函数Super中的this替换成了ins实例对象,这样在Super只有定义的私有属性会被继承下来,原型属性中定义的公共方法不会被继承下来
}
const ins = new Sub();
console.log(ins.colors);

매개변수 전달: 프로토타입 체인과 비교할 때 생성자 상속 상속은 큰 이점이 있습니다. 즉, 하위 클래스 생성자의 상위 클래스 생성자에 매개변수를 전달할 수 있습니다.

function B(name){
    this.name = name;
}
function A(){
    //继承了B,同时还传递了参数
    B.call(this,'ZZ');
    //实例属性
    this.age = 100;
}
const p = new A();
alert(p.name);//'ZZ'
alert(p.age);//100

단점

If 생성자를 빌리기만 하면 생성자 패턴의 문제를 피할 수 없습니다. 메서드는 모두 생성자에 정의되어 있으므로 함수 재사용은 불가능합니다. 게다가 상위 클래스의 프로토타입에 정의된 메서드는 하위 클래스에 표시되지 않으므로 이 메서드는 거의 사용되지 않습니다.

결합 상속

Principle결합 상속은 프로토타입 체이닝과 차용 생성자 기술을 결합하여 두 가지의 장점을 모두 활용하는 상속 패턴을 의미합니다. 그 뒤에 있는 아이디어는 프로토타입 체인을 사용하여 프로토타입의 공개 속성과 메서드를 상속하고 생성자 상속을 빌려 부모 클래스의 비공개 속성을 상속하는 것입니다. 이러한 방식으로 함수 재사용은 부모 클래스 프로토타입에 메서드를 정의하여 달성되며 각 인스턴스는 부모 클래스의 비공개 속성을 갖도록 보장됩니다.

달성

function Super(name){
    this.name = name;
    this.colors = ['red','blue','green'];
}
Super.prototype.sayName = function(){
    alert(this.name);
}
function Sub(name,age){
    Super.call(this,name);
    this.age = age;
}
// 继承方法
Sub.prototype = new Super();
Sub.prototype.constructor = Sub;
Sub.prototype.sayAge = function(){
    alert(this.age);
}
const ins = new Sub('jarvis',18);
ins.colors.push('black');
console.log(ins.colors);// ["red", "blue", "green", "black"]
ins.sayName();//'jarvis'
ins.sayAge();//18
const ins2 = new Sub('ershiyi',21);
console.log(ins2.colors);//["red", "blue", "green"]
ins2.sayName();//'ershiyi'
ins2.sayAge();//21

在上个例子中,Sub构造函数定义了两个属性:name和age。Super的原型定义了一个sayName()方法。在Sub构造函数中调用Super构造函数时传入了name参数,紧接着又定义它自己的属性age。然后,将Super的实例赋值给Sub的原型,然后又在该新原型上定义了方法sayAge()。这样一来,就可以让不同的Sub实例分别拥有自己的属性——包括colors属性,又可以使用相同的方法组合继承避免了原型链和借用构造函数的缺陷,融合了他们的优点,称为JavaScript中最常用的继承模式。

缺点

无论在什么情况下,都会调用两次父类的构造函数:一次是在创建子类原型的时候,另一次是在子类构造函数内部。

寄生组合式继承

原理

组合继承是JavaScript最常用的继承模式;不过,它也有自己的不足。组合继承最大的问题就是无论什么情况下,都会调用两次父类构造函数:一次是在创建子类原型的时候,另一次是在子类构造函数内部。没错,子类型最终会包含超类型对象的全部实例属性,但不得不在调用子类型构造函数时重写这些属性。再来看一看下面组合继承的例子。

实现

function Super(name){
    this.name = name;
    this.colors = ['red','blue','green'];
}
Super.prototype.sayName = function(){
    alert(this.name);
}
function Sub(name,age){
    Super.call(this,name);
    this.age = age;
}
// 继承方法
Sub.prototype = new Super();
Sub.prototype.constructor = Sub;
Sub.prototype.sayAge = function(){
    alert(this.age);
}
const ins = new Sub('jarvis',18);
ins.colors.push('black');
console.log(ins.colors);// ["red", "blue", "green", "black"]
ins.sayName();//'jarvis'
ins.sayAge();//18
const ins2 = new Sub('ershiyi',21);
console.log(ins2.colors);//["red", "blue", "green"]
ins2.sayName();//'ershiyi'
ins2.sayAge();//21

所谓寄生组合式继承,即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。其背 后的基本思路是:不必为了指定子类型的原型而调用超类型的构造函数,所需要的无非就是超类型原型的一个副本而已。本质上,就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。寄生组合式继承的基本模式如下所示。

function Super(name){
    this.name = name;
    this.colors = ['red','blue','green'];
}
Super.prototype.sayName = function(){
    alert(this.name);
}
function Sub(name,age){
    //继承实例属性
    Super.call(this,name);
    this.age = age;
}
// 继承公有的方法
Sub.prototype = Object.create(Super.prototype);
Sub.prototype.constructor = Sub;
Sub.prototype.sayAge = function(){
    alert(this.age);
}
const ins = new Sub('jarvis',18);
ins.colors.push('black');
console.log(ins.colors);// ["red", "blue", "green", "black"]
ins.sayName();//'jarvis'
ins.sayAge();//18
const ins2 = new Sub('ershiyi',21);
console.log(ins2.colors);//["red", "blue", "green"]
ins2.sayName();//'ershiyi'
ins2.sayAge();//21

多重继承

JavaScript中不存在多重继承,那也就意味着一个对象不能同时继承多个对象,但是可以通过变通方法来实现。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>18 多重继承</title>
</head>
<body>
<script type="text/javascript">
// 多重继承:一个对象同时继承多个对象
// Person  Parent  Me
function Person(){
this.name = &#39;Person&#39;;
}
Person.prototype.sayName = function(){
console.log(this.name);
}
// 定制Parent
function Parent(){
this.age = 30;
}
Parent.prototype.sayAge = function(){
console.log(this.age);
}
function Me(){
// 继承Person的属性
Person.call(this);
Parent.call(this);
}
// 继承Person的方法
Me.prototype = Object.create(Person.prototype);
// 不能重写原型对象来实现 另一个对象的继承
// Me.prototype = Object.create(Parent.prototype);
// Object.assign(targetObj,copyObj)
Object.assign(Me.prototype,Parent.prototype);
// 指定构造函数
Me.prototype.constructor = Me;
const me = new Me();
</script>
</body>
</html>

ES5 与 ES6 继承差异

在 ES5 的传统继承中, this 的值会先被派生类创建,随后基类构造器才被调用。这意味着 this 一开始就是派生类的实例,之

后才使用了基类的附加属性对其进行了装饰。

在 ES6 基于类的继承中, this 的值会先被基类创建,随后才被派生类的构造 器所修改。结果是 this 初始就拥有作为基类的内置对象的所有功能,并能正确接收与之关联的所有功能。

【相关推荐:javascript学习教程

위 내용은 10분 안에 JavaScript 상속을 이해해 보세요.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

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