>웹 프론트엔드 >JS 튜토리얼 >JavaScript의 프로토타입 체인과 상속에 대해 간단한 용어로 자세히 설명합니다.

JavaScript의 프로토타입 체인과 상속에 대해 간단한 용어로 자세히 설명합니다.

黄舟
黄舟원래의
2017-03-03 15:25:561176검색

Javascript 언어의 상속 메커니즘에는 "하위 클래스"와 "상위 클래스"의 개념이 없으며 "클래스"와 "인스턴스"의 구분이 없으며 모두 하나의 A에 의존합니다. 상속을 구현하는 매우 독특한 "프로토타입 체인" 모델입니다.

이 부분의 지식도 자바스크립트의 핵심 포인트 중 하나이자, 어려운 점이기도 합니다. 나는 모든 사람의 공부를 용이하게 하고 나 자신의 인상을 깊게 하기 위해 공부 노트를 정리했습니다. 코드의 이 부분에는 많은 세부 사항이 있으며 반복적으로 고려해야 합니다. 그럼 시작해 보겠습니다.

시리즈 목차

  • 자바스크립트의 클로저(Closure)를 간단하게 소개

  • 이 심층 설명 of JavaScript

  • 자바스크립트의 프로토타입 체인과 상속에 대한 심층 소개

작은 시도

프로토타입 체인의 예 (핵심 사항은 주석에 기록되어 있습니다. 코드를 복사하여 장치에서 테스트됨을 찾아볼 수 있습니다. 아래와 동일합니다.)

function foo(){}              //通过function foo(){}定义一个函数对象
foo.prototype.z = 3;          //函数默认带个prototype对象属性   (typeof foo.prototype;//"object")

var obj =new foo();           //我们通过new foo()构造器的方式构造了一个新的对象
obj.y = 2;                    //通过赋值添加两个属性给obj
obj.x = 1;                    //通过这种方式构造对象,对象的原型会指向构造函数的prototype属性,也就是foo.prototype

obj.x; // 1                 //当访问obj.x时,发现obj上有x属性,所以返回1
obj.y; // 2                 //当访问obj.y时,发现obj上有y属性,所以返回2
obj.z; // 3                 //当访问obj.z时,发现obj上没有z属性,那怎么办呢?它不会停止查找,它会查找它的原型,也就是foo.prototype,这时找到z了,所以返回3

//我们用字面量创建的对象或者函数的默认prototype对象,实际上它也是有原型的,它的原型指向Object.prototype,然后Object.prototype也是有原型的,它的原型指向null。
                                   //那这里的Object.prototype有什么作用呢?
typeof obj.toString; // ‘function'  

//我们发现typeof obj.toString是一个函数,但是不管在对象上还是对象的原型上都没有toString方法,因为在它原型链的末端null之前都有个Object.prototype方法,
//而toString正是Object.prototype上面的方法。这也解释了为什么JS基本上所有对象都有toString方法
'z' in obj; // true               //obj.z是从foo.prototype继承而来的,所以'z' in obj返回了true
obj.hasOwnProperty('z'); 
// false   
//但是obj.hasOwnProperty('z')返回了false,表示z不是obj直接对象上的,而是对象的原型链上面的属性。(hsaOwnProperty也是Object.prototype上的方法)

방금 x, y 및 z 및 프로토타입 체인을 통해 각각 검색하면 객체의 특정 속성에 액세스할 때 객체에 해당 속성이 없으면 프로토타입 체인을 통해 위쪽으로 검색할 것입니다. 정의되지 않은 상태로 반환됩니다.

프로토타입 기반 상속

function Foo(){
   this.y = 2;     
}

Foo.prototype.x = 1;
var obj3 = new Foo();  
//①当使用new去调用的时候,函数会作为构造器去调用②this会指向一个对象(这里是obj3),而这个对象的原型会指向构造器的prototype属性(这里是Foo.prototype)
obj3.y; //2 
obj3.x; //1    //可以看到y是对象上的,x是原型链上的原型(也就是Foo.prototype上)

프로토타입 속성 및 프로토타입

