Heim  >  Artikel  >  Web-Frontend  >  Vollständige Analyse des Prototyp-Prototyps in JavaScript_Grundkenntnisse

Vollständige Analyse des Prototyp-Prototyps in JavaScript_Grundkenntnisse

WBOY
WBOYOriginal
2016-05-16 15:00:511292Durchsuche

Um Prototypen in JS zu verstehen, müssen Sie zunächst die folgenden Konzepte verstehen
1. Alles in JS ist ein Objekt

2. Alles in JS wird von Object abgeleitet, das heißt, der Endpunkt der Prototypenkette von allem zeigt auf Object.prototype

  // ["constructor", "toString", "toLocaleString", "valueOf", "hasOwnProperty", "isPrototypeOf", 
   // "propertyIsEnumerable", "__defineGetter__", "__lookupGetter__", "__defineSetter__",
   // "__lookupSetter__"]
   console.log(Object.getOwnPropertyNames(Object.prototype));

3. Die subtile Beziehung zwischen Konstruktoren und Instanzen (Objekten) in JS
Der Konstruktor definiert den Prototyp, um die Spezifikationen seiner Instanz zu vereinbaren, und verwendet dann new, um die Instanz zu erstellen.
Der Konstruktor (Methode) selbst ist eine Instanz der Methode (Funktion), daher kann auch sein __proto__ (Prototypkette) gefunden werden

Objekt/Funktion F() {} Dies ist der Konstruktor, einer wird von der nativen JS-API bereitgestellt und der andere wird angepasst
new Object() / new F() Dies ist eine Instanz
Die Instanz „kann nur“ __proto__ überprüfen, um zu wissen, auf welchem ​​Prototyp sie basiert,
Und „kann“ den Prototyp der Instanz nicht neu definieren, um eine Instanz der Instanz zu erstellen.

Übung bringt wahres Wissen, nur wenn du dich selbst beobachtest/nachdenkst, kannst du wirklich verstehen:

  // 先来看看构造函数到底是什么
  // function Empty() {}  function Empty() {}
  console.log(Function.prototype, Function.__proto__);
  // Object {}          function Empty() {}
  console.log(Object.prototype, Object.__proto__);
  function F() {}
  // F {}              function Empty() {}
  console.log(F.prototype, F.__proto__);

Möglicherweise sind Sie ohnmächtig geworden, lassen Sie es uns aufschlüsseln.

Prototyp
Das Format der Prototyp-Ausgabe ist: Konstruktorname Prototyp
Schauen wir uns zunächst an, was Object.prototype ausgibt?
Objekt {} -> Das vorherige Objekt ist der Name des Konstruktors und das letztere stellt den Prototyp dar. Hier ist ein {}, eine Instanz eines Objektobjekts (leeres Objekt)
Dann werden wir verstehen, was F {} bedeutet. F ist der Name des Konstruktors und der Prototyp ist auch ein leeres Objekt

  // 再来看看由构造函数构造出来的实例
  var o = new Object(); // var o = {};
  // undefined       Object {}
  console.log(o.prototype, o.__proto__);
  function F() {}
  var i = new F();
  // undefined       F {}
  console.log(i.prototype, i.__proto__);

Lassen Sie uns etwas tiefer gehen und den Prototyp von F definieren, um zu sehen, was passiert?

  function F() {}
  F.prototype.a = function() {};
  var i = new F();
  // undefined       F {a: function}
  console.log(i.prototype, i.__proto__);

Auf diese Weise können wir deutlich sehen, dass i aus F konstruiert ist und der Prototyp {a: function} ist, was bedeutet, dass der ursprüngliche leere Objektprototyp eine neue a-Methode hat

Ändern wir eine andere Situation: Was passiert, wenn wir den Prototyp von F vollständig abdecken?
 

function F() {}
  F.prototype = {
    a: function() {}
  };
  var i = new F();
  // undefined       Object {a: function}
  console.log(i.prototype, i.__proto__);
  

Hey~ Warum wird angezeigt, dass ich aus Objekt besteht? Das ist nicht richtig! Da wir den Prototyp von F vollständig überschreiben, geben wir den Prototyp tatsächlich als Objekt {a: Funktion} an. Dies führt jedoch dazu, dass die ursprünglichen Konstruktorinformationen verloren gehen und zum durch das Objekt {a: Funktion} angegebenen Konstruktor werden.
Was ist also der Konstruktor des Objekts {a: function}?
Weil das Objekt {a: function} tatsächlich relativ zu
ist

  var o = {a: function() {}} // new了一个Object
Dann ist der Konstruktor von o natürlich Object

Korrigieren wir diesen Fehler


  function F() {}
  F.prototype = {
    a: function() {}
  }
  // 重新指定正确的构造函数
  F.prototype.constructor = F;
  var i = new F();
  // undefined       F {a: function, constructor: function}
  console.log(i.prototype, i.__proto__);
Jetzt können Sie wieder die korrekten Prototypinformationen abrufen~

Prototypenkette

Dann werfen wir einen Blick darauf, was eine Prototypenkette ist?

Einfach ausgedrückt ist es dasselbe wie die Vererbungsbeziehung (Kette) in OOP. Sie gehen Schicht für Schicht nach oben, bis Sie den endgültigen Object.prototype erreichen

2016510172352211.jpg (560×248)

Das Wichtigste ist, herauszufinden, welche Dinge in JS (Instanz-)Objekte sind. Das ist einfach Eine andere Sache, die man verstehen sollte, ist, dass jedes Objekt einen Prototyp hat!


Dann beweisen wir es:


  Object // 这是一个函数, 函数是 Function 的实例对象, 那么就是由 Function 构造出来的
  Object.__proto__ == Function.prototype // 那么Object的原型, true
  // 这个是一个普通对象了, 因此属于 Object 的实例
  Function.prototype.__proto__ == Object.prototype // true
  // 这已经是原型链的最顶层了, 因此最终的指向 null
  Object.prototype.__proto__ == null // true

  Function // 这也是一个函数, 没错吧!
  Function.__proto__ == Function.prototype // true
  
  function A() {} // 这是一个自定义的函数, 终归还是一个函数, 没错吧! 
  A.__proto__ == Function.prototype // 任何函数都是 Function 的实例, 因此A的原型是?
  var a = new A()
  a.__proto__ == A.prototype // 实例a是由A构造函数构造出来的, 因此a的原型是由A的prototype属性定义的
  A.prototype.__proto__ == Object.prototype // 普通对象都是 Object 的示例

Prototyp und __Proto__

Jedes Objekt enthält ein __proto__, das auf den „Prototyp“ dieses Objekts verweist.
Eine ähnliche Sache ist, dass jede Funktion einen Prototyp enthält. Was macht dieses Prototypobjekt?

Schauen wir uns den folgenden Code an und verwenden den Konstruktor zum Erstellen eines Objekts (oben dient das Erstellen eines Objekts in Form eines Literals).

function Foo(){};
var foo = new Foo();
console.log(foo.__proto__);
Denken Sie mal darüber nach: Worauf weist das __proto__ dieses foo-Objekts hin?


2016510172448163.png (163×68)Ein Objekt, das eine Konstruktoreigenschaft enthält? Es macht nichts, wenn Sie es nicht gut verstehen. Drucken Sie einfach das Prototypattribut der Funktion Foo aus und vergleichen Sie es.

function Foo(){};
var foo = new Foo();
console.log(foo.__proto__);
console.log(Foo.prototype);
console.log(foo.__proto__ === Foo.prototype);

2016510172512274.png (183×69)Es stellt sich heraus, dass der __proto__ des neuen Objekts foo nur auf den Prototyp der Funktion Foo zeigt.


JS这么设计有何意义了?回忆下上面说的,在JS的世界中,对象不是根据类(模具)创建出来的,而是从原型(另一个对象)衍生出来的。

当我们执行new操作创建一个新的对象时,先不深入new操作的具体实现,但有一点我们是肯定的——就是为新对象的__proto__指向一个原型对象。

就刚才这段代码

function Foo(){};
var foo = new Foo();

foo.__proto__到底要指向谁了?你怎么不能指向Foo这个函数本身吧,虽然函数也是对象,这个有机会会详细讲。但如何foo.__proto__指向Foo固然不合适,因为Foo是一个函数,有很多逻辑代码,foo作为一个对象,继承逻辑处理没有任何意义,它要继承的是“原型对象”的属性。

所以,每个函数会自动生成一个prototype对象,由这个函数new出来的对象的__proto__就指向这个函数的prototype。

foo.__proto__ --> Foo.prototype

总结
说了这么多,感觉还是没完全说清楚,不如上一张图。我曾经参考过其他网友的图,但总觉得哪里没说清楚,所以我自己画了一张图,如果觉得我的不错,请点个赞!(老子可是费了牛劲才画出来)。

2016510172555695.png (800×600)

咱们就着这张图,记住如下几个事实:

1. 每个对象中都有一个_proto_属性。

JS世界中没有类(模具)的概念,对象是从另一个对象(原型)衍生出来的,所以每个对象中会有一个_proto_属性指向它的原型对象。(参考左上角的那个用字面量形式定义的对象obj,它在内存中开辟了一个空间存放对象自身的属性,同时生成一个_proto_指向它的原型——顶层原型对象。)

2. 每个函数都有一个prototype属性。

“构造函数”为何叫构造函数,因为它要构造对象。那么根据上面第一条事实,构造出来的新对象的_proto_属性指向谁了?总不能指向构造函数自身,虽然它也是个对象,但你不希望新对象继承函数的属性与方法吧。所以,在每个构造函数都会有一个prototype属性,指向一个对象作为这个构造函数构造出来的新对象的原型。

3. 函数也是对象。

每个函数都有一些通用的属性和方法,比如apply()/call()等。但这些通用的方法是如何继承的呢?函数又是怎么创建出来的呢?试想想,一切皆对象,包括函数也是对象,而且是通过构造函数构造出来的对象。那么根据上面第二条事实,每个函数也会有_proto_指向它的构造函数的prototype。而这个构造函数的函数就是Function,JS中的所有函数都是由Function构造出来的。函数的通用属性与方法就存放在Function.prototype这个原型对象上。

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