javascript - validacion - validar formulario jquery
¿Cómo creo un error personalizado en JavaScript? (18)
Por alguna razón, parece que la delegación de constructores no funciona en el siguiente fragmento de código:
function NotImplementedError() {
Error.apply(this, arguments);
}
NotImplementedError.prototype = new Error();
var nie = new NotImplementedError("some message");
console.log("The message is: ''"+nie.message+"''")
Ejecutando esto da The message is: ''''
. ¿Alguna idea de por qué o si existe una mejor manera de crear una nueva subclase de Error
? ¿Hay algún problema con la apply
del constructor de Error
nativo que desconozco?
A expensas de no poder usar instanceof
, lo siguiente preserva la traza original de la pila y no utiliza ningún trucos no estándar.
// the function itself
var fixError = function(err, name) {
err.name = name;
return err;
}
// using the function
try {
throw fixError(new Error(''custom error message''), ''CustomError'');
} catch (e) {
if (e.name == ''CustomError'')
console.log(''Wee! Custom Error! Msg:'', e.message);
else
throw e; // unhandled. let it propagate upwards the call stack
}
Actualice su código para asignar su prototipo al Error.prototype y al instanceof y a su trabajo de afirmación.
function NotImplementedError(message) {
this.name = "NotImplementedError";
this.message = (message || "");
}
NotImplementedError.prototype = Error.prototype;
Sin embargo, solo lanzaría su propio objeto y simplemente verificaría la propiedad del nombre.
throw {name : "NotImplementedError", message : "too lazy to implement"};
Edición basada en comentarios
Después de mirar los comentarios e intentar recordar por qué asignaría un prototipo a Error.prototype
lugar de un new Error()
como lo hizo Nicholas Zakas en su article , creé un jsFiddle con el siguiente código:
function NotImplementedError(message) {
this.name = "NotImplementedError";
this.message = (message || "");
}
NotImplementedError.prototype = Error.prototype;
function NotImplementedError2(message) {
this.message = (message || "");
}
NotImplementedError2.prototype = new Error();
try {
var e = new NotImplementedError("NotImplementedError message");
throw e;
} catch (ex1) {
console.log(ex1.stack);
console.log("ex1 instanceof NotImplementedError = " + (ex1 instanceof NotImplementedError));
console.log("ex1 instanceof Error = " + (ex1 instanceof Error));
console.log("ex1.name = " + ex1.name);
console.log("ex1.message = " + ex1.message);
}
try {
var e = new NotImplementedError2("NotImplementedError2 message");
throw e;
} catch (ex1) {
console.log(ex1.stack);
console.log("ex1 instanceof NotImplementedError2 = " + (ex1 instanceof NotImplementedError2));
console.log("ex1 instanceof Error = " + (ex1 instanceof Error));
console.log("ex1.name = " + ex1.name);
console.log("ex1.message = " + ex1.message);
}
La salida de la consola era esto.
undefined
ex1 instanceof NotImplementedError = true
ex1 instanceof Error = true
ex1.name = NotImplementedError
ex1.message = NotImplementedError message
Error
at window.onload (http://fiddle.jshell.net/MwMEJ/show/:29:34)
ex1 instanceof NotImplementedError2 = true
ex1 instanceof Error = true
ex1.name = Error
ex1.message = NotImplementedError2 message
Esto confirma que el "problema" con el que me encontré fue que la propiedad de la pila del error era el número de línea donde se creó el new Error()
, y no dónde se produjo el throw e
. Sin embargo, puede ser mejor que tener el efecto secundario de una línea NotImplementedError.prototype.name = "NotImplementedError"
que afecte al objeto Error.
Además, observe con NotImplementedError2
, cuando no establezco el .name
explícitamente, es igual a "Error". Sin embargo, como se menciona en los comentarios, debido a que esa versión establece el prototipo como new Error()
, pude establecer NotImplementedError2.prototype.name = "NotImplementedError2"
y estar bien.
El constructor debe ser como un método de fábrica y devolver lo que quieras. Si necesita métodos / propiedades adicionales, puede agregarlos al objeto antes de devolverlo.
function NotImplementedError(message) { return new Error("Not implemented", message); }
x = new NotImplementedError();
Aunque no estoy seguro de por qué tendrías que hacer esto. ¿Por qué no solo usas un new Error...
? Las excepciones personalizadas realmente no agregan mucho en JavaScript (o probablemente en cualquier lenguaje sin tipo).
En ES2015, puede usar la class
para hacer esto limpiamente:
class NotImplemented extends Error {
constructor(message = "", ...args) {
super(message, ...args);
this.message = message + " has not yet been implemented.";
}
}
Esto no modifica el prototipo de Error
global, le permite personalizar el message
, el name
y otros atributos, y captura correctamente la pila. También es bastante legible.
Por supuesto, puede necesitar usar una herramienta como babel
si su código se ejecutará en navegadores más antiguos.
Esta sección del estándar puede explicar por qué la llamada Error.apply
no inicializa el objeto:
15.11.1 El constructor de error llamado como una función
Cuando se llama a Error como una función en lugar de como un constructor, crea e inicializa un nuevo objeto Error. Por lo tanto, la función llamada Error (...) es equivalente a la expresión de creación del objeto nuevo Error (...) con los mismos argumentos.
En este caso, la función Error
probablemente determine que no se está llamando como un constructor, por lo que devuelve una nueva instancia de Error en lugar de inicializar el objeto.
Las pruebas con el siguiente código parecen demostrar que esto es, de hecho, lo que está sucediendo:
function NotImplementedError() {
var returned = Error.apply(this, arguments);
console.log("returned.message = ''" + returned.message + "''");
console.log("this.message = ''" + this.message + "''");
}
NotImplementedError.prototype = new Error();
var nie = new NotImplementedError("some message");
El siguiente resultado se genera cuando se ejecuta esto:
returned.message = ''some message''
this.message = ''''
MDN tiene un excelente ejemplo :
try {
throw new Error(''Whoops!'');
} catch (e) {
console.log(e.name + '': '' + e.message);
}
Manera más fácil. Puede hacer que su objeto herede del objeto Error. Ejemplo:
function NotImplementError(message)
{
this.message = message;
Error.call();
Error.call(message);
}
lo que estamos haciendo es usar la función call () que llama al constructor de la clase Error, por lo que básicamente es lo mismo que implementar una herencia de clase en otros lenguajes orientados a objetos.
Otra alternativa podría no funcionar en todos los entornos. Afirmó que funciona en nodejs 0.8. Este enfoque usa una forma no estándar de modificar el protoprop interno.
function myError(msg){
var e = new Error(msg);
_this = this;
_this.__proto__.__proto__ = e;
}
Pruebe un nuevo prototipo de objeto para cada instancia del tipo de error definido por el usuario. Permite que las verificaciones de instanceof
se comporten como siempre, además de que el tipo y el mensaje se informan correctamente en Firefox y V8 (Chome, nodejs).
function NotImplementedError(message){
if(NotImplementedError.innercall===undefined){
NotImplementedError.innercall = true;
NotImplementedError.prototype = new Error(message);
NotImplementedError.prototype.name = "NotImplementedError";
NotImplementedError.prototype.constructor = NotImplementedError;
return new NotImplementedError(message);
}
delete NotImplementedError.innercall;
}
Tenga en cuenta que una entrada adicional precederá a la pila por lo demás correcta.
Si alguien tiene curiosidad sobre cómo crear un error personalizado y obtener el seguimiento de la pila:
function CustomError(message) {
this.name = ''CustomError'';
this.message = message || '''';
var error = new Error(this.message);
error.name = this.name;
this.stack = error.stack;
}
CustomError.prototype = Object.create(Error.prototype);
try {
throw new CustomError(''foobar'');
}
catch (e) {
console.log(''name:'', e.name);
console.log(''message:'', e.message);
console.log(''stack:'', e.stack);
}
Si está utilizando Node / Chrome. El siguiente fragmento obtendrá una extensión que cumple con los siguientes requisitos.
-
err instanceof Error
-
err instanceof CustomErrorType
- console.log () devuelve
[CustomErrorType]
cuando se crea con un mensaje - console.log () devuelve
[CustomErrorType: message]
cuando se crea sin mensaje - throw / stack proporciona la información en el punto donde se creó el error.
- Funciona de manera óptima en Node.JS y Chrome.
- Aprobará checks de instancia en Chrome, Safari, Firefox e IE 8+, pero no tendrá una pila válida fuera de Chrome / Safari. Estoy de acuerdo con eso porque puedo depurar en Chrome, pero el código que requiere tipos de error específicos seguirá funcionando con el navegador cruzado. Si solo necesita Node, puede eliminar fácilmente las declaraciones
if
y está listo para continuar .
Retazo
var CustomErrorType = function(message) {
if (Object.defineProperty) {
Object.defineProperty(this, "message", {
value : message || "",
enumerable : false
});
} else {
this.message = message;
}
if (Error.captureStackTrace) {
Error.captureStackTrace(this, CustomErrorType);
}
}
CustomErrorType.prototype = new Error();
CustomErrorType.prototype.name = "CustomErrorType";
Uso
var err = new CustomErrorType("foo");
Salida
var err = new CustomErrorType("foo");
console.log(err);
console.log(err.stack);
[CustomErrorType: foo]
CustomErrorType: foo
at Object.<anonymous> (/errorTest.js:27:12)
at Module._compile (module.js:456:26)
at Object.Module._extensions..js (module.js:474:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
at Function.Module.runMain (module.js:497:10)
at startup (node.js:119:16)
at node.js:906:3
/errorTest.js:30
throw err;
^
CustomErrorType: foo
at Object.<anonymous> (/errorTest.js:27:12)
at Module._compile (module.js:456:26)
at Object.Module._extensions..js (module.js:474:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
at Function.Module.runMain (module.js:497:10)
at startup (node.js:119:16)
at node.js:906:3
Solo tuve que implementar algo como esto y descubrí que la pila se había perdido en mi propia implementación de error. Lo que tuve que hacer fue crear un error ficticio y recuperar la pila de eso:
My.Error = function (message, innerException) {
var err = new Error();
this.stack = err.stack; // IMPORTANT!
this.name = "Error";
this.message = message;
this.innerException = innerException;
}
My.Error.prototype = new Error();
My.Error.prototype.constructor = My.Error;
My.Error.prototype.toString = function (includeStackTrace) {
var msg = this.message;
var e = this.innerException;
while (e) {
msg += " The details are:/n" + e.message;
e = e.innerException;
}
if (includeStackTrace) {
msg += "/n/nStack Trace:/n/n" + this.stack;
}
return msg;
}
Todas las respuestas anteriores son terriblemente horribles, realmente. ¡Incluso el que tiene 107 ups! La verdadera respuesta es aquí chicos:
Heredar desde el objeto Error - ¿dónde está la propiedad del mensaje?
TL; DR:
R. El message
motivo por el message
no se establece es que Error
es una función que devuelve un nuevo objeto Error y no lo manipula de ninguna manera.
B. La forma de hacerlo bien es devolver el resultado de la aplicación desde el constructor, así como configurar el prototipo de la manera habitual y complicada:
function MyError() {
var temp = Error.apply(this, arguments);
temp.name = this.name = ''MyError'';
this.message = temp.message;
if(Object.defineProperty) {
// getter for more optimizy goodness
/*this.stack = */Object.defineProperty(this, ''stack'', {
get: function() {
return temp.stack
},
configurable: true // so you can change it if you want
})
} else {
this.stack = temp.stack
}
}
//inherit prototype using ECMAScript 5 (IE 9+)
MyError.prototype = Object.create(Error.prototype, {
constructor: {
value: MyError,
writable: true,
configurable: true
}
});
var myError = new MyError("message");
console.log("The message is: ''" + myError.message + "''"); // The message is: ''message''
console.log(myError instanceof Error); // true
console.log(myError instanceof MyError); // true
console.log(myError.toString()); // MyError: message
console.log(myError.stack); // MyError: message /n
// <stack trace ...>
//for EMCAScript 4 or ealier (IE 8 or ealier), inherit prototype this way instead of above code:
/*
var IntermediateInheritor = function() {};
IntermediateInheritor.prototype = Error.prototype;
MyError.prototype = new IntermediateInheritor();
*/
Probablemente puedas hacer algunos trucos para enumerar a través de todas las propiedades no enumerables del error tmp
para establecerlos en lugar de establecer explícitamente solo la stack
y el message
, pero el truco no es compatible, por ejemplo, <9
Tuve un problema similar a esto. Mi error debe ser una instanceof
Error
y NotImplemented
, y también necesita producir una NotImplemented
coherente en la consola.
Mi solución:
var NotImplemented = (function() {
var NotImplemented, err;
NotImplemented = (function() {
function NotImplemented(message) {
var err;
err = new Error(message);
err.name = "NotImplemented";
this.message = err.message;
if (err.stack) this.stack = err.stack;
}
return NotImplemented;
})();
err = new Error();
err.name = "NotImplemented";
NotImplemented.prototype = err;
return NotImplemented;
}).call(this);
// TEST:
console.log("instanceof Error: " + (new NotImplemented() instanceof Error));
console.log("instanceof NotImplemented: " + (new NotImplemented() instanceofNotImplemented));
console.log("message: "+(new NotImplemented(''I was too busy'').message));
throw new NotImplemented("just didn''t feel like it");
Resultado de ejecutar con node.js:
instanceof Error: true
instanceof NotImplemented: true
message: I was too busy
/private/tmp/t.js:24
throw new NotImplemented("just didn''t feel like it");
^
NotImplemented: just didn''t feel like it
at Error.NotImplemented (/Users/colin/projects/gems/jax/t.js:6:13)
at Object.<anonymous> (/Users/colin/projects/gems/jax/t.js:24:7)
at Module._compile (module.js:449:26)
at Object.Module._extensions..js (module.js:467:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
at Module.runMain (module.js:487:10)
at process.startup.processNextTick.process._tickCallback (node.js:244:9)
El error pasa todos mis 3 criterios, y aunque la propiedad de la stack
no es estándar, es compatible con la mayoría de los navegadores más nuevos, lo cual es aceptable en mi caso.
Usé el Patrón de Constructor para crear el nuevo objeto de error. Definí la cadena de prototipos , como una instancia de Error
. Ver la referencia del constructor de errores MDN.
Puedes verificar este fragmento en esta gist .
IMPLEMENTACIÓN
// Creates user-defined exceptions
var CustomError = (function() {
''use strict'';
//constructor
function CustomError() {
//enforces ''new'' instance
if (!(this instanceof CustomError)) {
return new CustomError(arguments);
}
var error,
//handles the arguments object when is passed by enforcing a ''new'' instance
args = Array.apply(null, typeof arguments[0] === ''object'' ? arguments[0] : arguments),
message = args.shift() || ''An exception has occurred'';
//builds the message with multiple arguments
if (~message.indexOf(''}'')) {
args.forEach(function(arg, i) {
message = message.replace(RegExp(''//{'' + i + ''}'', ''g''), arg);
});
}
//gets the exception stack
error = new Error(message);
//access to CustomError.prototype.name
error.name = this.name;
//set the properties of the instance
//in order to resemble an Error instance
Object.defineProperties(this, {
stack: {
enumerable: false,
get: function() { return error.stack; }
},
message: {
enumerable: false,
value: message
}
});
}
// Creates the prototype and prevents the direct reference to Error.prototype;
// Not used new Error() here because an exception would be raised here,
// but we need to raise the exception when CustomError instance is created.
CustomError.prototype = Object.create(Error.prototype, {
//fixes the link to the constructor (ES5)
constructor: setDescriptor(CustomError),
name: setDescriptor(''JSU Error'')
});
function setDescriptor(value) {
return {
configurable: false,
enumerable: false,
writable: false,
value: value
};
}
//returns the constructor
return CustomError;
}());
USO
El constructor CustomError puede recibir muchos argumentos para construir el mensaje, por ejemplo
var err1 = new CustomError("The url of file is required"),
err2 = new CustomError("Invalid Date: {0}", +"date"),
err3 = new CustomError("The length must be greater than {0}", 4),
err4 = new CustomError("Properties .{0} and .{1} don''t exist", "p1", "p2");
throw err4;
Y así es como se ve el error personalizado:
Acordando con Joyent no deberías meterse con la propiedad de la pila (que veo en muchas de las respuestas dadas aquí), ya que tendrá un impacto negativo en el rendimiento. Esto es lo que dicen:
pila: en general, no te metas con esto. Ni siquiera lo aumente. V8 solo lo calcula si alguien realmente lee la propiedad, lo que mejora dramáticamente el rendimiento de los errores manejables. Si lee la propiedad solo para aumentarla, terminará pagando el costo incluso si la persona que llama no necesita la pila.
Me gusta y me gustaría mencionar su idea de envolver el error original, que es un buen reemplazo para pasar a la pila.
Así que aquí es cómo creo un error personalizado, teniendo en cuenta lo anterior:
versión es5:
function RError(options) {
options = options || {}; // eslint-disable-line no-param-reassign
this.name = options.name;
this.message = options.message;
this.cause = options.cause;
// capture stack (this property is supposed to be treated as private)
this._err = new Error();
// create an iterable chain
this.chain = this.cause ? [this].concat(this.cause.chain) : [this];
}
RError.prototype = Object.create(Error.prototype, {
constructor: {
value: RError,
writable: true,
configurable: true
}
});
Object.defineProperty(RError.prototype, ''stack'', {
get: function stack() {
return this.name + '': '' + this.message + ''/n'' + this._err.stack.split(''/n'').slice(2).join(''/n'');
}
});
Object.defineProperty(RError.prototype, ''why'', {
get: function why() {
var _why = this.name + '': '' + this.message;
for (var i = 1; i < this.chain.length; i++) {
var e = this.chain[i];
_why += '' <- '' + e.name + '': '' + e.message;
}
return _why;
}
});
// usage
function fail() {
throw new RError({
name: ''BAR'',
message: ''I messed up.''
});
}
function failFurther() {
try {
fail();
} catch (err) {
throw new RError({
name: ''FOO'',
message: ''Something went wrong.'',
cause: err
});
}
}
try {
failFurther();
} catch (err) {
console.error(err.why);
console.error(err.stack);
console.error(err.cause.stack);
}
versión es6:
class RError extends Error {
constructor({name, message, cause}) {
super();
this.name = name;
this.message = message;
this.cause = cause;
}
[Symbol.iterator]() {
let current = this;
let done = false;
const iterator = {
next() {
const val = current;
if (done) {
return { value: val, done: true };
}
current = current.cause;
if (!val.cause) {
done = true;
}
return { value: val, done: false };
}
};
return iterator;
}
get why() {
let _why = '''';
for (const e of this) {
_why += `${_why.length ? '' <- '' : ''''}${e.name}: ${e.message}`;
}
return _why;
}
}
// usage
function fail() {
throw new RError({
name: ''BAR'',
message: ''I messed up.''
});
}
function failFurther() {
try {
fail();
} catch (err) {
throw new RError({
name: ''FOO'',
message: ''Something went wrong.'',
cause: err
});
}
}
try {
failFurther();
} catch (err) {
console.error(err.why);
console.error(err.stack);
console.error(err.cause.stack);
}
He puesto mi solución en un módulo, aquí está: https://www.npmjs.com/package/rerror
function InvalidValueError(value, type) {
this.message = "Expected `" + type.name + "`: " + value;
var error = new Error(this.message);
this.stack = error.stack;
}
InvalidValueError.prototype = new Error();
InvalidValueError.prototype.name = InvalidValueError.name;
InvalidValueError.prototype.constructor = InvalidValueError;