Foo.prototype이 어떤 구조인지 살펴보겠습니다. 함수 선언을 사용하여 빈 함수를 만들면 이 함수에는 프로토타입 속성이 있고 기본적으로 생성자 및 _ _proto__라는 두 가지 속성이 있습니다.

생성자 속성은 Foo 자신을 가리키고, __proto__는 크롬에 노출되며(표준 속성이 아니라 그냥 알아두세요) Foo.prototype의 프로토타입은 Object.prototype을 가리킵니다. 따라서 Object.prototype에 있는

의 일부 toString 및 valueOf 메소드는 모든 일반 객체에서 사용됩니다.

function Foo(){}
typeof Foo.prototype; // "object"
Foo.prototype.x = 1;
var obj3 = new Foo();

여기에 Foo 함수가 있습니다. 이 함수에는 프로토타입 객체 속성이 있습니다. new Foo()를 사용하여 인스턴스를 생성할 때 이 생성자의 프로토타입은 다음과 같습니다. 이러한 새로운 개체의 프로토타입으로 사용됩니다.

그래서 우리는 프로토타입과 프로토타입이 서로 다른 것임을 이해해야 합니다. Prototype은 함수 객체의 기본 속성이고 프로토타입은 일반적으로 생성자의 프로토타입 속성입니다.

다른 클래스를 상속받은 클래스 구현

function Person(name, age) {
   this.name = name;    
   //直接调用的话,this指向全局对象(this知识点整理)
   this.age = age;      
   //使用new调用Peoson的话,this会指向原型为Person.prototype的空对象,通过this.name给空对象赋值,最后this作为return值
}

Person.prototype.hi = function() {   
//通过Person.prototype.hi创建所有Person实例共享的方法,(可以参考上节的左图:对象的原型会指向构造器的prototype属性,所以想让obj1,obj2,obj3共享一些方法的话,只需在原型对象上一次性地添加属性和方法就可以了);
   console.log('Hi, my name is ' + this.name + ',I am ' + this.age + ' years old now.')//这里的this是全局对象
};

Person.prototype.LEGS_NUM = 2;   //再设置一些对Person类的所有实例共享的数据
Person.prototype.ARMS_NUM = 2;
Person.prototype.walk = function() {
  console.log(this.name + ' is walking...');
};

function Student(name, age, className) {  //每个学生都属于人
  Person.call(this, name, age);  //在Student这个子类里面先调用一下父类
  this.className = className;
}

//下一步就是我们怎么去把Student的实例继承Person.prototype的一些方法

Student.prototype = Object.create(Person.prototype);    
//Object.create():创建一个空对象,并且这个对象的原型指向它的参数  
//这样子我们可以在访问Student.prototype的时候可以向上查找到Person.prototype,又可以在不影响Person的情况下,创建自己的方法
Student.prototype.constructor = Student;  
//保持一致性,不设置的话constructor会指向Person

Student.prototype.hi = function() {    
//通过Student.prototype.hi这样子的赋值可以覆盖我们基类Person.prototype.hi
  console.log('Hi, my name is ' + this.name + ',I am ' + this.age + ' years old now, and from ' + this.className + '.');
}
Student.prototype.learn = function(subject) {    
//同时,我们又有自己的learn方法
  console.log(this.name + 'is learning ' + subject + ' at' + this.className + '.');
};

//test
var yun = new Student('Yunyun', 22, 'Class 3,Grade 2');
yun.hi(); //Hi,my name is Yunyun,I'm 22 years old now,and from Class 3, Grade 2.
console.log(yun.ARMS_NUM); // 2     
//我们本身对象是没有的,对象的原型也就是Student.prototype也没有,但是我们用了继承,继续向上查找,找到了Person.prototype.ARMS_NUM,所以返回2
yun.walk(); //Yunyun is walking...
yun.learn('math'); //Yunyun is learning math at Class 3,Grade 2.

