Maison > interface Web > js tutoriel > Explication détaillée de la chaîne de prototypes JS

Explication détaillée de la chaîne de prototypes JS

小云云
Libérer: 2018-03-13 14:54:09
original
2997 Les gens l'ont consulté

Cet article partage principalement avec vous l'explication détaillée de la chaîne de prototypes JS. Seules les fonctions (Function) ont l'attribut prototype, et les objets (sauf Object) ont __proto__. J'espère que cela aide tout le monde.


La chaîne dite prototype fait référence au protoC'est une chaîne de pointeur !

Le niveau supérieur de la chaîne de prototypes est Object.prototype, et cet objet n'a pas d'objet prototype.

peut être saisi dans la console Chrome :


    Object.__proto__
Copier après la connexion
Le résultat est :

<br/>

    function Empty() {}
Copier après la connexion
Chaîne prototype, c'est tout.

Un prototype est un objet à travers lequel d'autres objets peuvent implémenter l'héritage de propriétés.

1 La différence entre prototype et __proto__<br/>.

<br/>

var a = {};
console.log(a.prototype);  //undefined
console.log(a.__proto__);  //Object {}

var b = function(){}
console.log(b.prototype);  //b {}
console.log(b.__proto__);  //function() {}
Copier après la connexion

<br/>

/*1、字面量方式*/
var a = {};
console.log(a.__proto__);  //Object {}

console.log(a.__proto__ === a.constructor.prototype); //true

/*2、构造器方式*/
var A = function(){};
var a = new A();
console.log(a.__proto__); //A {}

console.log(a.__proto__ === a.constructor.prototype); //true

/*3、Object.create()方式*/
var a1 = {a:1}
var a2 = Object.create(a1);
console.log(a2.__proto__); //Object {a: 1}

console.log(a.__proto__ === a.constructor.prototype); //false(此处即为图1中的例外情况)
Copier après la connexion

<br/>

var A = function(){};
var a = new A();
console.log(a.__proto__); //A {}(即构造器function A 的原型对象)
console.log(a.__proto__.__proto__); //Object {}(即构造器function Object 的原型对象)
console.log(a.__proto__.__proto__.__proto__); //null
Copier après la connexion
Lorsque j'écrivais un article expliquant la différence entre prototype et __proto__, j'ai cherché des informations et j'ai trouvé un phénomène intéressant. Les résultats renvoyés par les deux opérations suivantes sont les mêmes :

<. 🎜>

<br/>

Que se passe-t-il ? Commençons par l’opérateur instanceof.
Function instanceof Object;//true
Object instanceof Function;//true
Copier après la connexion

1. Que fait exactement instanceof ?

我曾经简单理解instanceof只是检测一个对象是否是另个对象new出来的实例(例如var a = new Object(),a instanceof Object返回true),但实际instanceof的运算规则上比这个更复杂。

首先w3c上有官方解释(传送门,有兴趣的同学可以去看看),但是一如既往地让人无法一目了然地看懂……

知乎上有同学把这个解释翻译成人能读懂的语言(传送门),看起来似乎明白一些了:

<br/>

//假设instanceof运算符左边是L,右边是R
L instanceof R 
//instanceof运算时,通过判断L的原型链上是否存在R.prototype
L.__proto__.__proto__ ..... === R.prototype ?
//如果存在返回true 否则返回false
Copier après la connexion

注意:instanceof运算时会递归查找L的原型链,即L.__proto__.__proto__.__proto__.__proto__...直到找到了或者找到顶层为止。

所以一句话理解instanceof的运算规则为:

instanceof检测左侧的__proto__原型链上,是否存在右侧的prototype原型。

二、图解构造器Function和Object的关系

<br/>
Copier après la connexion

<br/>

<br/>

我们再配合代码来看一下就明白了:<br/>

//①构造器Function的构造器是它自身
Function.constructor=== Function;//true

//②构造器Object的构造器是Function(由此可知所有构造器的constructor都指向Function)
Object.constructor === Function;//true



//③构造器Function的__proto__是一个特殊的匿名函数function() {}
console.log(Function.__proto__);//function() {}

//④这个特殊的匿名函数的__proto__指向Object的prototype原型。
Function.__proto__.__proto__ === Object.prototype//true

//⑤Object的__proto__指向Function的prototype,也就是上面③中所述的特殊匿名函数
Object.__proto__ === Function.prototype;//true
Function.prototype === Function.__proto__;//true
Copier après la connexion

三、当构造器Object和Function遇到instanceof

我们回过头来看第一部分那个“奇怪的现象”,从上面那个图中我们可以看到:

<br/>

Function.__proto__.__proto__ === Object.prototype;//true
Object.__proto__ === Function.prototype;//true
Copier après la connexion

所以再看回第一点中我们说的instanceof的运算规则,Function instanceof Object 和 Object instanceof Function运算的结果当然都是true啦!

如果看完以上,你还觉得上面的关系看晕了的话,只需要记住下面两个最重要的关系,其他关系就可以推导出来了:

1、所有的构造器的constructor都指向Function

2、Function的prototype指向一个特殊匿名函数,而这个特殊匿名函数的__proto__指向Object.prototype

Pour savoir comment déduire la relation entre prototype et __proto__, vous pouvez vous référer au blog précédent que j'ai écrit "Trois images pour comprendre l'objet prototype et la chaîne prototype de JavaScript"

Cet article essaie d'expliquer des concepts tels que le prototype et la chaîne de prototypes dans JS et leurs mécanismes de fonctionnement. L'article précédent (Illustrant le contexte et la portée de Javascript) a présenté les concepts associés de portée des variables dans Js. En fait, une question centrale est la suivante : "Quelles variables l'interpréteur Js peut-il obtenir lors de l'exécution de la ligne de code actuelle ? » les chaînes concernent en fait ce problème.

Nous savons que tout dans Js est un objet (Object), mais il n'y a pas de classe dans Js ; Js est un paradigme de programmation orienté objet (POO) implémenté sur la base de prototypes. Oui, mais pas tous les objets avoir l'attribut prototype :

<br/>

<br/>

1

2

3

4

5

6

7

8

<br/>

var a = {};

console.log(a.prototype); //=> undefined

 

var b = function(){};

console.log(b.prototype); //=> {}

 

var c = &#39;Hello&#39;;

console.log(c.prototype); //=> undefined

prototype est un attribut qui accompagne chaque définition function, mais function lui-même en Js est aussi un objet. Jetons d'abord un coup d'œil aux différences entre les concepts suivants :

1. . function, Function, Object et {}

function est un mot-clé en JS, qui est utilisé pour définir des variables de type fonction. Il existe deux formes de syntaxe :

.

<br/>

<br/>

1

2

3

4

5

6

7

8

9

<br/>

