ES3, Chap 7. — Les types en JavaScript

Ce billet fait partie de la collection ES3 dans le détail et en constitue le Chapitre 7.

Tout n'est pas que SOLDAT à la Shinra, il y en a différent types.
Tout n'est pas que SOLDAT à la Shinra, il y en a différent types.

Cet article va traiter des six types existants en JavaScript dans sa version ES3.

Introduction

Nous allons ici prendre en considération les types de données existants. Il est en premier lieu nécessaire de noter que le JavaScript distingue les entités à valeurs primitives des objets. Malgré la phrase « en JavaScript, tout est objet » qu'il est possible de croiser au détour d'un article, il n'en est rien, où plutôt ; cela est partiellement correcte. Les valeurs primitives sont en rapport avec les types de données. Nous allons en discuter plus en détail.

Types de données

Même si le JavaScript est un langage faiblement typé il n'en reste pas moins un langage typé. C'est même un langage avec objet dynamique et dont la conversion de type est piloté par les opérandes utilisés : elle est automatique et implicite, on appel cela de la coercion de type.

Le standard ES3 définit 9 types dont seulement 6 sont directement accessibles depuis un programme JavaScript :

  • Undefined
  • Null
  • Boolean
  • String
  • Number
  • Object

Les 3 autres types sont accessibles uniquement au niveau de l'implémentation (aucun objet en JavaScript ne possède ces types) et sont utilisés par la spécification pour expliquer le comportement de diverses opérations (comme le stockage de valeurs intermédiaires). Ce sont les trois types suivants :

  • Reference
  • List
  • Completion

Pour faire court, Reference est le type utilisé pour expliquer le fonctionnement des opérateurs delete, typeof, this et bien d'autres. Il est constitué d'un objet base et d'un nom de propriété. Le type List décrit le comportement des listes d'arguments (lors de l'utilisation de l'opérateur new dans une expression ou dans les appels de fonctions). Le type Completion est quand à lui utilisé pour expliquer les comportements des structures de contrôle break, continue, return et throw.

Les valeurs primitives

Revenons en à nos six types utilisés dans les programmes JavaScript. Cinq d'entre eux sont des valeurs primitives : Undefined, Null, Boolean, String et Number.

En voici des exemples

Code JavaScript

var sephiroth = undefined;
var youfie = null;
var zack = true;
var cloud = 'test';
var xiii = 13;

Ces valeurs sont implémentées à un très bas niveau. Ce ne sont pas des objets, elles n'ont ni constructeurs, ni prototypes.

L'opérateur typeof peut être contre intuitif s'il n'est pas proprement compris. L'un des meilleurs exemples est celui de la valeur null. Quand null est fourni à l'opérateur typeof, le résultat est "object", indépendamment du fait que la spécification indique que null est de type Null.

Code JavaScript

console.log(typeof null); // `"object"`

La raison en est très simple. L'opérateur typeof retourne simplement une valeur lue depuis un tableau standard disant simplement : « pour la valeur null, la chaîne de caractère "object" doit être retournée ».

Cependant, la spécification ne clarifiant pas ce point, Brendan Eich, l'inventeur du JavaScript expliquera que la différence entre undefined et null s'explique justement avec le type Object, c.-à-d. qu'il est intimement lié aux objets. Le fait est qu'une référence « vide » pour un objet peut être matérialisée par le type Null. Ce détail n'a pas été reporté dans la spécification ECMA-262-3 et actuellement le type Null est bien un type à part entier.

Le type Object

C'est au tour du type Object d'être parcouru (à ne pas confondre avec le constructeur Object dont nous ne parlerons pas dans cet article). C'est le seul type JavaScript qui représente des objets.

Un type Object est une collection non ordonnée de paire de clé-valeur.

Les clés d'un objet sont appelées des propriétés. Les propriétés sont des conteneurs pour des valeurs primitives ou d'autres objets. Si une propriété contient une fonction en tant que valeur (une fonction étant un type Object) on appelle alors cette propriété une méthode.

Code JavaScript

var team = { // objet `team` avec trois propriétés : `xiii`, `cloud` et `sephiroth`
    xiii: 13, // valeur primitive de type Number
    cloud: { hp: 302 }, // objet `cloud` avec la propriété `hp`
    sephiroth: function () { // méthode `sephiroth` (fonction objet)
        console.log('Je suis un glitch');
    }
};

console.log(team.xiii); // `13`
console.log(team.cloud); // `{ hp: 302 }`
console.log(team.cloud.hp); // `302`
team.sephiroth(); // `'Je suis un glitch'`

Nature dynamique

Les objets en JavaScript sont entièrement dynamiques. Cela signifie que nous pouvons ajouter, modifier ou supprimer les propriétés d'un objet à n'importe quel moment lors de l'exécution d'un programme.

Code JavaScript

var cloud = { hp: 302 };

// ajouter une nouvelle propriété
cloud.mp = 54;
console.log(cloud); // `{ hp: 302, mp: 54 }`

// changer la valeur d'une propriété en fonction
cloud.hp = function () {
    console.log('Ce sont les points de vie');
};

cloud.hp(); // `'Ce sont les points de vie'`

// supprimer une propriété
delete cloud.hp;
console.log(cloud); // `{ mp: 54 }`

Plusieurs propriétés ne peuvent pas être modifiées (read-only) ou supprimées (non-configurable). Nous en parlerons dans la sections consacrées aux attributs de propriétés.

Notons que ES5 standardise les objets statiques ne pouvant pas être étendus avec de nouvelles propriétés, modifiés ou supprimés. Ils sont appelés objets gelés (« frozen ») et peuvent être gelés avec la méthode Object.freeze.

Code JavaScript

var cloud = { hp: 302 };

// geler l'objet
Object.freeze(cloud);
console.log(Object.isFrozen(cloud)); // `true`

// il n'est ni modifiable
cloud.hp = 0;

// ni extensible
cloud.mp = 54;

// ni supprimable
delete cloud.hp;
console.log(cloud); // `{ hp: 302 }`

Il est également possible de seulement empêcher l'extension en utilisant la méthode Object.preventExtensions ou de contrôler spécifiquement les attributs avec la méthode Object.defineProperty :

Code JavaScript

var cloud = { hp : 302 };

Object.defineProperty(cloud, 'mp', {
    value: 54,
    writable: false, // lecture seule
    configurable: false // non configurable
});

// ne peut être modifié
cloud.mp = 0;

// ne peut être supprimé
delete cloud.mp; // `false`

// empécher l'extension
Object.preventExtensions(cloud);
console.log(Object.isExtensible(cloud)); // `false`

// on ne peut plus ajouter de nouvelles propriétés
cloud.level = 1;

console.log(cloud); `{ hp: 302, mp: 54 }`

Plus de détail dans ce chapitre.

Objets pré-conçus, natifs et hôtes

Il est nécessaire que la spécification distingue les objets natifs, des objets pré-conçus et des objets hôtes.

