Consider a page containing an iframe. The iframe's content might look like this
<script type="text/javascript">
window.foo = function () {
nonExisting();
};
window.bar = function () {
throw "An error!";
};
</script>
Now, I wanna execute something like this:
try {
iframe.contentWindow.foo();
} catch (e) { console.log('ok'); }
and
try {
iframe.contentWindow.bar();
} catch (e) { console.log('ok'); }
This is what I get:
Chrome/Firefox/Opera - 'ok', 'ok'
(expected behaviour)
IE8 - "Object expected" error, Uncaught Exception
WTF is going on here? How could that be an uncaught exception when I'm using a try/catch block? Is that a bug? Or does anything in the specs allow this behaviour?
And most importantly: Can I make it work as it should?
That's because you have a typo: "An error"!.
If I run it without that typo on IE9 with IE8 emulated, it works: http://jsfiddle.net/vsSgE/3/.
I ran into this exact issue today. I had defined a couple of "exception classes" in the parent window, which I "imported" into the child window (iframe) to be able to handle them with instanceof in the parent. Something like this:
Parent window
window.MyExceptions = {
RenderingException: function () { ... }
// ...more exception types
};
// Somewhere further down in the code, assuming 'iframe' is the child iframe
try {
iframe.contentWindow.renderAllTheThings();
} catch (ex) {
if (ex instanceof MyExceptions.RenderingException) {
// ...
}
}
Child (iframe) window
window.MyExceptions = window.parent.MyExceptions; // Import exception types
window.renderAllTheThings = function () {
// ...
throw new MyExceptions.RenderingException();
};
Using this setup, I got the same problem as you did - it worked in all modern browsers I tested, but failed in IE8 with the "uncaught exception" error.
My workaround was to add a simple utility function to the MyExceptions object in the parent window that does the actual throwing, like this:
window.MyExceptions = {
RenderingException: function () { ... },
// ...more exception types
Throw: function (ex) {
throw ex;
}
};
Then whenever I wanted to throw an exception from the child window to be handled in the parent, I would do MyExceptions.Throw(new MyExceptions.RenderingException());.
Not exactly elegant, but I needed to get it working.
Related
I am testing my clipboard tooltip code, but when I run the test it gives the error
TypeError: Clipboard.on is not a function
Test File
let clipboard_args;
class Clipboard {
constructor(...args) {
clipboard_args = args;
}
}
mock_cjs("clipboard", Clipboard);
...
...
...
rm.update_elements($content); // here the error is caused after calling this function
Main file
export const update_elements = ($content) => {
...
...
...
clipboard.on('success', function(e) { //There error will not occur without this line
show_copied_confirmation($copy_button[0]);
});
};
The mocks seems support the bare minimum functionality needed to run the existing tests with the existing code. The code seems to be fine, but I am not sure what to do in order to fix the tests.
In browser (Chrome) javascript:
var DataModler = {
Data: { Something: 'value' },
Process: function() { alert('not implemented.'); },
Render: function() { alert('not implemented.'); }
}
DataModler.Process = function() {
// do some data processing, then render it.
this.Render(); // this == Window, so an exception is thrown.
};
DataModler.Render = function() {
// render the data.
};
The problem I'm having is, if I set a breakpoint in DataModler.Process(), this is set to Window. I expect this to be set to the object that defines the function, which to my mind is DataModler. If I implement the function within the definition of DataModler, then this == DataModler as I would expect.
So I suppose the question is, how can I allow a function on my object to be replaced but still have this refer to the object on which the function is defined (DataModler)?
As mentioned in c69's comment, you are experiencing this problem due to how the function is actually called.
You can write
DataModler.Process = function() {
...
}.bind(DataModler);
to ensure that this withing Process is DataModler.
window.onload = function () {
var console = null;
console.log(1);
}
When I run that js code,find this error
Uncaught TypeError: console is not a function
I know the Object console is Overwritten by variable ,but how can I resolve the problem, I don't want to change the variable console.
Try this.
window.onload = function () {
test_console();
}
var console = null;
function test_console() {
window.console.log(1);
}
Try this
window.console.log('something')
this work because all global object are in window;
window.console will get you the object, however, if you're using IE and you don't have the Developer Tools window open console will not return anything.
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
The project I'm working on currently has a single bootstrap file that initialises all the javascript objects within the app. similar to the code below
if(document.getElementById('nav')) {
new navigation();
}
if(document.getElementById('search')) {
new search();
}
new carousel();
However my concern is that for whatever reason one of the lines of JS errors all JS code following will not execute and we're almost creating a single point of failure.
I am interested in hearing alternatives and solutions to this problem and any other bootstrap strategies that may help to alleviate this.
Thanks in advance
Check this fiddle.
For rolling your own, if you can use data- html 5 attributes to specify the bootstrap relationships. Then your bootstrap function can iterate through them and spin up the constructors.
You could also have a mapping object for id to constructors. I'll use that example here.
Assume markup similar to this:
<body>
<div id="navigation"></div>
<div id="search"></div>
</body>
Define your bootstrap maps, create a bootstrap method, and invoke it:
// Declare your bindings in an object
var bootstrapBindings = {
"navigation" : NavigationBootstrap,
"search" : SearchBootstrap,
"failure" : null
};
function bootstrap() {
var i, element, instance;
// Bind everything in the bindings
for(i in bootstrapBindings) {
if(bootstrapBindings.hasOwnProperty(i)) {
try {
element = document.getElementById(i);
if(element) {
instance = new bootstrapBindings[i](element);
// Nestable bootstrap calls
instance.bootstrap();
}
} catch(e) {
// Do something with error if you want
alert('Unable to bootstrap: ' + e);
}
}
}
}
bootstrap();
If you know that there is a chance one of your calls may fail then you should have a solution to catch that error as well. Using a try catch block would allow you to catch the exceptions and have your code continue.
try{
if(document.getElementById('nav')) {
new navigation();
}
}catch(e){
//handle exception for navigation fail
}
try{
if(document.getElementById('search')) {
new search();
}
}catch(e){
//handle exception for search fail
}
try{
new carousel();
}catch(e){
//handle exception for carousel fail
}
Generally you dont want to wrap all your code in try catch blocks, but it sounds like you're fairly confident that some of the code will throw an exception.
You could encapsulate the init logic inside separate init functions, and then have a further function invoke them and capture the results. For example:
<body>
<div id=nav></div>
<div id=search></div>
<script>
function initNavigation() {
if (document.getElementById('nav')) {
new navigation();
}
}
function initSearch() {
if (document.getElementById('search')) {
new search();
}
}
function initCarousel() {
new carousel();
}
function initNoError() {
return "ok";
}
/**
* Expects a list of functions to execute, and will capture either the retVal or the error.
*/
function init() {
var results = [],
result;
for (var i = 0; i < arguments.length; i++) {
result = {};
try {
result.retVal = arguments[i]();
} catch (e) {
result.error = e;
}
results.push(result);
}
return results;
}
var initResults = init(initNavigation, initSearch, initCarousel, initNoError);
console.log(initResults);
</script>
</body>
This code will show the following for me. The ReferenceError errors are because I don't have your constructor functions. The TypeError is seemingly because Firefox is returning the <div> of the same id, and this div is of course not a constructor function.
[
Object { error=ReferenceError: navigation is not defined},
Object { error=TypeError: search is not a constructor},
Object { error=ReferenceError: carousel is not defined},
Object { retVal="ok"}
]