function f1(){

console.log(&#39;This is function f1!&#39;);

}

typeof(f1); //=> &#39;function&#39;

 

var f2 = function(){

console.log(&#39;This is function f2!&#39;);

}

typeof(f2); //=> &#39;function&#39;

Si vous utilisez une approche plus orientée objet pour définir des fonctions, vous pouvez utiliser Function :

<br/>

<br/>

1

2

3

4

5

<br/>

var f3 = new Function("console.log(&#39;This is function f3!&#39;);");

f3(); //=> &#39;This is function f3!&#39;

typeof(f3); //=> &#39;function&#39;

 

typeof(Function); //=> &#39;function&#39;

En fait, Function est une classe utilisée pour construire des variables de type fonction, ou le constructeur d'instances de type fonction ; il existe des Object ou String, Number, etc. constructeurs d’instances de type intégrées Js. Le plus spécial est Object, qui est utilisé pour générer des types d'objets. Son abréviation est {} :

<br/>

<br/>

<🎜. >
1

2

3

4

5

6

7

<br/>

var o1 = new Object();

typeof(o1); //=> &#39;object&#39;

 

var o2 = {};

typeof(o2); //=> &#39;object&#39;

 

typeof(Object); //=> &#39;function&#39;

2. prototype VS __proto__

Après avoir clarifié les concepts ci-dessus, regardons prototype :

Chaque fonction a deux propriétés : length et prototype

prototype et length sont deux attributs fournis avec chaque type de fonction, mais pas les autres types non fonctionnels (l'exemple du début a déjà expliqué la raison). pour cette comparaison Il est facile d'être ignoré ou mal compris car tous les types de constructeurs sont eux-mêmes des fonctions, ils sont donc livrés avec l'attribut prototype :

<br/>

<br/>

1

2

3

4

<br/>

// Node

console.log(Object.prototype); //=> {}

console.log(Function.prototype);//=> [Function: Empty]

console.log(String.prototype); //=> [String: &#39;&#39;]

À l'exception de prototype, tous les objets dans JS (sauf cas particuliers tels que undefined et null) ont un attribut [[Prototype]] intégré qui pointe vers le prototype de sa "classe parent" .Il n'existe aucun moyen clair d'obtenir les propriétés intégrées dans la norme ECMA, mais de nombreuses implémentations Js (telles que Node, la plupart des navigateurs, etc.) fournissent un attribut __proto__ pour faire référence à ce [[Prototype]]. Exemple suivant pour illustrer comment __proto__ dans l'instance pointe vers prototype du constructeur :

<br/>

<br/>

1

2

3

4

5

6

7

8

9

10

11

<br/>

var Person = function(){};

Person.prototype.type = &#39;Person&#39;;

Person.prototype.maxAge = 100;

 

var p = new Person();

console.log(p.maxAge);

p.name = &#39;rainy&#39;;

 

Person.prototype.constructor === Person; //=> true

p.__proto__ === Person.prototype; //=> true

console.log(p.prototype); //=> undefined

L'exemple de code ci-dessus peut être expliqué par la figure suivante :

Person est une variable de type fonction, elle est donc livrée avec l'attribut prototype, prototype Le constructor dans l'attribut pointe vers Person lui-même ; l'instance new de la classe Person générée via le mot-clé p1 pointe vers le prototype de __proto__ via l'attribut Person. Le __proto__ ici sert simplement à illustrer la relation entre l'instance p1 et la classe parent lorsqu'elle est implémentée en interne (pointant vers le prototype de la classe parent). Pendant le fonctionnement réel, l'instance peut être directement obtenue à partir du parent). prototype de classe via les attributs ., réalisant ainsi la fonction d'héritage.

3. Chaîne de prototypes

Après avoir clarifié les concepts et les relations entre prototype et __proto__, nous aurons une compréhension plus approfondie de la phrase "Tout en Js est un objet". Ensuite, nous penserons que puisque __proto__ est une propriété intégrée de (presque) tous les objets et pointe vers le prototype de la classe parent, cela signifie-t-il que nous pouvons « remonter en amont » et trouver la source ? Regardons l'exemple suivant :

<br/>

<br/>

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

<br/>

// Node

var Obj = function(){};

var o = new Obj();

o.__proto__ === Obj.prototype; //=> true

o.__proto__.constructor === Obj; //=> true

 

Obj.__proto__ === Function.prototype; //=> true

Obj.__proto__.constructor === Function; //=> true

 

Function.__proto__ === Function.prototype; //=> true

Object.__proto__ === Object.prototype; //=> false

Object.__proto__ === Function.prototype; //=> true

 

Function.__proto__.constructor === Function;//=> true

Function.__proto__.__proto__; //=> {}

Function.__proto__.__proto__ === o.__proto__.__proto__; //=> true

o.__proto__.__proto__.__proto__ === null; //=> true

Comme le montrent les exemples et illustrations ci-dessus, l'objet prototype possède également l'attribut __proto__, remontant jusqu'à null. La fonction du mot-clé

new est de compléter la connexion entre l'instance affichée dans l'image ci-dessus et le prototype de la classe parent, et de créer un nouvel objet. La fonction du mot-clé instanceof peut également être ; vu de l'image ci-dessus Comme on peut le voir, c'est en fait le prototype qui détermine si le __proto__ (et __proto__.__proto__...) pointe vers la classe parent :

<br/>

<br/>

1

2

3

4

5

6

7

8

9

10

<br/>

var Obj = function(){};

var o = new Obj();

 

o instanceof Obj; //=> true

o instanceof Object; //=> true

o instanceof Function; //=> false

 

o.__proto__ === Obj.prototype; //=> true

o.__proto__.__proto__ === Object.prototype; //=> true

o.__proto__.__proto__ === Function; //=> false

Chaîne de prototypes orientés objet JS

<br/>

<br/>

Chaîne de prototypes orientés objet JS

Chaîne de prototypes d'objets

  1. Tant qu'il s'agit d'un objet, il a un prototype

  2. Les prototypes sont aussi des objets

  3. Tant que c'est un objet, il a un prototype, et un prototype est aussi un objet, donc tant qu'un l'objet est défini, son prototype peut être trouvé, et ainsi de suite. Constituant une séquence d'objets, cette structure est appelée une chaîne prototype

  4. Où est la tête de. la chaîne de prototypes ?

  5. À quoi ressemble une structure de chaîne de prototypes par défaut ?

  6. Quelles modifications la structure de chaîne de prototypes apporte-t-elle aux structures syntaxiques connues ?

Chaîne de prototypes La structure de

  1. héritage de la chaîne de prototypes consiste à utiliser et à modifier la structure de la chaîne de prototypes (ajouter , supprimer, modifier les membres dans le nœud), afin que l'objet instance puisse utiliser l'intégralité du prototype. Tous les membres (propriétés et méthodes) de la chaîne

  2. L'utilisation de l'héritage de chaîne de prototype doit satisfaire l'attribut principe de recherche

Principe de recherche d'attributs

  1. Le principe dit de recherche d'attributs est que lorsqu'un objet accède aux attributs et aux méthodes, il recherche d'abord

  2. dans l'objet actuel s'il est stocké dans un attribut ou une méthode, arrêtez la recherche et utilisez directement l'attribut et la méthode

  3. Si. l'objet n'a pas de membres modifiés, alors recherchez dans son objet prototype

  4. Si le prototype L'objet contient ce membre, alors arrêtez la recherche et utilisez

  5. directement. Si le prototype n'existe pas encore, allez sur le prototype du prototype pour trouver

  6. et ainsi de suite jusqu'à ce qu'il n'y ait pas d'Object.prototype, puis retournez undefind.<🎜. >

  7. Si la méthode est appelée, une erreur sera incluse. Le xxxx n'est pas une fonction

Diagramme de structure de chaîne de prototype

  1. Diagramme de structure de chaîne de prototype d'objet constructeur

    <br/>function Person (){}; var p = new Person();<br/>

  2. {} Schéma de structure de chaîne de prototype d'objet

    <br/>

  3. [] Diagramme de structure de chaîne de prototype de tableau

    <br/>

  4. Constructeur correspondant Object.prototype<br/>

  5. p Constructeur correspondant

  6. p -> pTag.prototype( is o ) -> Object.prototype ->
var o = {
    appendTo: function ( dom ) {
    }
};
function pTag() {}
pTag.prototype = o;

var p = new pTag();
Copier après la connexion

函数的构造函数 Function

在 js 中 使用 Function 可以实例化函数对象. 也就是说在 js 中函数与普通对象一样, 也是一个对象类型( 非常特殊 )

  1. 函数是对象, 就可以使用对象的动态特性

  2. 函数是对象, 就有构造函数创建函数

  3. 函数是函数, 可以创建其他对象(函数的构造函数也是函数)

  4. 函数是唯一可以限定变量作用域的结构

函数是 Function 的实例

new Function( arg0, arg1, arg2, ..., argN, body );
Copier après la connexion
  1. Function 中的参数全部是字符串

  2. 该构造函数的作用是将 参数链接起来组成函数

  • 如果参数只有一个, 那么表示函数体

  • 如果参数有多个, 那么最后一个参数表示新函数体, 前面的所有参数表示新函数的参数

  • 如果没有参数, 表示创建一个空函数

创建一个打印一句话的函数

    // 传统的
    function foo () {
        console.log( &#39;你好&#39; );
    }
    // Function
    var func = new Function( &#39;console.log( "你好" );&#39; );
    // 功能上, 这里 foo 与 func 等价
Copier après la connexion

创建一个空函数

    // 传统
    function foo () {}
    // Function
    var func = new Function();
Copier après la connexion

传入函数内一个数字, 打印该数字

    // 传统
    function foo ( num ) {
        console.log( num );
    }
    // Function
    var func = new Function ( "num" ,"console.log( num );" );
    func();
Copier après la connexion

利用 Function 创建一个函数, 要求传入两个数字, 打印其和

    var func = new Function( &#39;num1&#39;, &#39;num2&#39;, &#39;console.log( num1 + num2 );&#39; );
Copier après la connexion

练习: 利用 Function 创建一个函数, 要求允许函数调用时传入任意个数参数, 并且函数返回这些数字中最大的数字.<br/>练习: 利用 Function 创建一个求三个数中最大数的函数.

    // 传统
    function foo ( a, b, c ) {
        var res = a > b ? a : b;
        res = res > c ? res : c;
        return res;
    }
    // Function
    var func = new Function( &#39;a&#39;, &#39;b&#39;, &#39;c&#39;, &#39;var res = a > b ? a : b;res = res > c ? res : c;return res;&#39; )
Copier après la connexion

解决代码太长的办法:

  1. 利用 加法 连接字符串

    var func = new Function( &#39;a&#39;, &#39;b&#39;, &#39;c&#39;,
            &#39;var res = a > b ? a : b;&#39; +
            &#39;res = res > c ? res : c;&#39; +
            &#39;return res;&#39; );
    Copier après la connexion
  2. 利用字符串特性( 刚学 )

    function foo ( a, b, c ) {
        var res = a > b ? a : b;
        res = res > c ? res : c;
        return res;
    }
    var func = new Function( &#39;a&#39;, &#39;b&#39;, &#39;c&#39;, &#39;return foo( a, b, c );&#39; );
    Copier après la connexion
  3. ES6 的语法( 少浏览器实现 )

    • 使用 键盘左上角的 左单引号 表示可换行字符串的界定符

  4. (最终)利用 DOM 的特性完成该方法

arguments 对象

arguments 是一个伪数组对象. 它表示在函数调用的过程中传入的所有参数的集合.<br/>在函数调用过程中没有规定参数的个数与类型, 因此函数调用就具有灵活的特性, 那么为了方便使用,<br/>在 每一个函数调用的过程中, 函数代码体内有一个默认的对象 arguments, 它存储着实际传入的所有参数.

js 中函数并没有规定必须如何传参

  1. 定义函数的时候不写参数, 一样可以调用时传递参数

  2. 定义的时候写了参数, 调用的时候可以不传参

  3. 定义的时候写了一参数, 调用的时候可以随意的传递多个而参数

在代码设计中, 如果需要函数带有任意个参数的时候, 一般就不带任何参数, 所有的 参数利用 arguments 来获取.<br/>一般的函数定义语法, 可以写成:

    function foo ( /* ... */ ) {
    }
Copier après la connexion

利用 Function 创建一个函数, 要求允许函数调用时传入任意个数参数, 并且函数返回这些数字中最大的数字.

    function foo ( ) {
        // 所有的参数都在 arguments 中. 将其当做数组使用
        // 问题而已转换成在有一个数组中求最大值
        var args = arguments;
        var max = args[ 0 ];
        for ( var i = 1; i < args.length; i++ ) {
            if ( max < args[ i ] ) {
                max = args[ i ];
            }
        }
        return max;
    }
Copier après la connexion

练习: 利用 Function 写一个函数, 要求传入任意个数字 求和

函数的原型链结构

任意的一个函数, 都是相当于 Function 的实例. 类似于 {} 与 new Object() 的关系

    function foo () {};
    // 告诉解释器, 有一个对象叫 foo, 它是一个函数
    // 相当于 new Function() 得到一个 函数对象
Copier après la connexion
  1. 函数有 __proto__ 属性

  2. 函数的构造函数是 Function

  3. 函数应该继承自 Function.prototype

  4. Fucntion.prototype 继承自 Object.protoype

  5. 构造函数有prototype, 实例对象才有__proto__指向原型, 构造函数的原型才有 constructor 指向构造函数

intanceof

array instanceof Array
判断 构造函数 Array 的原型 是否在 实例对象 array 的原型链存在

相关推荐:

关于js原型链的7篇文章推荐

js原型链继承的几个细节问题

js原型链原理看图说明_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