tipos recorrer objetos funciones ejemplos ejemplo autoejecutables array javascript constructor

funciones - recorrer array de objetos javascript



¿Cómo detectar si una función se llama como constructor? (20)

NOTA: Esta respuesta se escribió en 2008 , cuando JavaScript aún estaba en ES3 desde 1999 . Se ha agregado una gran cantidad de nuevas funcionalidades desde entonces, por lo que ahora existen mejores soluciones. Esta respuesta se guarda por razones históricas.

El beneficio del siguiente código es que no necesita especificar el nombre de la función dos veces y también funciona para funciones anónimas.

function x() { if ( (this instanceof arguments.callee) ) { alert("called as constructor"); } else { alert("called as function"); } }

Actualización Como ha señalado en un comentario a continuación, el código anterior no funciona si asigna el constructor al mismo objeto que ha creado. Nunca he escrito un código que lo haga y he visto más nuevo que alguien más lo haya hecho.

Ejemplo de Claudio:

var Z = new x(); Z.lolol = x; Z.lolol();

Al agregar una propiedad al objeto, es posible detectar si el objeto se ha inicializado.

function x() { if ( (this instanceof arguments.callee && !this.hasOwnProperty("__ClaudiusCornerCase")) ) { this.__ClaudiusCornerCase=1; alert("called as constructor"); } else { alert("called as function"); } }

Incluso el código anterior se romperá si elimina la propiedad agregada. Sin embargo, puede sobrescribirlo con cualquier valor que desee, incluido undefined , y todavía funciona. Pero si lo eliminas, se romperá.

En este momento no hay soporte nativo en ecmascript para detectar si se llamó a una función como constructor. Esto es lo más cercano que he encontrado hasta ahora, y debería funcionar a menos que elimines la propiedad.

Dada una función:

function x(arg) { return 30; }

Puedes llamarlo de dos maneras:

result = x(4); result = new x(4);

El primero devuelve 30, el segundo devuelve un objeto.

¿Cómo se puede detectar en qué forma se llamó a la función dentro de la función misma ?

Cualquiera que sea su solución, debe funcionar también con la siguiente invocación:

var Z = new x(); Z.lolol = x; Z.lolol();

Todas las soluciones actualmente piensan que Z.lolol() lo llama como un constructor.


1) Usted puede verificar this.constructor :

function x(y) { if (this.constructor == x) alert(''called with new''); else alert(''called as function''); }

2) Sí, el valor de retorno simplemente se descarta cuando se usa en el new contexto


A partir de ECMAScript 6, esto es posible con new.target . new.target se establecerá si la función se llama con new (o con Reflect.construct , que actúa como new ), de lo contrario no está undefined .

function Foo() { if (new.target) { console.log(''called with new''); } else { console.log(''not called with new''); } } new Foo(); // "called with new" Foo(); // "not called with new" Foo.call({}); // "not called with new"


Aunque este hilo es antiguo, me sorprende que nadie haya mencionado que bajo el modo estricto ( ''use strict'' ) el valor predeterminado de una función es indefinido, en lugar de establecerlo en global / window como antes, para comprobar si no se usa new simplemente prueba el valor de falsey de !this - EG:

function ctor() { ''use strict''; if (typeof this === ''undefined'') console.log(''Function called under strict mode (this == undefined)''); else if (this == (window || global)) console.log(''Function called normally (this == window)''); else if (this instanceof ctor) console.log(''Function called with new (this == instance)''); return this; }

Si prueba esa función tal como está, no obtendrá la definición de this valor, debido a la directiva ''use strict'' al comienzo de la función. Por supuesto, si ya tiene el modo estricto activado, no cambiará si elimina la directiva ''use strict'' , pero de lo contrario, si la elimina, this valor se configurará como window o global . Si usa new para llamar a la función, this valor coincidirá con la instancia de verificación (aunque si verificó las otras cosas, la instancia es la última opción, por lo que no es necesaria y debe evitarse si desea heredar instancias de todos modos)

function ctor() { ''use strict''; if (!this) return ctor.apply(Object.create(ctor.prototype), arguments); console.log([this].concat([].slice.call(arguments))); return this; }

Esto registrará this valor y cualquier argumento que pase a la función para consolar, y devolverá this valor. Si this valor es falsey entonces crea una nueva instancia usando Object.create(ctor.prototype) y usa Function.apply() para volver a llamar al constructor con los mismos params pero con la instancia correcta como this . Si this valor no es falsey , se supone que es una instancia válida y se devuelve.


Comprobar el tipo de instancia de [this] dentro del constructor es el camino a seguir. El problema es que sin más preámbulos este enfoque es propenso a errores. Sin embargo, hay una solución.

Digamos que estamos tratando con la función ClassA (). El enfoque rudimentario es:

function ClassA() { if (this instanceof arguments.callee) { console.log("called as a constructor"); } else { console.log("called as a function"); } }

Hay varios medios que la solución mencionada anteriormente no funcionará como se esperaba. Considera solo estos dos:

var instance = new ClassA; instance.classAFunction = ClassA; instance.classAFunction(); // <-- this will appear as constructor call ClassA.apply(instance); //<-- this too

Para superar esto, algunos sugieren que, o bien a) coloque alguna información en un campo de la instancia, como "Constructor Finalizado" y vuelva a verificarlo o b) mantenga un registro de sus objetos construidos en una lista. No me siento cómodo con ambos, ya que alterar cada instancia de ClassA es demasiado invasivo y costoso para que funcione una característica relacionada con el tipo. La recopilación de todos los objetos en una lista puede proporcionar recolección de basura y problemas de recursos si ClassA tendrá muchas instancias.

El camino a seguir es poder controlar la ejecución de su función ClassA. El enfoque simple es:

function createConstructor(typeFunction) { return typeFunction.bind({}); } var ClassA = createConstructor( function ClassA() { if (this instanceof arguments.callee) { console.log("called as a function"); return; } console.log("called as a constructor"); }); var instance = new ClassA();

Esto evitará efectivamente todos los intentos de engañar con el [este] valor. Una función vinculada siempre mantendrá su contexto original [this] a menos que lo llame con el nuevo operador.

La versión avanzada devuelve la capacidad de aplicar el constructor en objetos arbitrarios. Algunos usos podrían ser usar el constructor como un convertidor de tipo o proporcionar una cadena invocable de constructores de clase base en escenarios de herencia.

function createConstructor(typeFunction) { var result = typeFunction.bind({}); result.apply = function (ths, args) { try { typeFunction.inApplyMode = true; typeFunction.apply(ths, args); } finally { delete typeFunction.inApplyMode; } }; return result; } var ClassA = createConstructor( function ClassA() { if (this instanceof arguments.callee && !arguments.callee.inApplyMode) { console.log("called as a constructor"); } else { console.log("called as a function"); } });


De John Resig:

function makecls() { return function(args) { if( this instanceof arguments.callee) { if ( typeof this.init == "function") this.init.apply(this, args.callee ? args : arguments) }else{ return new arguments.callee(args); } }; } var User = makecls(); User.prototype.init = function(first, last){ this.name = first + last; }; var user = User("John", "Resig"); user.name


De dos maneras, esencialmente lo mismo bajo el capó. Puedes probar cuál es el alcance de this o puedes probar qué es this.constructor .

Si llamó a un método como constructor, this será una nueva instancia de la clase, si llama al método como método, this será el objeto de contexto de los métodos. De forma similar, el constructor de un objeto será el método en sí mismo si se lo llama como nuevo, y el constructor de objetos del sistema en caso contrario. Eso es claro como el barro, pero esto debería ayudar:

