Maison > interface Web > js tutoriel > Analyse complète des prototypes en JavaScript

Analyse complète des prototypes en JavaScript

亚连
Libérer: 2018-05-21 14:53:47
original
1423 Les gens l'ont consulté

Cet article présente principalement l'analyse complète du prototype en JavaScript. Le prototype est un support important pour l'argument "tout est un objet" dans le monde js. Il explique l'attribut __proto__ et la chaîne de prototypes et d'autres informations sèches. en cas de besoin, vous pouvez y faire référence

Pour comprendre le prototype en JS, vous devez d'abord comprendre les concepts suivants
1. Tout dans JS est un objet

2. Tout dans JS est dérivé de Object Comes from, c'est-à-dire le point final de la chaîne de prototypes de toutes choses pointe vers Object.prototype

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

3. La relation subtile entre les constructeurs et les instances (objets) dans JS
Le constructeur définit son prototype en définissant les spécifications de l'instance, puis construit l'instance via new, leur rôle est de produire l'objet
Et le constructeur (méthode) lui-même est une instance de la méthode (Fonction), donc son __proto__. (chaîne de prototypes) peut également être trouvée )

Objet / fonction F() {} C'est le constructeur, l'un est fourni par l'API native JS et l'autre est personnalisé
new Object() / new F() Cela ressemble à ceci C'est juste une instance
Une instance peut "seulement" regarder __proto__ pour savoir sur quel prototype elle est basée,
et "ne peut pas" redéfinir le prototype de l'instance à créer une instance de l'instance.

La pratique apporte la vraie connaissance, et ce n'est qu'en observant/pensant soi-même que l'on peut vraiment comprendre :

  // 先来看看构造函数到底是什么
  // 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__);
Copier après la connexion

Vous avez peut-être le vertige, décomposons-le.

prototype Le format de sortie du prototype est : constructeur nom de la fonction prototype
Tout d'abord, jetons un coup d'œil à ce que génère Object.prototype ?
Object {} -> L'objet précédent est un constructeur Le nom de la fonction, celui qui suit représente le prototype, voici un {}, c'est-à-dire une instance d'un objet Object (objet vide)
Ensuite, nous comprenons ce que F {} signifie, F est le nom du constructeur, et le prototype est aussi Un objet vide

  // 再来看看由构造函数构造出来的实例
  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__);
Copier après la connexion

Allons un peu plus loin et définissons le prototype de F pour voir ce qui se passe

  function F() {}
  F.prototype.a = function() {};
  var i = new F();
  // undefined       F {a: function}
  console.log(i.prototype, i.__proto__);
Copier après la connexion

De cette façon, nous pouvons clairement voir que i est représenté par F Le prototype construit est {a: function}, ce qui signifie que le prototype d'objet vide d'origine a une nouvelle méthode a

Changeons encore la situation, qu'est-ce que se produire si nous couvrons complètement le prototype de F ?

function F() {}
  F.prototype = {
    a: function() {}
  };
  var i = new F();
  // undefined       Object {a: function}
  console.log(i.prototype, i.__proto__);
Copier après la connexion

Hé ~ Pourquoi cela montre-t-il que i est construit à partir de l'objet Non ?
Parce que nous écrasons complètement le prototype de F, ce qui signifie en fait spécifier le prototype comme objet {a: function}, mais cela entraînera la perte des informations du constructeur d'origine et deviendra le constructeur spécifié par l'objet {a: function}.
Alors, quel est le constructeur de l'objet { a: function}?
Parce que l'objet {a: function} est en fait Relatif à

var o = {a: function() {}} // new了一个Object
Copier après la connexion

Alors le constructeur de o est bien sûr Objet

Corrigeons cette erreur

  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__);
Copier après la connexion

Nous pouvons maintenant obtenir les informations correctes sur le prototype ~

Chaîne prototype

Ensuite, regardons ce qu'est la chaîne prototype ?
En termes simples, la relation d'héritage (chaîne) en POO est la même, remontez couche par couche jusqu'à atteindre l'Object.prototype final

2016510172352211.jpg (560×248)

Le plus important est de comprendre savoir quelles choses dans JS sont des objets (d'instance). C'est simple, tout dans JS est un objet !
Une autre chose à comprendre est que chaque objet a un prototype !

Alors prouvons-le :

  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 的示例
Copier après la connexion

Prototype et __proto__Chaque objet contient un __proto__, pointant vers le "prototype" de cet objet.
Une chose similaire est que chaque fonction contient un prototype. Que fait cet objet prototype ?

Jetons un coup d'œil au code suivant, en utilisant le constructeur pour créer un objet (ce qui précède consiste à créer un objet sous la forme d'un littéral).

function Foo(){};
var foo = new Foo();
console.log(foo.__proto__);
Copier après la connexion

Pensez-y, que pointe le __proto__ de cet objet foo ?

2016510172448163.png (163×68)

Un objet contenant une propriété constructeur ? Peu importe si vous ne le comprenez pas bien. Imprimez simplement l'attribut prototype de la fonction Foo et comparez-le.

function Foo(){};
var foo = new Foo();
console.log(foo.__proto__);
console.log(Foo.prototype);
console.log(foo.__proto__ === Foo.prototype);
Copier après la connexion

2016510172512274.png (183×69)

Il s'avère que le __proto__ du nouvel objet foo ne pointe que vers le prototype de la fonction Foo.

foo.__proto__ --> Foo.prototype
Copier après la connexion
Copier après la connexion

A quoi ça sert de concevoir du JS comme ça ? Rappelons ce qui a été dit plus haut, dans le monde de JS, les objets ne sont pas créés à partir de classes (moules), mais dérivés de prototypes (un autre objet).

Lorsque nous effectuons la nouvelle opération pour créer un nouvel objet, nous n'entrerons pas dans l'implémentation spécifique de la nouvelle opération, mais une chose dont nous sommes sûrs est que le __proto__ du nouvel objet pointe vers un prototype objet.

Juste ce code

function Foo(){};
var foo = new Foo();
Copier après la connexion

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

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

foo.__proto__ --> Foo.prototype
Copier après la connexion
Copier après la connexion

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

2016510172555695.png (800×600)

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

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

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

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

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

3. 函数也是对象。

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

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

全面分析JavaScript面向对象概念中的Object类型与作用域(附有示例)

在javascript中创建对象的各种模式解析(图文教程)

详细解读JavaScript设计模式开发中的桥接模式(高级篇)

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal