variable es6 ejemplos clases attribute javascript constructor ecmascript-6 instance

javascript - ejemplos - ES6: constructor de clase de llamada sin nueva palabra clave



javascript class properties (15)

Acabo de hacer este módulo npm para ti;)

https://www.npmjs.com/package/classy-decorator

import classy from "classy-decorator"; @classy() class IamClassy { constructor() { console.log("IamClassy Instance!"); } } console.log(new IamClassy() instanceof IamClassy); // true console.log(IamClassy() instanceof IamClassy); // true

Dada una clase simple

class Foo { constructor(x) { if (!(this instanceof Foo)) return new Foo(x); this.x = x; } hello() { return `hello ${this.x}`; } }

¿Es posible llamar al constructor de la clase sin la new palabra clave?

El uso debe permitir

(new Foo("world")).hello(); // "hello world"

O

Foo("world").hello(); // "hello world"

Pero este último falla con

Cannot call a class as a function


Aquí hay un lugar donde puede usar un ''constructor seguro de alcance''. Observe este código:

function Student(name) { if(this instanceof Student) { this.name = name; } else { return new Student(name); } }

Ahora puede crear un objeto Estudiante sin usar nuevo de la siguiente manera:

var stud1 = Student(''Kia'');


Aquí hay un patrón que he encontrado que realmente me ayuda. No usa una class , pero tampoco requiere el uso de new . Ganar / ganar.

const Foo = x => ({ x, hello: () => `hello ${x}`, increment: () => Foo(x + 1), add: ({x: y}) => Foo(x + y) }) console.log(Foo(1).x) // 1 console.log(Foo(1).hello()) // hello 1 console.log(Foo(1).increment().hello()) // hello 2 console.log(Foo(1).add(Foo(2)).hello()) // hello 3


Bien, tengo otra respuesta aquí, y creo que esta es bastante innovadora.

Básicamente, el problema de hacer algo similar a la respuesta de Naomik es que usted crea funciones cada vez que encadena métodos juntos.

EDITAR: Esta solución comparte el mismo problema, sin embargo, esta respuesta se deja para fines educativos.

Así que aquí estoy ofreciendo una manera de vincular simplemente nuevos valores a sus métodos, que son básicamente funciones independientes. Esto ofrece el beneficio adicional de poder importar funciones de diferentes módulos en el objeto recién construido.

De acuerdo, así que aquí va.

const assoc = (prop, value, obj) => Object.assign({},obj,{[prop]: value}) const reducer = ( $values, accumulate, [key,val] ) => assoc( key, val.bind( undefined,...$values ), accumulate ) const bindValuesToMethods = ( $methods, ...$values ) => Object.entries( $methods ).reduce( reducer.bind( undefined, ...$values), {} ) const prepareInstance = (instanceMethods, staticMethods = ({}) ) => Object.assign( bindValuesToMethods.bind( undefined, instanceMethods ), staticMethods ) // Let''s make our class-like function const RightInstanceMethods = ({ chain: (x,f) => f(x), map: (x,f) => Right(f(x)), fold: (x,l,r) => r(x), inspect: (x) => `Right(${x})` }) const RightStaticMethods = ({ of: x => Right(x) }) const Right = prepareInstance(RightInstanceMethods,RightStaticMethods)

Ahora puedes hacer

Right(4) .map(x=>x+1) .map(x=>x*2) .inspect()

Tambien puedes hacer

Right.of(4) .map(x=>x+1) .map(x=>x*2) .inspect()

También tiene el beneficio adicional de poder exportar desde módulos como tal

export const Right = prepareInstance(RightInstanceMethods,RightStaticMethods)

Si bien no obtiene ClassInstance.constructor , sí tiene FunctorInstance.name (tenga en cuenta que es posible que deba rellenar Function.name y / o no usar una función de flecha para exportar para la compatibilidad del navegador con fines de Function.name )

export function Right(...args){ return prepareInstance(RightInstanceMethods,RightStaticMethods)(...args) }

PD: nuevas sugerencias de nombres para preparar la entrada bienvenida, ver Gist.

https://gist.github.com/babakness/56da19ba85e0eaa43ae5577bc0064456


Como otros han señalado, la especificación ES2015 establece estrictamente que dicha llamada debería arrojar TypeError, pero al mismo tiempo proporciona una función que se puede utilizar para lograr exactamente el resultado deseado, es decir, Proxies .

Proxies nos permite virtualizar sobre un concepto de un objeto. Por ejemplo, se pueden usar para cambiar algún comportamiento de un objeto en particular sin afectar nada más.

En su caso de uso específico, la class Foo es un Function object que se puede invocar; esto normalmente significa que se ejecutará el cuerpo de esta función. Pero esto se puede cambiar con Proxy :

const _Foo = new Proxy(Foo, { // target = Foo apply (target, thisArg, argumentsList) { return new target(...argumentsList); } }); _Foo("world").hello(); const f = _Foo("world"); f instanceof Foo; // true f instanceof _Foo; // true

(Tenga en cuenta que _Foo ahora es la clase que desea exponer, por lo que los identificadores probablemente deberían ser al revés)

Si lo ejecuta un navegador que admite Proxies, al llamar a _Foo(...) ahora se ejecutará la función de captura de apply lugar del constructor original.

Al mismo tiempo, esta "nueva" clase _Foo es indistinguible de la Foo original (aparte de poder llamarla como una función normal). Del mismo modo, no hay diferencia por la cual pueda distinguir objetos creados con Foo y _Foo .

El mayor inconveniente de esto es que no puede ser transpilado o polifundido , pero sigue siendo su solución viable para que la clase tipo Scala se aplique en JS en el futuro.


Desenterró este en el borrador

Constructores definidos usando la sintaxis de definición de clase cuando se llaman como funciones

Así que supongo que eso no es posible con las clases.


El constructor de la clase de llamada manualmente puede ser útil cuando se refactoriza el código (que tiene partes del código en ES6, otras partes de la función beeing y definición del prototipo)

Terminé con un pequeño, pero útil repetitivo, dividiendo el constructor en otra función. Período.

class Foo { constructor() { //as i will not be able to call the constructor, just move everything to initialize this.initialize.apply(this, arguments) } initialize() { this.stuff = {}; //whatever you want } } function Bar () { Foo.prototype.initialize.call(this); } Bar.prototype.stuff = function() {}


Esto puede ser un poco artificial, pero funciona

function Foo(x){ "use strict" class Bar { constructor(x) { if (!(this instanceof Bar)) return new Bar(x); this.x = x; } hello() { return `hello ${this.x}`; } } return new Bar(x) } Foo("world").hello()


Estoy agregando esto como seguimiento a un comentario de naomik y utilizo el método ilustrado por Tim y Bergi. También voy a sugerir una función para usar como un caso general.

Para hacer esto de una manera funcional Y utilizar la eficiencia de los prototipos (no volver a crear todo el método cada vez que se crea una nueva instancia), se podría usar este patrón

const Foo = function(x){ this._value = x ... } Foo.of = function(x){ return new Foo(x) } Foo.prototype = { increment(){ return Foo.of(this._value + 1) }, ... }

Tenga en cuenta que esto es consistente con fantasy-land especificaciones JS de fantasy-land Land

https://github.com/fantasyland/fantasy-land#of-method

Personalmente, creo que es más limpio usar la sintaxis de clase ES6

class Foo { static of(x) { new Foo(x)} constructor(x) { this._value = x } increment() { Foo.of(this._value+1) } }

Ahora uno podría envolver esto en un cierre como tal

class Foo { static of(x) { new _Foo(x)} constructor(x) { this._value = x } increment() { Foo.of(this._value+1) } } function FooOf (x) { return Foo.of(x) }

O cambie el nombre de FooOf y Foo como desee, es decir, la clase podría ser FooClass y la función solo Foo , etc.

Esto es mejor que colocar la clase en la función porque crear nuevas instancias no nos agobia con la creación de nuevas clases también.

Otra forma más es crear una función

const of = (classObj,...args) => ( classObj.of ? classObj.of(value) : new classObj(args) )

Y luego hacer algo como of(Foo,5).increment()


Las clases tienen un "cuerpo de clase" que es un constructor .
Si usa una función interna constructor() , esa función también sería el mismo cuerpo de clase, y sería lo que se llama cuando se llama a la clase, por lo tanto, una clase siempre es un constructor.

Los constructores requieren el uso del new operador para crear una nueva instancia, por lo que invocar una clase sin el new operador genera un error, ya que el constructor de la clase debe crear una nueva instancia.

El mensaje de error también es bastante específico y correcto.

TypeError: los constructores de clase no se pueden invocar sin ''nuevo''

Tú podrías;

  • use una función regular en lugar de una clase 1 .
  • Siempre llame a la clase con new .
  • Llame a la clase dentro de una función regular de ajuste, siempre usando new , de esa manera obtendrá los beneficios de las clases, pero la función de ajuste aún se puede llamar con y sin el new operador 2 .

1)

function Foo(x) { if (!(this instanceof Foo)) return new Foo(x); this.x = x; this.hello = function() { return this.x; } }

2)

class Foo { constructor(x) { this.x = x; } hello() { return `hello ${this.x}`; } } var _old = Foo; Foo = function(...args) { return new _old(...args) };


No es posible llamar al constructor de la clase sin la new palabra clave.

El mensaje de error es bastante específico.

Vea una publicación de blog sobre 2ality y las spec :

However, you can only invoke a class via new, not via a function call (Sect. 9.2.2 in the spec): > Point() TypeError: Classes can’t be function-called


No puede usar una clase sin el nuevo constructor, en mi caso no quería usar el new constructor en cualquier momento que quisiera usar mi clase, así que lo que puede hacer es ajustar su clase de la siguiente manera (en mi caso es una biblioteca de Dates utils):


No, esto no es posible. Los constructores que se crean con la palabra clave de class solo se pueden construir con new , si se editan [[call]]ed sin que siempre throw un TypeError 1 (y ni siquiera hay una forma de detectar esto desde el exterior).
1: No estoy seguro de si los transpiladores hacen esto bien

Sin embargo, puede usar una función normal como solución alternativa:

class Foo { constructor(x) { this.x = x; } hello() { return `hello ${this.x}`; } } { const _Foo = Foo; Foo = function(...args) { return new _Foo(...args); }; Foo.prototype = _Foo.prototype; }

Descargo de responsabilidad: la instanceof y la extensión de Foo.prototype funcionan de manera normal, Foo.length no, .constructor y los métodos estáticos no lo hacen, pero pueden solucionarse agregando Foo.prototype.constructor = Foo; y Object.setPrototypeOf(Foo, _Foo) si es necesario.

Para subclasificar Foo (no _Foo ) con la class Bar extends Foo … , debe usar return Reflect.construct(_Foo, args, new.target) lugar de la new _Foo llamada new _Foo . Subclasificar en estilo ES5 (con Foo.call(this, …) ) no es posible.


Tuve problemas para extender las clases convertidas con la función de transformación mencionada en algunas otras respuestas. El problema parece ser que el nodo (a partir de v9.4.0) no admite correctamente el operador de propagación de argumentos ( (...args) => ).

Esta función basada en la salida transpilada del classy-decorator (mencionado en otra respuesta ) funciona para mí y no requiere soporte para decoradores o el operador de propagación de argumentos.

// function that calls `new` for you on class constructors, simply call // YourClass = bindNew(YourClass) function bindNew(Class) { function _Class() { for ( var len = arguments.length, rest = Array(len), key = 0; key < len; key++ ) { rest[key] = arguments[key]; } return new (Function.prototype.bind.apply(Class, [null].concat(rest)))(); } _Class.prototype = Class.prototype; return _Class; }

Uso:

class X {} X = bindNew(X); // or const Y = bindNew(class Y {}); const x = new X(); const x2 = X(); // woohoo x instanceof X; // true x2 instanceof X; // true class Z extends X {} // works too

Como beneficio adicional, TypeScript (con salida "es5") parece estar bien con el viejo truco de instanceof (bueno, no escribirá verificación si se usa sin new pero funciona de todos modos):

class X { constructor() { if (!(this instanceof X)) { return new X(); } } }

porque lo compila en:

var X = /** @class */ (function () { function X() { if (!(this instanceof X)) { return new X(); } } return X; }());


class MyClass { constructor(param) { // ... } static create(param) { return new MyClass(param); } doSomething() { // ... } } MyClass.create(''Hello World'').doSomething();

¿Es eso lo que quieres?

Si necesita algo de lógica al crear una nueva instancia de MyClass , podría ser útil implementar una "CreationStrategy" para superar la lógica:

class MyClassCreationStrategy { static create(param) { let instance = new MyClass(); if (!param) { // eg. handle empty param } instance.setParam(param); return instance; } } class DefaultCreationStrategy { static create(classConstruct) { return new classConstruct(); } } MyClassCreationStrategy.create(param).doSomething(); DefaultCreationStrategy.create(MyClass).doSomething();