var a = {}; a.foo = function () { if(this==a) //''a'' because the context of foo is the parent ''a'' { //method call } else { //constructor call } } var bar = function () { if(this==window) //and ''window'' is the default context here { //method call } else { //constructor call } } a.baz = function () { if(this.constructor==a.baz); //or whatever chain you need to reference this method { //constructor call } else { //method call } }



Extendiendo la solución de Gregs, esta funciona perfectamente con los casos de prueba que proporcionó:

function x(y) { if( this.constructor == arguments.callee && !this._constructed ) { this._constructed = true; alert(''called with new''); } else { alert(''called as function''); } }

EDITAR: agregar algunos casos de prueba

x(4); // OK, function var X = new x(4); // OK, new var Z = new x(); // OK, new Z.lolol = x; Z.lolol(); // OK, function var Y = x; Y(); // OK, function var y = new Y(); // OK, new y.lolol = Y; y.lolol(); // OK, function


Hasta que vi este hilo, nunca consideré que el constructor podría ser una propiedad de una instancia, pero creo que el siguiente código cubre ese escenario raro.

// Store instances in a variable to compare against the current this // Based on Tim Down''s solution where instances are tracked var Klass = (function () { // Store references to each instance in a "class"-level closure var instances = []; // The actual constructor function return function () { if (this instanceof Klass && instances.indexOf(this) === -1) { instances.push(this); console.log("constructor"); } else { console.log("not constructor"); } }; }()); var instance = new Klass(); // "constructor" instance.klass = Klass; instance.klass(); // "not constructor"

Para la mayoría de los casos, probablemente solo verifique instanceof.


No hay una manera confiable de distinguir cómo se llama una función en el código JavaScript. 1

Sin embargo, una llamada de función tendrá this asignado al objeto global, mientras que un constructor tendrá this asignado a un nuevo objeto. Este nuevo objeto nunca puede ser el objeto global, porque incluso si una implementación le permite establecer el objeto global, aún no ha tenido la oportunidad de hacerlo.

Puede obtener el objeto global haciendo que una función llamada como función (heh) lo devuelva.

Mi intuición es que en la especificación de ECMAScript 1.3, se supone que los constructores que tienen un comportamiento definido para cuando se llama como una función distinguen cómo se llamaron usando esta comparación:

function MyClass () { if ( this === (function () { return this; })() ) { // called as a function } else { // called as a constructor } }

De todos modos, cualquiera puede usar la función o la call de un constructor o apply y configurar this para cualquier cosa. Pero de esta manera, puedes evitar "inicializar" el objeto global:

function MyClass () { if ( this === (function () { return this; })() ) { // Maybe the caller forgot the "new" keyword return new MyClass(); } else { // initialize } }

1. El host (implementación aka) puede notar la diferencia, si implementa el equivalente a las propiedades internas [[Call]] y [[Construct]] . El primero se invoca para expresiones de función o método, mientras que el segundo se invoca para new expresiones.


Si estás haciendo un hack, entonces instanceof es la solución mínima después de new.target como por otras respuestas. Pero al usar la solución instanceof fallaría con este ejemplo:

let inst = new x; x.call(inst);

Combinando con la solución @TimDown, puede usar WeakSet de ES6 si desea compatibilidad con versiones anteriores de ECMAScript para evitar poner propiedades dentro de las instancias. Bueno, WeakSet se usará para permitir que los objetos no utilizados sean basura recolectada. new.target no será compatible en el mismo código fuente, ya que es una característica de sintaxis de ES6. ECMAScript especifica que los identificadores no pueden ser una de las palabras reservadas, y que el new no es un objeto, de todos modos.

(function factory() { ''use strict''; var log = console.log; function x() { log(isConstructing(this) ? ''Constructing'' : ''Not constructing'' ); } var isConstructing, tracks; var hasOwnProperty = {}.hasOwnProperty; if (typeof WeakMap === ''function'') { tracks = new WeakSet; isConstructing = function(inst) { if (inst instanceof x) { return tracks.has(inst) ? false : !!tracks.add(inst); } return false; } } else { isConstructing = function(inst) { return inst._constructed ? false : inst._constructed = true; }; } var z = new x; // Constructing x.call(z) // Not constructing })();

El operador instanceof ECMAScript 3 se specified como:

11.8.6 El operador de instanceof
--- La producción RelationalExpression: RelationalExpression instancia de ShiftExpression se evalúa de la siguiente manera:
--- 1. Evaluar RelationalExpression.
--- 2. Llame a GetValue (Resultado (1)).
--- 3. Evalúa ShiftExpression.
--- 4. Llamar a GetValue (Resultado (3)).
--- 5. Si Result (4) no es un objeto, ejecute una excepción TypeError .
--- 6. Si Result (4) no tiene un método [[HasInstance]], ejecute una excepción TypeError .
--- 7. Llame al método [[HasInstance]] del Resultado (4) con el parámetro Resultado (2).
--- 8. Resultado de devolución (7).
15.3.5.3 [[HasInstance]] (V)
--- Suponer que F es un objeto Function.
--- Cuando se llama al método [[HasInstance]] de F con el valor V, se siguen los siguientes pasos:
--- 1. Si V no es un objeto, devuelve falso .
--- 2. Llame al método [[Get]] de F con el nombre de propiedad "prototipo" .
--- 3. Sea O el resultado (2).
--- 4. Si O no es un objeto, ejecute una excepción TypeError .
--- 5. Deje V ser el valor de la propiedad [[Prototipo]] de V.
--- 6. Si V es ** nulo **, devuelve falso .
--- 7. Si O y V se refieren al mismo objeto o si se refieren a objetos unidos entre sí (13.1.2), devuelve verdadero .
--- 8. Vaya al paso 5.

Y eso significa que volverá a encontrar el valor del lado izquierdo después de ir a su prototipo hasta que no sea un objeto o hasta que sea igual al prototipo del objeto del lado derecho con el método especificado [[HasInstance]] . Lo que significa que comprobará si el lado izquierdo es una instancia del lado derecho, aunque consume todos los prototipos internos del lado izquierdo.

function x() { if (this instanceof x) { /* Probably invoked as constructor */ } else return 30; }


Si no desea poner una propiedad __previouslyConstructedByX en el objeto, porque contamina la interfaz pública del objeto y podría sobrescribirse fácilmente, simplemente no devuelva una instancia de x :

function x() { if(this instanceof x) { console.log("You invoked the new keyword!"); return that; } else { console.log("No new keyword"); return undefined; } } x(); var Z = new x(); Z.lolol = x; Z.lolol(); new Z.lolol();

Ahora la función x nunca devuelve un objeto de tipo x , entonces (creo) this instanceof x solo se evalúa como verdadera cuando se invoca la función con la new palabra clave.

La desventaja es que esto efectivamente estropea el comportamiento de instanceof - pero dependiendo de cuánto lo uses (no tiendo) eso puede no ser un problema.

Si su objetivo es que ambos casos devuelvan 30 , podría devolver una instancia de Number lugar de una instancia de x :

function x() { if(this instanceof x) { console.log("You invoked the new keyword!"); var that = {}; return new Number(30); } else { console.log("No new"); return 30; } } console.log(x()); var Z = new x(); console.log(Z); Z.lolol = x; console.log(Z.lolol()); console.log(new Z.lolol());


Tim Down, creo que es correcto. Creo que una vez que llegas al punto en el que crees que necesitas poder distinguir entre los dos modos de llamada, entonces no debes usar la palabra clave " this ". this no es confiable, y podría ser el objeto global, o podría ser un objeto completamente diferente. el hecho es que tener una función con estos diferentes modos de activación, algunos de los cuales funcionan según lo previsto, otros hacen algo completamente salvaje, es indeseable. Creo que tal vez estás tratando de resolver esto por eso.

Existe una forma idiomática de crear una función de constructor que se comporte igual sin importar cómo se llame. si es como Thing (), new Thing () o foo.Thing (). Dice así:

function Thing () { var that = Object.create(Thing.prototype); that.foo="bar"; that.bar="baz"; return that; }

donde Object.create es un nuevo método estándar de ecmascript 5 que puede implementarse en javascript regulares como este:

if(!Object.create) { Object.create = function(Function){ // WebReflection Revision return function(Object){ Function.prototype = Object; return new Function; }}(function(){}); }

Object.create tomará un objeto como parámetro y devolverá un nuevo objeto con ese objeto pasado como su prototipo.

Sin embargo, si realmente intentas que una función se comporte de manera diferente según cómo se llame, entonces eres una mala persona y no deberías escribir código JavaScript.


Tuve el mismo problema cuando traté de implementar una función que devuelve una cadena en lugar de un objeto.

Parece ser suficiente para verificar la existencia de "esto" al comienzo de su función:

function RGB(red, green, blue) { if (this) { throw new Error("RGB can''t be instantiated"); } var result = "#"; result += toHex(red); result += toHex(green); result += toHex(blue); function toHex(dec) { var result = dec.toString(16); if (result.length < 2) { result = "0" + result; } return result; } return result; }

De todos modos, al final decidí convertir mi pseudoclass RGB () en una función rgb (), así que no intentaré crear una instancia, por lo que no necesitaría ningún control de seguridad. Pero eso dependería de lo que estás tratando de hacer.


Utilice this instanceof arguments.callee (opcionalmente reemplazando arguments.callee con la función en la que se encuentra, que mejora el rendimiento) para verificar si se llama a algo como un constructor. No use este this.constructor ya que puede cambiarse fácilmente.


en realidad, la solución es muy posible y simple ... no entiendo por qué se escribieron tantas palabras para algo tan pequeño

ACTUALIZACIÓN: gracias a TwilightSun, la solución está completa, ¡incluso para la prueba sugerida por Claudiu ! ¡¡¡gracias chicos!!!

function Something() { this.constructed; if (Something.prototype.isPrototypeOf(this) && !this.constructed) { console.log("called as a c''tor"); this.constructed = true; } else { console.log("called as a function"); } } Something(); //"called as a function" new Something(); //"called as a c''tor"

demostrado aquí: https://jsfiddle.net/9cqtppuf/


tal vez estoy equivocado pero (a costa de un parásito) el siguiente código parece una solución:

function x(arg) { //console.debug(''_'' in this ? ''function'' : ''constructor''); //WRONG!!! // // RIGHT(as accepted) console.debug((this instanceof x && !(''_'' in this)) ? ''function'' : ''constructor''); this._ = 1; return 30; } var result1 = x(4), // function result2 = new x(4), // constructor Z = new x(); // constructor Z.lolol = x; Z.lolol(); // function


NOTA: Esto ahora es posible en ES2015 y posterior. Ver la respuesta de Daniel Weiner .

No creo que lo que quieres sea posible [antes de ES2015]. Simplemente no hay suficiente información disponible dentro de la función para hacer una inferencia confiable.

En cuanto a la tercera edición de ECMAScript, los pasos que se siguen cuando se llama a una new x() son esencialmente:

  • Crea un nuevo objeto
  • Asigne su propiedad interna [[Prototype]] a la propiedad prototipo de x
  • Llame a x como lo normal, pasando el nuevo objeto como this
  • Si la llamada a x devolvió un objeto, devuélvalo; de lo contrario, devuelva el nuevo objeto

Nada útil acerca de cómo se llamó a la función está disponible para el código de ejecución, por lo que lo único que se puede probar dentro de x es this valor, que es lo que están haciendo todas las respuestas aquí. Como ha observado, una nueva instancia de * x al llamar a x como un constructor no se puede distinguir de una instancia preexistente de x pasada como this al llamar a x como una función, a menos que asigne una propiedad a cada nuevo objeto creado por x como está construido:

function x(y) { var isConstructor = false; if (this instanceof x // <- You could use arguments.callee instead of x here, // except in in EcmaScript 5 strict mode. && !this.__previouslyConstructedByX) { isConstructor = true; this.__previouslyConstructedByX = true; } alert(isConstructor); }

Obviamente, esto no es ideal, ya que ahora tiene una propiedad adicional inútil en cada objeto construido por x que podría sobrescribirse, pero creo que es lo mejor que puede hacer.

(*) "instancia de" es un término inexacto, pero lo suficientemente cerca, y más conciso que "objeto que se ha creado llamando a x como un constructor"


function createConstructor(func) { return func.bind(Object.create(null)); } var myClass = createConstructor(function myClass() { if (this instanceof myClass) { console.log(''You used the "new" keyword''); } else { console.log(''You did NOT use the "new" keyword''); return; } // constructor logic here // ... });