Heim >Web-Frontend >js-Tutorial >Eine detaillierte Erklärung der Prototypenkette und Vererbung von JavaScript in einfachen Worten

Eine detaillierte Erklärung der Prototypenkette und Vererbung von JavaScript in einfachen Worten

黄舟
黄舟Original
2017-03-03 15:25:561176Durchsuche

Der Vererbungsmechanismus der Javascript-Sprache hat kein Konzept von „Unterklasse“ und „übergeordneter Klasse“ und es gibt keine Unterscheidung zwischen „Klasse“ und „Instanz“, alles basiert auf einem A sehr eigenartiges „Prototypenketten“-Modell zur Implementierung der Vererbung.

Dieser Teil des Wissens ist auch einer der Kernpunkte in JavaScript und auch ein schwieriger Punkt. Ich habe meine Studiennotizen so geordnet, dass es allen leichter fällt, zu lernen und meinen eigenen Eindruck zu vertiefen. Dieser Teil des Codes enthält viele Details, die eine wiederholte Betrachtung erfordern. Also fangen wir an.

Inhaltsverzeichnis der Serie

  • Eine ausführliche Einführung in den Abschluss von JavaScript (Closure)

  • Eine ausführliche Einführung zu JavaScript ist das

  • Ausführliche Einführung in die Prototypenkette und Vererbung von JavaScript

Ein kleiner Versuch

Prototypenkette Beispiel (die wichtigsten Punkte sind in den Kommentaren geschrieben, Sie können den Code kopieren, um ihn zu durchsuchen. Getestet auf dem Gerät, dasselbe unten)

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上的方法)

Gerade haben wir auf x zugegriffen , y und z und durchsucht die Prototypenkette. Wir können Folgendes wissen: Wenn ein bestimmtes Attribut auf dem Objekt gefunden wird, wird die Prototypenkette nach oben durchsucht Wird nicht gefunden, bis null gefunden wird, wird undefiniert zurückgegeben.

Prototypbasierte Vererbung

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上)

Prototypattribute und Prototypen

Sehen wir uns an, was für eine Struktur Foo.prototype ist. Wenn wir eine Funktionsdeklaration verwenden, um eine leere Funktion zu erstellen, verfügt diese Funktion standardmäßig über zwei Attribute: Konstruktor und __proto__. ,

Konstruktorattribut zeigt auf sich selbst Foo, __proto__ wird in Chrome verfügbar gemacht (kein Standardattribut, wissen Sie es einfach), dann zeigt der Prototyp von Foo.prototype auf Object.prototype. Daher werden einige Methoden toString und valueOf von

auf Object.prototype von jedem allgemeinen Objekt verwendet.

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

Zusammenfassend: Wir haben hier eine Foo-Funktion. Diese Funktion hat ein Prototyp-Objektattribut als Prototyp dieser neuen Objekte dienen.

Wir müssen also verstehen, dass Prototyp und Prototyp zwei verschiedene Dinge sind. Prototyp ist das Standardattribut des Funktionsobjekts und Prototyp ist normalerweise das Prototypattribut des Konstruktors.

Implementierung einer Klasse, die eine andere Klasse erbt

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.

Kombinieren wir das Diagramm und analysieren wir den obigen Code in umgekehrter Reihenfolge: Wir erstellen zunächst einen neuen Studenten durch neu Die Instanz von Student Yun, Yuns Prototyp zeigt auf das Prototypattribut des Konstruktors (hier ist Student.prototype), Student.prototype verfügt über eine Hi-Methode und eine Lernmethode, Student.prototype wird über Object.create (Person.prototype) erstellt, also Student .prototype ist hier ein leeres Objekt, und der Prototyp dieses Objekts zeigt auf Person.prototype. Dann legen wir auch die Attribute LEGS_NUM, ARMS_NUM und Hi, Walk-Methoden für Person.prototype fest. Dann haben wir direkt eine Person-Funktion definiert, die auch ihren Prototyp hat: Object.prototype. Genau aus diesem Grund wird jedes unserer Objekte hasOwnProperty, valueOf und andere haben Öffentliche Funktionen, diese Funktionen sind alle von Object.prototype abgeleitet. Auf diese Weise wird eine

Vererbung basierend auf der Prototypenkette implementiert. Was passiert also, wenn wir die Methoden „Hi“, „Walk“ und „Learn“ aufrufen? Wenn wir beispielsweise die Hi-Methode aufrufen, prüfen wir zunächst, ob für das Objekt Yun eine Hi-Methode vorhanden ist. In diesem Fall gibt es jedoch keine Hi-Methode. Daher suchen wir nach oben und finden den Prototyp von Yun, bei dem es sich um die Hi-Methode handelt auf Student.protoype, also nennen wir es schließlich Student.prototype.hi, und der Aufruf anderer Methoden ist ähnlich.

Prototyp ändern

Wir wissen, dass der Prototyp in JavaScript nicht wie die Klasse in Java ist. Sobald die Klasse in Java geschrieben ist, ist es schwierig, sie dynamisch zu ändern,

in JavaScript Der Prototyp ist eigentlich ein gewöhnliches Objekt, was bedeutet, dass wir während der Ausführungsphase des Programms dem Prototyp auch dynamisch einige Attribute hinzufügen oder löschen können.

Basierend auf dem obigen Code haben wir bereits die Instanz von Yun, fahren wir mit dem Experiment fort:

Wenn es also dynamisch geändert wird Wenn Sie einen Prototyp verwenden, wirkt sich dies auf alle erstellten oder neu erstellten Instanzen aus. Wenn Sie jedoch den gesamten Prototyp ändern und ihn einem neuen Objekt zuweisen, wirkt sich dies nicht auf die erstellten Instanzen aus, sondern auf nachfolgende Instanzen.
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)!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn