Extending the native JavaScript Error constructor - javascript

I tried to extend the JavaScript error properties through the extension of the prototype of the Error constructor:
<script type="text/javascript">
// extending the Error properties to all the ones available in the various browsers:
Error.prototype = {
name: null, // (all browsers); the name of the error
message: null, // (all browsers); the error message as a string
description: null, // (Internet Explorer); description of the error
fileName: null, // (Firefox); the name of the file where the error occurred
lineNumber: null, // (Firefox); the number of line where the error occurred
columnNumber: null, // (Firefox); the number of column where the error occurred
number: null, // (Internet Explorer); the error code as a number
stack: null // (Firefox, Chrome); detailed information about the location where the error exactly occurred
};
function log(error) {
var errors = [];
for (var prop in error) {
errors.push(prop + ': ' + error[prop]);
}
alert(errors.join('\n'));
}
</script>
Then I test the log function:
<script type="text/javascript>
try {
var a = b; // b is undefined!
} catch(error) {
log(error);
}
</script>
The result is that the error object only shows some properties (e.g. on Firefox fileName, lineNumber and columnNumber) like if it has not been extended.
But the most strange thing is that the for...in cycle seems unable to walk all the error object properties: trying to alert the standard property error.message normally returns a message.
So the results of my test are:
the Error constructor is not extensible through its prototype, as other native constructors are;
the for...in loop is not able to walk the properties of an error object.
Am I right?
Are there some interesting evidences/resources you may suggest to know more about it?

A. Like, Raynos said, The reason message isn't being set is that Error is a function that returns a new Error object and does not manipulate this in any way.
B. The way to do this right is to return the result of the apply from the constructor, as well as setting the prototype in the usual complicated javascripty way:
function MyError() {
var tmp = Error.apply(this, arguments);
tmp.name = this.name = 'MyError'
this.stack = tmp.stack
this.message = tmp.message
return this
}
var IntermediateInheritor = function() {}
IntermediateInheritor.prototype = Error.prototype;
MyError.prototype = new IntermediateInheritor()
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 ...>
The only problems with this way of doing it at this point (i've iteratted it a bit) are that
properties other than stack and message aren't included in MyError and
the stacktrace has an additional line that isn't really necessary.
The first problem could be fixed by iterating through all the non-enumerable properties of error using the trick in this answer: Is it possible to get the non-enumerable inherited property names of an object?, but this isn't supported by ie<9. The second problem could be solved by tearing out that line in the stack trace, but I'm not sure how to safely do that (maybe just removing the second line of e.stack.toString() ??).

Related

In Javascript, how can I throw an error that prints an object to the console?

The console.error() (or console.log()) statement has some nice features like variable-length argument lists that print out objects (and arrays):
const spaghettio = {sauce: 'arrabiata', cheese: 'gorgonzola'}
console.log(`Uh oh,`, spaghettio)
The object is printed out in a way I can expand the keys, which is very handy for large objects:
But if I want to throw a real error, I don't have those options. So I usually end up doing this:
console.error(`Uh oh`, spaghettio)
throw new Error(`Uh oh: ${spaghettio}`)
The Error version generally does a crappy job of formatting the objects and arrays:
Uh oh: [object Object]. I can do
throw new Error(`Uh oh: ${JSON.stringify(spaghettio)}`)
But no matter how I slice it it's not as nice as what console.error does.
Is there a better way to avoid repeating myself here while still printing out objects to the console?
Combining console.error with throw new Error is probably shortest way to achieve what you want (please notice that you won't be able to expand object in StackOverflow console, it will be fully printed here):
const spaghettio = {sauce: 'arrabiata', cheese: 'gorgonzola'}
throw new Error(console.error('Uh oh:', spaghettio))
You can create your own errors and then create custom handling, which can include i.e. objects:
class MyError extends Error {
constructor(message, obj) {
super(message);
this.obj = obj;
}
}
async function x() {
throw new MyError('ab', { that: 'is', much: { really: 'weird' } });
}
x().catch(err => {
if (err instanceof MyError) {
console.error(err.message);
console.error(err.obj);
}
})

Redefine TypeError to have a static message

My question is quite strange in the sense that it is purely for digging into js features as opposed to it being useful. Is there a way to redefine window.TypeError so that I can permanent change the message property to something static like "testing message"?
Consider this piece of code:
try {
null.f();
}
catch(ex) {
console.log(ex.message) // should print 'testing message'
//but keeps on printing 'TypeError: Cannot read property 'f' of null'
}
If I inspect TypeError with Object.getOwnPropertyDescriptor(window,'TypeError') I can see it is configurable:true so I must be able to override it but I can't seem to get it working.
I have tried all different variations but this is my latest attempt:
function MyError() {
this.message = "testing message";
}
MyError.prototype = Object.create(TypeError.prototype);
MyError.prototype.name = "MyError";
MyError.prototype.constructor = MyError;
Object.defineProperty(window, 'TypeError',
{
get: function () {
return MyError;
}
})

TypeError: object is not a function - Javascript, ExtJS, Jasmine and TypeError: Converting circular structure to JSON

I am new to this (really new to Jasmine testing, ExtJs, and JS), and I have to fix this bug/error. I am running some unit tests and I keep getting the following error:
TypeError: object is not a function
TypeError: object is not a function
at eval (eval at <anonymous> (...main/lib/ext/ext-4.2.0-gpl/ext-all-debug.js:5499:56), <anonymous>:3:8)
at Object.Ext.ClassManager.instantiate (...main/lib/ext/ext-4.2.0-gpl/ext-all-debug.js:5471:53)
at Object.create (...main/lib/ext/ext-4.2.0-gpl/ext-all-debug.js:2110:39)
at Ext.define.urnSearchBasic (../../../../main/app/model/SecurityGatewayUrnSearchResponse.js:72:26)
at urnSearchBasic (...test/spec/model/SecurityGatewayUrnSearchResponseTest.js:68:27)
at null.<anonymous> (...test/spec/model/SecurityGatewayUrnSearchResponseTest.js:150:17)
at jasmine.Block.execute (...test/lib/jasmine/jasmine.js:1024:15)
at jasmine.Queue.next_ (...test/lib/jasmine/jasmine.js:2025:31)
at jasmine.Queue.start (...test/lib/jasmine/jasmine.js:1978:8)
at jasmine.Spec.execute (.../test/lib/jasmine/jasmine.js:2305:14)
timeout: timed out after 10000 msec waiting for passed === true
The code for the SecurityGatewanyUrnSeachResponse (posted only partial code from lines 61-84 ) is:
61 urnSearchBasic: function (basic, config) {
config = Ext.apply({}, config);
var proxy = Deft.ioc.Injector.resolve('securityGatewayUrnMultiSearchProxy'),
urn = Ext.create('CAS.securityAdminUrnProv.model.Urn'),
formWriter,
createdProxy;
urn.data.urn = basic;
urn.data.pageNo = 0;
urn.data.pageSize = this.configData.getUrnPageSize();
72 formWriter = Ext.create('CAS.securityAdminUrnProv.model.SearchFormWriter');
formWriter.setUrn(urn.data);
createdProxy = Ext.create(proxy.$className);
createdProxy.url = createdProxy.url + '/basic';
createdProxy.setApiUrl(createdProxy.url);
createdProxy.headers = this.configData.getGatewayProxyHeaders();
createdProxy.setWriter(formWriter);
this.setProxy(createdProxy);
this.save(config);
84 },
Line 72 is causing the problem (Ext.create(...SearchFormWriter).
The code for SearchFormWirter is listed below:
Ext.define('CAS.securityAdminUrnProv.model.i SearchFormWriter', {
extend: "Ext.data.Writer",
alias: 'writer.SearchFormWriter',
inject: [ 'configData' ],
allowSingle: true,
config: {
urn: {}
},
writeRecords: function (request, data) {
var cache = [];
request.rawData = JSON.stringify(this.getUrn(), function(key, value) {
if (typeof value === 'object' && value !== null) {
if (cache.indexOf(value) !== -1) {
// Circular reference found, discard key
return;
}
// Store value in our collection
cache.push(value);
}
return value;
});
cache = null; // Enable garbage collection
return request;
}
});
The writeRecords function was causing the "TypeError: Converting circular structure to JSON."
when the code was:
writeRecords: function (request, data) {
request.rawData = JSON.stringify(this.getUrn());
return request;
}
After changing it, as suggested by the internet, the "TypeError: object is not a function" keeps appearing. There are about 23 specs and 16 of the failing due to this error. If you guys could please help me fix this, that would be great.
Thank you so much! Sorry for the long post...
Near as I can tell, your attempts to serialize the result of your this.getUrn() function is failing due to a circular object structure. This in turn is causing your your Ext object definition to fail, resulting in the object is not a function error.
Edit
To address the circular structure of the Javascript object, one must examine it for characteristics similar to the following:
var a = {};
a.b = a;
JSON.stringify() cannot handle objects such as 'a' as its children refer to their parent at some point. The result of this.getUrn() likely returns a Javascript object wherein a child refers to the parent resulting in the error in question.
To correct this, one can remove the offending values or their properties in the object returned by your this.getUrn() function. If this.getUrn() is returning values from the server, then it may be more desirable to remove them server side instead depending on your use case.
Does this help?

How to do elegant exception handling while retaining JavaScript debugger capabitilities

You know JavaScript can basically throw any object or even primitive as an exception:
throw 1;
throw { text: "hello" }
Sadly, debuggers like Firefox will log exceptions to console including a link to the code line where the exception was thrown if we throw built-in Error object.
In order to solve that limitation I thought: why don't I override toString and I give an exception instance as argument of Error constructor so exception will be implicitly converted to string?
var ArgumentException = function(args) {
this._argName= args.argName;
}
ArgumentException.prototype = {
_argName: null,
get argName() { return this._argName; },
toString: function() {
return "ArgumentException was thrown. Affected argument: " + this.argName;
}
};
throw Error(new ArgumentException({ argName: "someArgument" }));
Obviously, above code listing is a simplification of a real-world case.
Ok, this works and solve the whole problem.
But this kills the purpose of using exceptions since a try/catch won't be able of handling exceptions by type:
try
{
throw Error(new ArgumentException({ argName: "someArgument" }));
} catch(e) {
if(e instanceof ArgumentException) {
// This will happen never! "e" will hold a Error instance!!!!
}
}
How do you solve this problem? In fact, it's something with Web browser's debugger rather than a problem with actual JavaScript, but as debugging is an important point in any development cycle, it should be took seriously.
Thank you in advance.
EDIT
I want to share my other conclusion:
try
{
debugger;
throw new ArgumentException({ argName: "someArgument" });
} catch(e) {
if(e instanceof ArgumentException) {
}
}
Above solution will log the exception to the debugger console, but it'll stop the debugger before it's ever thrown. Ok, you don't get the link to the exact line but the debugger gets stopped there so you can know where the exception is going to be thrown.
Why not make your exception inherit from Error?
function ArgumentException(data) {
this.name = "ArgumentException";
this.message = arguments.length ? 'Affected argument: ' + data.argName : "Illegal argument";
}
// set up inheritance
ArgumentException.prototype = Object.create(Error.prototype);
ArgumentException.prototype.constructor = ArgumentException;
// use it
try {
throw new ArgumentException({argName: "someArgument"});
} catch(e) {
if(e instanceof ArgumentException) {
console.log(e); // hi there
}
}
// see it normally
throw new ArgumentException({argName: "someOtherArgument"});
// ArgumentException: Affected argument: someOtherArgument
For more, look at Custom Error Types on MDN

chrome exceptions object (nearly) empty

I'm trying to get useful information from the exception object (passed to the a "catch") and it is nearly empty in chrome. I have used it before and was able to get a reasonable stack trace, among other things. Is this a change to chrome, or am I doing something wrong? I'm doing this:
http://jsfiddle.net/R9Wkg/
function pr(s) {
document.body.innerHTML += s.toString() + "<br>";
}
function test() {
try {
var a = b; // err: b not defined
} catch (ex) {
pr('==== print exception object =====');
pr(ex);
pr('======= typeof exception object =====');
pr(typeof ex);
pr('===== members ======');
for (var i in ex) {
pr(' ----- ' + i + " ------");
pr(ex[i]);
}
console.log(ex);
}
}
function first() {
second();
}
function second() {
test();
}
first();
In Chrome it gives me nothing more than the string "ReferenceError: b is not defined" if I do a toString() on the exception object, but if I try to look at the object's individual members, there is nothing there. Notably there is no "stack" member. Then again, if looked at in the console, there is more there (but the stack is simply "-")
That hyphen means it's a getter, and is not automatically executed because it could have side effects. You can log them separately however: http://jsfiddle.net/R9Wkg/1/.
The fact that the error isn't enumberable is filed as an issue at V8's project site.
You can get the members of the error using Object.getOwnPropertyNames(ex) to enumerate them.
pr('===== members ======');
Object.getOwnPropertyNames(ex).forEach(function(i) {
pr(' ----- ' + i + " ------");
pr(ex[i]);
});
http://jsfiddle.net/gilly3/R9Wkg/3/

Categories