Les objets pré-conçus et natifs sont définis par la spécification ECMAScript. La différence exacte entre les deux est minime : les objets natifs sont ceux fournis par l'implémentation du JavaScript. Certains d'entres eux sont pré-conçus et d'autres sont créés pendant l'exécution du programme (comme les objets définis par l'utilisateur).

Ainsi les objets pré-conçus en amont sont un sous type des objets natifs. Ils sont créés par JavaScript en début de programme (par exemple parseInt, Math, etc.).

Tous les objets hôtes sont des objets fournis par l'environnement hôte. On aura donc dans un navigateur, par exemple, window, console.log, etc.

Notez que ces objets hôtes peuvent être implémentés en utilisant intégralement la sémantique de la spécification ECMAScript. Ils sont en quelque sorte des objets « hôtes-natifs » (terme non utilisé dans la spécification).

Les objets Boolean, String et Number

En plus des trois primitives du même nom, la spécification définie des objets englobants spéciaux. Ce sont les trois objets suivants :

  • L'objet Boolean
  • L'objet String
  • L'objet Number

Chaque objet peut être créé avec son constructeur interne et contient dans l'une de ses propriétés internes la valeur primitive correspondante. Ainsi la représentation des objets peut être fournie en valeurs primitives et inversement.

Exemple de valeurs d'objets correspondants aux types primitifs :

Code JavaScript

var zack = new Boolean(true);
var cloud = new String('test');
var xiii = new Number(13);

// conversion en primitive
// utilisation de `ToPrimitive`
// il faut pour cela appliquer
// la fonction sans utiliser l'opérateur `new`
zack = Boolean(zack);
cloud = String(cloud);
xiii = Number(xiii);

// revenir à l'objet
// utilisation de `ToObject`
zack = Object(zack);
cloud = Object(cloud);
xiii = Object(xiii);

De plus, il y a aussi des objets créés par des constructeurs pré-conçus spéciaux comme Function (l'objet constructeur des fonctions), Array (l'objet constructeur des tableaux), RexExp (l'objet constructeur des expressions régulières), Math (le module de mathématique), Date (l'objet constructeur des dates), etc. Ces objets sont aussi du type Object et la distinction entre les uns et les autres est géré par des propriétés internes dont nous allons discuter plus tard.

Notations littérales

Pour trois valeurs d'objets : Object, Array et RegExp, il existe une notation courte qui appelle respectivement un initialiseur d'objet, un initialiseur de tableau et un initialiseur d'expression régulière :

Code JavaScript

// équivalent de `var reactors = new Array(1, 5);`
/* ou `var reactors = new Array();
       reactors[0] = 1;
       reactors[1] = 5;` */
var reactors = [1, 5];

// équivalent de
/* `var location = new Object();
    location.north = 'Midgar';
    location.west = 'Junon';
    location.south = 'Fort Condor';` */
var location = { north: 'Midgar', west: 'Junon', south: 'Fort Condor' };

// équivalent de `var ffvii = new RegExp("^\\d+$", "g");`
var ffvii = /^\d+$/g;

Notons que dans le cas où nous ré-affectons la liaison entre Object, Array ou RegExp a d'autres objets, le résultat de la notation littérale qui en découle peut varier d'une implémentation à l'autre. Par exemple, avec une implémentation, Rhino ou une vieille implémentation SpiderMonkey, les notations littérales vont créées un objet correspondant à la nouvelle valeur du nom de constructeur :

var getClass = Object.prototype.toString;

// Avec le nom Object
Object = Number;

var busterSword = new Object;
console.log([busterSword, getClass.call(busterSword)]); // `0`, `'[object Number]'`

var ultimaWeapon = {};

// dans Rhino ou SpiderMonkey 1.7 : `0`, `'[object Number]'`
// pour les autres : toujours `'[object Object]'`, `'[object Object]'`
console.log([ultimaWeapon, getClass.call(ultimaWeapon)]);

// la même chose avec l'objet Array
Array = Number;

var limits = new Array;
console.log([limits, getClass.call(limits)]); // `0`, `"[object Number]"`

var materias = [];

// dans Rhino ou SpiderMonkey 1.7 : `0`, `'[object Number]'`
// dans les autres : toujours `''`, `'[object Object]'`
console.log([materias, getClass.call(materias)]);

// mais pour `RegExp`, la sémantique litérale
// reste inchangée dans toutes les implémentations
RegExp = Number;

ffvii = new RegExp;
console.log([ffvii, getClass.call(ffvii)]); // `0`, `"[object Number]"`

ff7 = /(?!)/g;
console.log([ff7, getClass.call(ff7)]); // `/(?!)/g`, `"[object RegExp]"`

L'objet RegExp et l'expression régulière litérale

Notons également qu'en ES3, les deux derniers cas avec les expressions régulières sont identiques dans ce qu'ils accomplissent, néanmoins ils diffèrent sur un point. L'expression régulière littérale existe uniquement dans une instance et est créé pendant la phase d'exécution du code, alors que le constructeur RegExp créé toujours un nouvel objet. Cela peut causer quelques problèmes, comme par exemple avec la propriété lastIndex des objets RegExp :

Code JavaScript

for (var k = 0; k < 4; k++) {
    var ffvii = /FF/g;
    console.log(ffvii.lastIndex); // `0`, `2`, `0`, `2`
    console.log(ffvii.test('FF7')); // `true`, `false`, `true`, `false`
}

// par constrate avec

for (var k = 0; k < 4; k++) {
    var ffvii = new RegExp('ecma', 'g');
    console.log(ffvii.lastIndex); // `0`, `0`, `0`, `0`
    console.log(ffvii.test('FF7')); // `true`, `true`, `true`, `true`
}

Notons qu'en ES5 cette particularité a été résolue et ainsi toutes les expressions régulières littérales retournent toujours de nouveaux objets.

Tableaux associatifs ?

Souvent dans divers articles et discussions, les objets JavaScript (et plus particulièrement ceux créés via la forme littérale (ou déclarative) avec l'initialiser {}) sont appelées des tableaux de hachages (« hash-tables ») ou simplement des hashs en Ruby ou Perl, tableaux associatifs (« associative array ») en PHP, dictionnaires en Python, etc.

L'utilisation de ces terminologies est une habitude de nommage venant de ces technologies. Le concept est très similaire à celui d'un objet et respecte le même principe de paire « clé-valeur ». Ils correspondent ainsi à la description théorique des structures de données que sont les tableaux associatifs ou les tableaux de hachages. Cependant, un type de donnée « tableau de hachage » est habituellement utilisé à un niveau implémentation.

Donc, même si cette terminologie est utilisée pour décrire conceptuellement ce mécanisme, il n'est pas techniquement juste en JavaScript de l'utiliser pour parler d'un objet. Comme il a déjà été mentionné plus haut, le JavaScript ne possède qu'un seul type d'objet et tous ses « sous-types » respectent l'approche de stockage de paire « clé-valeur ». Cela n'est jamais différent d'un « sous-type » à l'autre. Il n'y a donc pas de terme spécifique pour décrire un objet en terme de « hash ». N'importe quel objet, indépendamment de ses propriétés internes peut stocker des paires :

Code JavaScript

var cloud = { hp: 302 };
cloud['mp'] = 54;
cloud.level = 1;

var xiii = new Number(12);
xiii.hp = 564;
xiii.mp = 98;
xiii['level'] = 5;

var jenova = new Function('');
jenova.hp = 400;
jenova.mp = 110;
jenova['level'] = 25;

// etc. avec n'importe quel « sous-type » d'objet.

De plus, les objets en JavaScript ne sont jamais uniquement une paire de clé-valeur non vide, le terme « hash » est donc mal choisi :

Code JavaScript

Object.prototype.world = 7;

var game = {}; // créer un « hash » vide

console.log(game['world']); // `7`, qui n'est pourtant pas vide
console.log(game.toString); // `function`

game['type'] = 'RPG'; // ajouter une nouvelle paire au « hash »
console.log(game['type']); // `'RPG'`

Object.prototype.type = 'RPG'; // la propriété se retrouvera dans le prototype

delete game['type']; // supprimé `type`
console.log(game['type']); // `'RPG'`, mais la clé et la valeur sont toujours là

Notons qu'avec ES5, la possibilité de créer des objets vierges sans prototype (c'est à dire que leur prototype est mis à null) a été ajouté. On peut faire cela en utilisant la méthode Object.create(null). De ce point de vu, un objet peut être interprété comme un simple hash :

Code JavaScript

var aHashTable = Object.create(null);
console.log(aHashTable.toString); // `undefined`

Un autre point a noté est que certaines propriétés peuvent avoir uniquement un accesseur ou un mutateur, ce qui rend le tout confus :

var name = new String('ff7');
name['length'] = 10;
console.log(name['length']); // `3`

Et même si l'on considère qu'un hash peut avoir un « prototype » (comme par exemple en Ruby ou Python), en JavaScript cette terminologie est aussi non conforme car il n'y a aucune distinction entre les types d'accesseur de propriété (c.-à-d. entre le point ou les crochets droits).

Ainsi le concept de « propriété » en JavaScript n'est pas différencié du principe de « clé », d'« index de tableau », de « méthode » ou de « propriété ». Ce sont tous des propriétés qui obéissent à la loi commune d'un algorithme de lecture/écriture en examinant la chaîne de prototype.

Dans l'exemple suivant, en Ruby, on voit bien cette distinction sémantique :

Code Ruby

game = {}
game.class # Hash

game.length # `0`

# nouvelle paire de « clé-valeur »
game['length'] = 7;

# mais la sémantique de la notation du point
# est différente et signifie accéder
# à la « propriété / méthode », mais pas à la « clé »

game.length # `1`

# et la notation crochet
# fournit un accès à la « clé » du hash

game['length'] # `7`

# nous pouvons augmenter dynamiquement la classe Hash
# avec de nouvelle propriétés et méthodes et cette délégation
# sera disponible dans les objets déjà créés

class Hash
    def score
        100
    end
end

# nouvelle « propriété » disponible dans `game`

game.score # `100`

# mais pas la « clé »

a['score'] # `nil`

Pour conclure le standard ECMA-262-3 ne définit pas de concept de « hash » (ou similaire). Cependant, puisque cette structure de données colle à la théorie, il est possible de nommer les objets ainsi.

Conversion de type

Pour convertir un objet en une valeur primitive, la méthode valueOf peut être utilisée. Comme nous l'avons noté, l'appel du constructeur (pour certains types) en tant que fonction (c.-à-d. sans l'opérateur new) applique la conversion d'un objet en son équivalent en valeur primitive. Pour cette conversion dite implicite, l'appel de la méthode valueOf est effectué :

Code JavaScript

var game = new Number(7);
var primitiveGame = Number(game); // appel implicite de `valueOf`
var alsoPrimitiveGame = game.valueOf(); // appel explicite de `valueOf`

console.log([
    typeof game, // `'object'`
    typeof primitiveGame, // `'number'`
    typeof alsoPrimitiveGame // `'number'`
]);

Cela permet aux objets de participer à des opérations variées, par exemple aux additions :

Code JavaScript

var ps1 = new Number(7);
var ps3 = new Number(13);

console.log(ps1 + ps3); // `20`

// ou même ainsi

var playstation = {
    ps1: 7,
    ps2: 10,
    valueOf: function () {
        return this.ps1 + this.ps2;
    }
};

var squaresoft = {
    ps1: 7,
    ps2: 13,
    // affectation de la même fonctionnalité
    // `valueOf` que l'objet `playstation`
    valueOf: playstation.valueOf
};

console.log(playstation + squaresoft); // `40`

La valeur de la méthode valueOf par défaut (si elle n'est pas ré-écrite) peut varier en fonction du type d'objet. Pour beaucoup d'objets cela retourne la valeur de this. Par exemple :

Code JavaScript

var soldier = {};
console.log(soldier.valueOf() === soldier); // `true`, `valueOf` de `Object` retourne la valeur `this`

// mais pas pour `Date`

var jenovasFall = new Date();
console.log(jenovasFall.valueOf()); // le temps sous forme de Number
console.log(jenovasFall.valueOf() === jenovasFall.getTime()); // `true`

Il y a aussi une autre représentation primitive d'un objet, une représentation sous forme de chaîne de caractère. Pour cela, c'est la méthode toString qui est utilisée, et qui est automatiquement appelée pour certaines opérations :

Code JavaScript

var game = {
    valueOf: function () {
        return 13;
    },
    toString: function () {
        return 'ff';
    }
};

// dans cette opération
// la méthode `toString` est
// appelée automatiquement
console.log(game); // `'ff'`

// mais ici, c'est la méthode `valueOf`
console.log(game + 7); // `20`

// mais s'il n'existe pas de méthode
// `valueOf` elle sera
// remplacée par
// la méthode `toString`
delete game.valueOf;
console.log(game + 7); // `'ff7'`

La méthode toString définie dans Object.prototype a une signification spéciale. Elle retourne la valeur de la propriété interne [[Class]] dont nous avons discuté plus haut.

De même qu'il y a la conversion ToPrimitive, il y a la conversion ToObject qui à l'inverse transforme une primitive en un type Object.

L'une des manières explicites d'appeler ToObject est d'utiliser le constructeur de Object en tant que fonction (ou avec new dans certain cas).

Code JavaScript

var xiii = Object(13); // `[object Number]`
var cloud = Object('test'); // `[object String]`
var zack = Object(true); // `[object Boolean]`

// il est aussi possible pour certain type d'appeler
// `Object` avec l'opérateur `new`
var Zack = new Object(true); // `[object Boolean]`

// mais en l'utilisant sans argument,
// `new Object()` crée un simple objet
var caitSith = new Object(); // `[object Object]`

// dans le cas ou l'argument pour la fonction `Object`
// est déjà une valeur objet,
// l'objet sera retourné en tant que tel
var reactors = [];
console.log(reactors === new Object(reactors)); // `true`
console.log(reactors === Object(reactors)); // `true`

L'appel d'un constructeur avec new ou sans new ne suit aucune règle générale particulière, cela dépend tout simplement du constructeur. Par exemple les constructeurs Array et Function retournent le même résultat quand leurs constructeurs sont appelés avec new ou en tant que simple fonction (sans new) :

Code JavaScript

var aerith = Array(1, 2, 3); // `[object Array]`
var ancient = new Array(1, 2, 3); // `[object Array]`
var flowergirl = [1, 2, 3]; // `[object Array]`

var calamity = Function(''); // `[object Function]`
var jenova = new Function(''); // `[object Function]`

Il y a aussi des conversions explicites et implicites quand certains opérateurs sont utilisés :

Code JavaScript

var xiii = 4;
var barret = 2;

// implicite
var legs = xiii + barret; // `6`, type Number
var meaningOfLife = '' + xiii + barret; // `"42"`, type String

// explicite
var game = '7'; // `"7"`, type String
var num = +game; // `7`, type Number
var numAgain = parseInt(game, 10); // `7`, type Number

// etc.

Attributs de propriété

Toutes les propriétés peuvent avoir un nombre d'attributs comme suit :

  • {ReadOnly} — toute tentative d'écrire une valeur dans la propriété est ignorée ; cependant, la propriété de lecture seule peut-être changée par une action de l'environnement hôte, ainsi lecture seule ne veut pas dire « valeur constante »,
  • {DontEnum} — la propriété ne peut pas être énumérée dans une boucle for..in,
  • {DontDelete} — l'action de l'opérateur delete appliquée est ignorée,
  • {Internal} — la propriété est interne, elle n'a pas de nom et est utilisée uniquement au niveau de l'implémentation, aussi ces propriétés ne sont pas accessibles en JavaScript.

Notez qu'en ES5 les propriétés {ReadOnly}, {DontEnum} et {DontDelete} on été renommé en [[Writable]], [[Enumerable]] et [[Configurable]] et celle-ci peuvent être manuellement gérer via la méthode Object.defineProperty ou d'autres méthodes similaires :

Code JavaScript

var cloud = {};

Object.defineProperty(cloud, 'hp', {
    value: 302,
    writable: true, // aka `{ReadOnly} = false`
    enumerable: false, // aka `{DontEnum} = true`
    configurable: true // aka `{DontDelete} = false`
});

console.log(cloud.hp); // `302`

// ce lot d'attributs est appelé un descripteur
var desc = Object.getOwnPropertyDescriptor(cloud, 'hp');

console.log(desc.enumerable); // `false`
console.log(desc.writable); // `true`
// etc.

Propriétés et méthodes internes

Les objets possèdent également un certain nombre de propriétés qui sont inaccessibles depuis un programme JavaScript directement (cependant comme nous allons le voir plus bas, certaines implémentations permettent d'avoir accès à ses propriétés). Ces propriétés sont entourés des doubles crochets droits par convention : [[ ]].

Nous allons nous intéresser à certaines d'entres elles (qui sont obligatoires pour tous les objets) : la description d'autres propriétés pouvant être trouvée dans la spécification.

Chaque object doit implémenter les propriétés et méthodes internes suivantes :

  • [[Prototype]] — le prototype de cet objet (il sera étudié plus en détail plus bas),
  • [[Class]] — une représentation en chaîne de caractère du type (par exemple Object, Array, Function, etc.). Il est utilisé pour distinguer les objets,
  • [[Get]] — une méthode pour donner la valeur de la propriété,
  • [[Put]] — une méthode pour affecter la valeur de la propriété,
  • [[CanPut]] — indique de quelle manière cette propriété peut être ré-écrite,
  • [[HasProperty]] — indique si cette propriété appartient déjà à l'objet,
  • [[Delete]] — retire la propriété de l'objet
  • [[DefaultValue]] — retourne une valeur primitive correspondant à l'objet (pour donner cette valeur la méthode valueOf est appelée, pour certain objet, l'exception TypeError peut être levée).

Pour obtenir la propriété [[Class]] d'un programme JavaScript il est possible de la lire indirectement via la méthode Object.prototype.toString(). Cette méthode devrait retourner la chaîne de caractère suivante : "[object " + [[Class]] + "]". Par exemple :

Code JavaScript

var getClass = Object.prototype.toString;

getClass.call({}); // `[object Object]`
getClass.call([]); // `[object Array]`
getClass.call(new Number(1)); // `[object Number]`

// etc.

Cette fonctionnalité est souvent utilisée pour vérifier le type de l'objet, cependant, il est nécessaire de préciser que la spécification autorise n'importe quelle chaîne de caractère valide et qu'il n'est pas sur que tous les retours respectent cette convention. Par exemple, la propriété [[Class]] de la méthode document.childNodes.item(...) retourne 'String' dans des vieux IE (et dans d'autres implémentations "Function" est retourné) :

Code JavaScript

// dans de vieux IE : `'String'`, dans d'autres `'Function'`
console.log(getClass.call(document.childNodes.item));

Conclusion

Nous venons de voir que le typage en JavaScript était loin d'être trivial. Les détails vus ici vont nous permettre de nous attaquer aux parties plus complexes du type Object, à savoir le constructeur et le prototype.

Références

Section correspondante de la spécification ECMA-262-3 :

Ce texte est une libre adaptation française d'une partie de l'excellent billet Тонкости ECMA-262-3. Часть 7.2. ООП: Реализация в ECMAScript. de Dmitry Soshnikov.

Lire dans une autre langue