다이어그램을 결합하여 위 코드를 역으로 분석해 보겠습니다. 먼저 새 Student 인스턴스를 통해 Student를 생성합니다. , yun의 프로토타입은 생성자의 프로토타입 속성(여기서는 Student.prototype)을 가리킵니다. Student.prototype에는 hi 메서드와 learn 메서드가 있습니다. Student.prototype은 Object.create(Person.prototype)를 통해 생성되므로 여기서는 Student.prototype은 빈 개체이고 이 개체의 프로토타입은 Person.prototype을 가리킵니다. 그런 다음 Person.prototype에 LEGS_NUM, ARMS_NUM 속성과 hi, walk 메서드도 설정합니다. 그런 다음 Person 함수를 직접 정의했습니다. Person.prototype은 자체적으로 프로토타입을 갖는 사전 설정된 객체입니다. 이 때문에 모든 객체에는 hasOwnProperty, valueOf, toString이 있습니다. 다른 공용 함수인 경우 이러한 함수는 모두 Object.prototype에서 파생됩니다. 이런 식으로 프로토타입 체인 기반 상속이 구현됩니다. 그렇다면 hi, walk 및 learn 메소드를 호출하면 어떻게 될까요? 예를 들어 hi 메소드를 호출할 때 먼저 yun 객체에 hi 메소드가 있는지 확인하는데, 이 경우에는 hi 메소드가 없으므로 위쪽으로 검색하여 yun의 프로토타입인 hi 메소드를 찾는다. Student.protoype에서 최종적으로 호출합니다. 하나는 Student.prototype.hi이고 다른 메서드를 호출하는 것도 비슷합니다.

프로토타입 변경

JavaScript의 프로토타입은 Java의 클래스와 다르다는 것을 알고 있습니다. Java의 클래스는 일단 작성되면 동적으로 변경하기 어렵지만 JavaScript에서는 프로토타입은 실제로 일반 객체입니다. 즉, 프로그램 실행 단계에서 프로토타입에 일부 속성을 동적으로 추가하거나 삭제할 수도 있습니다.

위 코드를 기반으로 이미 yun 인스턴스가 있으므로 실험을 진행해 보겠습니다.

Student.prototype.x = 101;        //通过Student.prototype.x把yun的原型动态地添加一个属性x
yun.x;   //101                    //那我们发现所有的实例都会受到影响
//接着我们做个有趣的实验
Student.prototype = {y:2};        //我们直接修改构造器的prototype属性,把它赋值为一个新的对象
yun.y;  //undefined               
yun.x;  //101                     //所以我们得出:当我们修改Student.prototype值的时候,并不能修改已经实例化的对象
var Tom = new Student('Tom',3,'Class LOL KengB');  
Tom.x; //undefined                //但当我们创建一个新的实例时,这一次x就不见了,
Tom.y; //2                        //并且y是新的值

그래서 프로토타입이 동적으로 수정되면 이번에는 생성되거나 새로 생성된 모든 인스턴스에 영향을 주지만 전체 프로토타입을 수정하여 새 객체에 할당하면 생성된 인스턴스에는 영향을 미치지 않지만 후속 인스턴스에는 영향을 미칩니다.

实现继承的方式

实现继承有多种方式,下面我们还是以Person和Student来分析

function Person() {
}

function Student() {
}

Student.prototype = Person.prototype; // 我们可不可用这种方式呢?这种方法是错误的:因为子类Student有自己的一些方法
//,如果通过这样子赋值,改变Student的同时也改变了Person。

Student.prototype = new Person(); //这种方式是可以实现的,但是调用构造函数有时候也是有问题的,比如要传进Person一个name和age
//,这里的Student是个类,还没实例化,这时候有些奇怪了,传什么都不是。

Student.prototype = Object.create(Person.prototype); //相对来说这中方式是比较理想的,这里我们创建了一个空的对象
//,并且对象的原型指向Person.prototype,这样我们既保证了继承了Person.prototype上的方法,并且Student.prototype又有自己空的对象。
//但是Object.create是ES5以后才有的

以上就是深入浅出JavaScript之原型链和继承的详解的内容,更多相关内容请关注PHP中文网(www.php.cn)!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.