Pourquoi, dans JavaScript, Object instanceof Function
et Function instanceof Object
renvoient-ils true
?
Je l'ai essayé dans Safari WebInspector.
J'ai mis du temps à comprendre, mais ça vaut vraiment le temps passé. Tout d’abord, voyons comment fonctionne instanceof
.
Citant de MDN ,
L'opérateur
instanceof
teste si un objet a dans sa chaîne de prototypes la propriétéprototype
d'un constructeur.
[instanceof]
Voyons maintenant comment instanceof
est défini par la spécification ECMA 5.1,
Le
RelationalExpression: RelationalExpression instanceof ShiftExpression
de production est évalué comme suit:
- Soit
lref
le résultat de l’évaluation deRelationalExpression
.- Soit
lval
êtreGetValue(lref)
.- Soit
rref
le résultat de l’évaluation deShiftExpression
.- Soit
rval
êtreGetValue(rref)
.- Si
Type(rval)
n'est pas un objet, générez une exceptionTypeError
.- Si
rval
n'a pas de méthode interne[[HasInstance]]
, lève une exceptionTypeError
.- Renvoie le résultat de l'appel de la méthode interne
[[HasInstance]]
derval
avec l'argumentlval
.
D'abord, les expressions de gauche et de droite sont évaluées (GetValue
), puis le résultat de droite devrait être un objet avec une méthode interne [[HasInstance]]
. Tous les objets n'auront pas la méthode interne [[HasInstance]]
, mais des fonctions. Par exemple, ce qui suit va échouer
console.log(Object instanceof {});
# TypeError: Expecting a function in instanceof check, but got #<Object>
[[HasInstance]]
Voyons maintenant comment [[HasInstance]]
a été défini dans la spécification ECMA 5.1,
Supposons que
F
soit un objet Function.Lorsque la méthode interne
[[HasInstance]]
deF
est appelée avec la valeurV
, les étapes suivantes sont entreprises:
- Si
V
n'est pas un objet, retournezfalse
.- Soit
O
le résultat de l'appel de la méthode interne[[Get]]
deF
avec le nom de propriété"prototype"
.- Si
Type(O)
n'est pas un objet, générez une exceptionTypeError
.- Répéter
- Soit
V
la valeur de la propriété[[Prototype]]
internal deV
.- Si
V
estnull
, retournezfalse
.- Si
O
etV
se rapportent au même objet, retourneztrue
.
C'est si simple. Prenez la propriété prototype
de F
et comparez-la avec la propriété [[Prototype]]
internal de O
jusqu'à ce qu'elle devienne null
ou prototype
de F
soit identique à O
.
[[prototype]]
propriété interneVoyons d’abord quelle est la propriété [[prototype]]
internal ,
Tous les objets ont une propriété interne appelée
[[Prototype]]
. La valeur de cette propriété estnull
ou un objet et est utilisée pour implémenter l'héritage. Le fait qu'un objet natif puisse ou non avoir un objet hôte en tant que[[Prototype]]
dépend de l'implémentation. Chaque chaîne[[Prototype]]
doit avoir une longueur finie (c’est-à-dire que, à partir de tout objet, l’accès récursif à la propriété interne[[Prototype]]
doit finalement conduire à une valeurnull
).
Remarque: Nous pouvons obtenir cette propriété interne avec la fonction Object.getPrototypeOf
.
prototype
property[[HasInstance]]
parle également d'une autre propriété appelée prototype
, qui est spécifique aux objets Function
.
La valeur de la propriété
prototype
est utilisée pour initialiser la propriété[[Prototype]]
internal d'un objet nouvellement créé avant que l'objet Function ne soit appelé en tant que constructeur pour cet objet nouvellement créé.
Cela signifie que, lorsqu'un objet fonction est utilisé en tant que constructeur, un nouvel objet est créé et le nouvel objet a son [[Prototype]]
interne initialisé avec cette propriété prototype
. Par exemple,
function Test() {}
Test.prototype.print = console.log;
console.log(Object.getPrototypeOf(new Test()) === Test.prototype);
# true
Revenons maintenant à la question même. Permet de prendre le premier cas
console.log(Object instanceof Function);
# true
Il va d'abord chercher Function.prototype
et il va essayer de trouver si cet objet est dans la hiérarchie prototype de Object
. Voyons comment cela se passe
console.log(Function.prototype);
# [Function: Empty]
console.log(Object.getPrototypeOf(Object));
# [Function: Empty]
console.log(Object.getPrototypeOf(Object) === Function.prototype);
# true
Puisque le Function.prototype
correspond à la propriété interne de Object
, [[Prototype]]
, il retourne true
.
Prenons maintenant le deuxième cas
console.log(Function instanceof Object);
# true
console.log(Object.prototype);
# {}
console.log(Object.getPrototypeOf(Function));
# [Function: Empty]
console.log(Object.getPrototypeOf(Function) === Object.prototype);
# false
console.log(Object.getPrototypeOf(Object.getPrototypeOf(Function)));
# {}
Object.getPrototypeOf(Object.getPrototypeOf(Function)) === Object.prototype
# true
Ici, nous obtenons d’abord le Object.prototype
, qui est {}
. Maintenant, il essaie de trouver si le même objet {}
est présent dans la chaîne de prototypes de Function
. Le parent immédiat de Function
est et la fonction vide.
console.log(Object.getPrototypeOf(Function));
# [Function: Empty]
Ce n'est pas la même chose que Object.prototype
console.log(Object.getPrototypeOf(Function) === Object.prototype);
# false
Mais l'algorithme [[HasInstance]]
ne s'arrête pas là. Il répète et monte d'un niveau
console.log(Object.getPrototypeOf(Object.getPrototypeOf(Function)));
# {}
Et ceci est identique à Object.prototype
. C'est pourquoi cela retourne true
.
De MDN :
L'opérateur instanceof teste si un objet a dans sa chaîne de prototypes la propriété de prototype d'un constructeur.
Essentiellement, il vérifie si Object
(pas une instance de Object
, mais le constructeur lui-même) a comme instance de Function.constructor
quelque part dans sa chaîne de prototypes.
Et en effet:
> Function.__proto__.__proto__ === Object.prototype
true
> Object.__proto__ === Function.prototype
true
Ceci explique pourquoi Object instanceof Function
et l'inverse.
TOUS les objets ont une propriété interne appelée [[Prototype]]. La valeur de cette propriété est null ou un objet et est utilisée pour implémenter l'héritage. Si vous essayez de rechercher une clé sur un objet et que celui-ci est introuvable, JavaScript le recherchera dans la chaîne de prototypes.
Le constructeur de fonctions crée de nouveaux objets de fonction (instances du constructeur de fonctions). La propriété prototype est spécifique aux objets Function. Le constructeur de fonction est lui-même un objet Function (instance du constructeur de fonction).
Lorsqu'un objet Function est utilisé en tant que constructeur, un nouvel objet est créé et son objet [[Prototype]] est initialisé avec la propriété prototype du constructeur.
function Dog () {}
var myCrazyDog = new Dog();
myCrazyDog.__proto__ === Dog.prototype // true
La spécification de langage est que tous les objets sont des instances du constructeur Object et que toutes les fonctions sont des instances du constructeur Function.
L'objet instanceof Function est vrai car Object est une fonction et constitue donc une instance de Function (Object est un objet Function - une instance du constructeur de la fonction). L'objet hérite de Function.prototype.
console.log(Object instanceof Function) // true
console.log(Object.__proto__ === Function.prototype) // true
Object instanceof Object est vrai car Object hérite de Function.prototype. Puisque Function.prototype est un objet, il hérite de Object.prototype. L'instance de fonction de Object est true car Function hérite de Function.prototype. Comme Function.prototype est un objet, il hérite de Object.prototype. La chaîne de prototypes ressemble à ceci:
Object ---> Function.prototype ---> Object.prototype ---> null
Function ---> Function.prototype ---> Object.prototype ---> null
console.log(Object instanceof Object) // true
console.log(Object.__proto__ === Function.prototype) // true
console.log(Object.__proto__.__proto__ === Object.prototype) // true
console.log(Function instanceof Object) // true
console.log(Function.__proto__ === Function.prototype) // true
console.log(Function.__proto__.__proto__ === Object.prototype) // true
Fonction instanceof Fonction est vraie . Function est une instance de lui-même (naturellement, car c’est une fonction, et donc une instance de Function). La chaîne de prototype ressemble à ceci:
Function ---> Function.prototype ---> Object.prototype ---> null
console.log(Function instanceof Function) // true
console.log(Function.__proto__ === Function.prototype) // true
console.log(Function.__proto__.__proto__ === Object.prototype) // true
Gardez donc à l’esprit que les constructeurs Function () et Object () sont des fonctions. Comme ce sont des fonctions, elles sont des instances du constructeur Function () et héritent de Function.prototype. Puisque Function.prototype est un objet, Function.prototype est une instance de Object, héritant donc de Object.prototype.
console.log(Object instance of Function) // true
console.log(Function instance of Function) // true
console.log(Function.prototype instanceof Object); // true
La source de confusion dans votre question réside dans la double nature inhérente des fonctions * en JavaScript (ECMAScript).
Les fonctions en js sont à la fois des fonctions normales et des objets en même temps. Considérez-les comme des algorithmes Dr. Jekyll et Mr. Hyde . Ils ressemblent à des objets à l’extérieur, mais à l’intérieur, ce ne sont que vos bonnes vieilles fonctions js avec toutes leurs bizarreries, ou peut-être que c’est l’inverse!
JavaScript est vraiment une affaire délicate :)
Revenons donc à votre question, en empruntant la syntaxe apparaissant sur MDN:
object instanceof constructor
En l'appliquant à la première instruction de votre code:
Object instanceof Function
Ici, vous avez Object
, une fonction constructeur utilisée en tant qu'initialiseur d'objet, mais puisque les fonctions mènent une double vie en js, elle est associée à des propriétés et à des méthodes propres à chaque objet, ce qui en fait également un objet.
La première condition de la déclaration a donc été remplie. Nous restons à enquêter sur l'autre condition ou l'opérande.
Function
, comme vous l'avez peut-être remarqué, est également un constructeur de fonctions, mais son autre objet, ne nous intéresse plus pour le moment de l'exécution de cette instruction particulière.
Ainsi, les conditions syntaxiques sont toutes deux remplies à savoir "objet" et "constructeur". Nous pouvons maintenant procéder à l’enquête sur leur relation héréditaire et s’il existe un lien entre eux.
Étant donné que Object
est une fonction fonctionnelle en soi, il est tout à fait logique de supposer que son prototype interne indique la référence d'objet Function.prototype
puisque, dans jsALLfunctions inherit leurs accessoires et méthodes à travers ce même emplacement Function.prototype
.
true
est définitivement leSEULrésultat attendu de cette comparaison effectuée par l'opérateur instanceof
.
Pour l'autre cas:
Function instanceof Object
Depuis que nous avons déjà établi que les fonctions dans js ont aussi un côté objet. Il est logique qu'ils aient obtenu leurs fantaisie spécifiques aux objets toys du Object.prototype
et qu'ils constituent donc instances du constructeur Object.
J'espère que je n'ai pas ajouté à la confusion avec mes explications et allégories. :)
*: Non seulement les fonctions qui mènent une double vie en js. Presque tous les types de données dans js ont un côté obscur qui facilite la réalisation d'opérations et de manipulations sans tracas.
La propriété la plus redoutable est en fait que Function est une instance de lui-même. Function instanceof Function
renvoie vrai.
Il est bien expliqué dans - Le modèle de type Javascript étonnamment élégant , sur http://web.archive.org/web/20140205182624/http://vijayan.ca/blog/2012/02/ 21/javascript-type-model
Citez à la fin de l'explication:
Oui, cela signifie que Function est une instance de lui-même (naturellement, car c’est une fonction, et donc une instance de Function). C’est quelque chose avec lequel nous avons tous affaire, en connaissance de cause ou non, depuis longtemps - toutes les fonctions du constructeur sont des fonctions régulières et donc des instances de Function, et Function elle-même est simplement la fonction constructeur pour la construction d’autres fonctions. une instance de Function.
Explication très simple, différente de toutes les réponses
Les deux objets Object et Function sont des constructeurs (type des deux avec retour "objets Function") et sont tous deux créés à partir de Function Constructor. La propriété __proto__ de ces deux éléments pointe vers l'objet 'Function.prototype'.
EXPLICATION RAPIDE: La propriété __proto__ d'un objet (disons p1 qui est un type de personne) pointe sur le prototype du constructeur (disons Person.prototype) De nouveau, __proto__ dans la chaîne de prototypes pointe sur l'objet "Object.prototype".
AVANT DE LIRE D'AUTRES DÉTAILS IMPRIMÉS SUR LA CONSOLE CHROME console.dir (Object), console.dir (Fonction)
KEEP IN MIND, les constructeurs de fonctions et les objets vous permettront de voir les propriétés .prototype et __proto__. Dans tous les objets d'instance (disons p1), vous ne trouverez que la propriété __proto__. __Proto__ est l'accesseur de la propriété masquée [[Prototye]] et le meilleur moyen d'obtenir est Object.getPrototypeOf (p1) car __proto__ est protégé.
(p1 instanceof Person) L'opérateur vérifie ici si le prototype de la personne constructeur se trouve dans la chaîne de prototypes de l'objet p1. notez que la première valeur est un objet d'instance (p1) et que la deuxième valeur est un constructeur (Personne).
Permet d'analyser => Instance de fonction de l'objet.
__Proto __.__ proto__ de l'objet de fonction pointe sur Object.prototype, il est donc vrai
Permet d'analyser => Instance d'objet de fonction.
__Proto__ de l'objet object pointe sur Function.prototype, il est donc vrai
J'espère que cela t'aides.