Reflect.construct()
Baseline Widely available
This feature is well established and works across many devices and browser versions. It’s been available across browsers since September 2016.
La méthode statique Reflect.construct()
agit comme l'opérateur new
sous la forme d'une fonction. Elle est équivalente à new cible(...args)
et permet d'indiquer une valeur différente pour new.target
.
Exemple interactif
Syntaxe
Reflect.construct(cible, listeArguments)
Reflect.construct(cible, listeArguments, nouvelleCible)
Paramètres
cible
-
La fonction cible à appeler.
listeArguments
-
Un objet semblable à un tableau définissant les arguments à passer à
cible
lors de l'appel. nouvelleCible
Facultatif-
La valeur de l'expression
new.target
pourcible
.cible
indique la logique d'initialisation de l'objet, tandis quenouvelleCible.prototype
indique le prototype de l'objet construit.
Valeur de retour
Une nouvelle instance de la cible indiquée, créée en l'appelant comme un constructeur (ou en appelant nouvelleCible
si elle est fournie) avec les arguments fournis.
Exceptions levées
TypeError
-
Levée si
cible
ounouvelleCible
ne sont pas des constructeurs, ou silisteArguments
n'est pas un objet.
Description
Reflect.construct()
fournit la sémantique réflective d'un appel à un constructeur. Autrement dit, Reflect.construct(cible, listeArguments, nouvelleCible)
est sémantiquement équivalent à :
new cible(...listeArguments);
Quand on utilise l'opérateur new
, cible
et nouvelleCible
sont toujours les mêmes constructeurs. Reflect.construct()
permet quant à elle de passer une valeur différente pour new.target
. Conceptuellement, nouvelleCible
est la fonction sur laquelle new
est appelé et nouvelleCible.prototype
devient le prototype de l'objet construit, tandis que cible
est le constructeur effectivement exécuté pour l'initialisation de l'objet. On peut par exemple avoir une valeur new.target
différente du constructeur exécuté pour l'héritage de classe :
class A {
constructor() {
console.log(new.target.name);
}
}
class B extends A {}
new B(); // "B"
Reflect.construct()
permet d'invoquer un constructeur avec un nombre variable d'arguments (ce qui est également possible lors d'un appel à un constructeur normal grâce à la syntaxe de décomposition).
const obj = new Toto(...args);
const obj = Reflect.construct(Toto, args);
Reflect.construct()
invoque la méthode interne [[Construct]]
de cible
.
Exemples
Utiliser Reflect.construct()
const d = Reflect.construct(Date, [1776, 6, 4]);
d instanceof Date; // true
d.getFullYear(); // 1776
Utiliser le paramètre nouvelleCible
Si le paramètre nouvelleCible
est utilisé, il modifiera la valeur de new.target
dans le constructeur. L'objet ainsi construit sera une instance de nouvelleCible
, pas de cible
.
function ClasseUne() {
console.log("ClasseUne exécutée");
console.log(`new.target vaut ${new.target.name}`);
}
function AutreClasse() {
console.log("AutreClasse exécutée");
console.log(`new.target vaut ${new.target.name}`);
}
const obj1 = Reflect.construct(ClasseUne, []);
// Affiche dans la console :
// ClasseUne exécutée
// new.target vaut ClasseUne
console.log(obj1 instanceof ClasseUne); // true
const obj2 = Reflect.construct(ClasseUne, [], AutreClasse);
// Affiche dans la console :
// ClasseUne exécutée
// new.target vaut AutreClasse
console.log(obj2 instanceof AutreClasse); // true
console.log(obj2 instanceof ClasseUne); // false
Il n'y a pas de garantie forte quant à la chaîne de prototypes de l'objet construit, car celle-ci dépend de l'implémentation du constructeur. Par exemple, si le constructeur cible
renvoie un objet, c'est cet objet qui sera l'objet construit, quelle que soit la valeur de nouvelleCible
. Si cible
est un proxy avec une trappe pour construct
, ce sera la trappe qui contrôlera complètement le processus de construction.
function ClasseUne() {
return { name: "un" };
}
function AutreClasse() {
return { name: "autre" };
}
const obj1 = Reflect.construct(ClasseUne, [], AutreClasse);
console.log(obj1.name); // 'un'
console.log(obj1 instanceof ClasseUne); // false
console.log(obj1 instanceof AutreClasse); // false
Pour être tout à fait valide, new.target
devrait être une fonction constructrice avec une propriété prototype
, mais cette règle n'est pas imposée. Si la valeur de la propriété prototype
n'est pas un objet, l'objet initialisé héritera de Object.prototype
.
function ClasseUne() {
console.log("ClasseUne exécutée");
console.log(`new.target vaut ${new.target.name}`);
}
function AutreClasse() {
console.log("AutreClasse exécutée");
console.log(`new.target vaut ${new.target.name}`);
}
AutreClasse.prototype = null;
const obj = Reflect.construct(ClasseUne, [], AutreClasse);
// Affiche dans la console :
// ClasseUne exécutée
// new.target vaut AutreClasse
console.log(Object.getPrototypeOf(obj) === Object.prototype); // true
Une comparaison entre Reflect.construct()
et Object.create()
Avant l'apparition de Reflect
, on pouvait construire des objets avec une combinaison donnée de consttructeur et de prototype grâce à Object.create()
.
function MaClasseA() {
this.name = "A";
}
function MaClasseB() {
this.name = "B";
}
const args = [];
const obj1 = Reflect.construct(MaClasseA, args, MaClasseB);
const obj2 = Object.create(MaClasseB.prototype);
MaClasseA.apply(obj2, args);
console.log(obj1.name); // 'A'
console.log(obj2.name); // 'A'
console.log(obj1 instanceof MaClasseA); // false
console.log(obj2 instanceof MaClasseA); // false
console.log(obj1 instanceof MaClasseB); // true
console.log(obj2 instanceof MaClasseB); // true
Toutefois, si les résultats sont identiques, il y a une différence notable. Lorsqu'on utilise Object.create()
et Function.prototype.apply()
, l'opérateur new.target
pointe vers undefined
dans la fonction utilisée comme constructeur, car le mot-clé new
n'est pas utilisé à la création de l'objet (dans cette situation, c'est la même sémantique que apply()
qui s'applique, pas celle de construct()
, même si les fonctions normales se comportent presque identiquement).
En revanche, quand on appelle Reflect.construct()
, new.target
pointe vers la valeur fournie par nouvelleCible
si elle indiquée ou vers cible
sinon.
function MaClasseA() {
console.log("MaClasseA");
console.log(new.target);
}
function MaClasseB() {
console.log("MaClasseB");
console.log(new.target);
}
const obj1 = Reflect.construct(MaClasseA, args);
// Affiche dans la console :
// MaClasseA
// function MaClasseA { ... }
const obj2 = Reflect.construct(MaClasseA, args, MaClasseB);
// Affiche dans la console :
// MaClasseA
// function MaClasseB { ... }
const obj3 = Object.create(MaClasseB.prototype);
MaClasseA.apply(obj3, args);
// Affiche dans la console :
// MaClasseA
// undefined
Spécifications
Specification |
---|
ECMAScript Language Specification # sec-reflect.construct |
Compatibilité des navigateurs
BCD tables only load in the browser