let say i have function like below
function doSomethingNow(){
callSomethingInFutureNotExistNow();
}
at the moment doSometingNow() is created callSomethingInFutureNotExistNow() not exist yet. It will be created in the future, on firefox, this doesn't show any error on firebug. Will these kind of function compatible on all browsers without throwing errors ?
Since javascript isn't compiled you shouldn't ever receive any errors with the code you posted assuming you don't call doSomethingNow() before callSomethingInFutureNotExistNow is declared.
To be safe though, you may want to do some null checking
function doSomethingNow(){
if (callSomethingInFutureNotExistNow) {
callSomethingInFutureNotExistNow();
}
}
Or if you want to be even more strict you can do type checking like this
function doSomethingNow(){
if (typeof(callSomethingInFutureNotExistNow) === 'function') {
callSomethingInFutureNotExistNow();
}
}
Related
I want to know whether a piece of JavaScript is executed in an ES module or a simple script.
This is what I tried so far:
function isEsm1() {
try {
// Script gives a syntax error during parsing when script is not an esm
return Boolean(import.meta.url);
} catch(err) {
return false;
}
}
function isEsm2() {
// will always return false, because `eval` always seems to be executed in regular script context
try {
return eval('Boolean(import.meta.url)');
} catch(err) {
return false;
}
}
function isEsm3() {
// Of course doesn't work, but had to try 😉
return 'meta' in import;
}
Is the regular script executed in the browser or in another context?
In a browser, how about:
var anyvar = {};
var inModule = anyvar === window.anyvar;
If you're in a module, you are not declaring anything on the window...
In NodeJS you could do something similar with global or this:
let inModule = this === module.exports..
Did not try it yet.. but should work I guess...
After testing, just the check for this === undefined is enough to test if you're executing in or out of a module..
Inside a module, this is undefined (as per spec). In global scope this points to global this, which is the window object in the case of a browser context...
Thanks to the discussion in John Gorter's answer, I think we've found a way.
console.log('In module: ' + (this === undefined));
It's as simple as that. Inside a module (and only inside a module (I hope)), this will be undefined. I found it in the v8 documentation here: https://v8.dev/features/modules#intro
I ran into a bug which I finally solved, but why it happen(ed/s) is still beyond me.
I save a variable foo using browser.storage.local.set for a firefox addon that I'm developing. I know that the variable is set, and the apparent bug was relying on some small piece of code that leads to this:
browser.storage.local.get((val) => {
if (val['foo'] === undefined){
console.log('Undefined');
}
else {
console.log('Defined')
}
});
// Outputs `Defined`, which is correct.
However, if I define the callback first, and then I get wrong output.
function checkStoredSettings(val) {
if (val['foo'] === undefined) {
console.log('Undefined')
}
else {
console.log("Defined")
}
}
browser.storage.local.get().then(checkStoredSettings, console.log);
// Outputs `Undefined`, which is incorrect.
//UPDATE
browser.storage.local.get(checkStoredSettings);
// Outputs `Undefined`, which is also incorrect.
Can someone explain what am I not getting here? I have now run the above two codes sequentially (in both orders) in the same scope.
Did you tried something like that ?
browser.storage.local.get('foo').then(console.log)
Note that doesNotExist intentionally does not exist, the alert is never shown, and instead we're left with an error in the browser's console. What's special about running this code in a jQuery callback that makes it fail so hard like this?
HTML:
<div>Shown! But we failed before the alert. Check your console...</div>
CSS:
div { display: none; }
JS:
$('div').fadeIn(100, function() {
if (doesNotExist) {
alert('Nope');
}
else {
alert('Still nope');
}
});
https://jsfiddle.net/mrpowell3j/gnhoheze/2/
There's nothing special about running that code in a callback. It'll cause that exception no matter how it runs. Using an undeclared variable name in an expression is always an error (except with typeof).
To check whether a variable is defined, you can use
if (typeof doesNotExist === "undefined") {
// it is undefined
}
Now that just tells you that it's either not defined at all, or that it has no value; either might be appropriate depending on the application. If it's supposed to be a global, then:
if (!("doesNotExist" in window)) {
// not defined
}
That checks for a property in the window object, which is where globals go (well, global var variables).
The problem is that your are executing invalid javascript code. The callback do not matter in this case.
You must initialize your variable before using it or check if it exist using a different approach:
var variableName;
// execute your code.
-
//or checking if variable is defined.
if(typeof variableName !== 'undefined') {
alert('nope');
} else {
alert('still nope');
}
To make debugging easier, I'm capturing all of the console logs in Chrome so that users who submit a feedback entry will also submit all of the logs to our server. When someone encounters a problem in production, I can first and foremost get them back to work so that I can then sit down and more thoroughly go through all of the logs to determine the root cause of whatever issue the user encountered in production.
The technique I use to capture the logs involves overriding console.log so that all text entered in the first argument gets stored in an array while simultaneously invoking the legacy function so that I can still see the logs in the console too.
The problem is when there's the occasional uncaught exception. These aren't included in the uploaded logs, so it's not always clear what caused the problem. So I tried overriding ReferenceError by writing a JavaScript function that takes a function as an argument, then returns a new function that does stuff with it, like storing data in a variable, and then invoking the legacy function as the last step:
function overrideException(legacyFn) {
/** arguments for original fn **/
return function() {
var args = [];
args[0] = arguments[0];
// pass in as arguments to original function and store result to
// prove we overrode the ReferenceError
output = ">> " + legacyFn.apply(this, args).stack;
return legacyFn.apply(this, arguments);
}
}
To test the overrideException function, I ran the following code on the console:
ReferenceError = overrideException(ReferenceError);
Afterwards, I tested the returned function, the new ReferenceError, by manually throwing a ReferenceError:
throw new ReferenceError("YES!! IT WORKS! HAHAHA!");
The resulting output on the console is:
ReferenceError: YES!! IT WORKS! HAHAHA!
And checking the global variable output from the overrideException function shows that it did indeed run:
output
">> ReferenceError: YES!! IT WORKS! HAHAHA!
at ReferenceError (<anonymous>)
at new <anonymous> (<anonymous>:18:35)
at <anonymous>:2:7
at Object.InjectedScript._evaluateOn (<anonymous>:562:39)
at Object.InjectedScript._evaluateAndWrap (<anonymous>:521:52)
at Object.InjectedScript.evaluate (<anonymous>:440:21)"
Now, here's where things start to fall apart. In our code, we're not going to know when an uncaught exception occurs, so I tested it by attempting to run a function that doesn't exist:
ttt();
Which results in:
ReferenceError: ttt is not defined
However, unlike the case where we explicitly throw an error, in this case, the function doesn't fire, and we're left with only the legacy functionality. The contents of the variable output is the same as in the first test.
So the question seems to be this: How do we override the ReferenceError functionality that the JavaScript engine uses to throw errors so that it's the same one we use when we throw a ReferenceError?
Keep in mind that my problem is limited only to Chrome at this time; I'm building a Chrome Packaged app.
I have done quite a bit of research for the same reason: I wanted to log errors and report them.
"Overriding" a native type (whether ReferenceError, String, or Array) is not possible.
Chrome binds these before any Javascript is run, so redefining window.ReferenceError has no effect.
You can extend ReferenceError with something like ReferenceError.prototype.extension = function() { return 0; }, or even override toString (for consistency, try it on the page, not the Dev Tools).
That doesn't help you much.
But not to worry....
(1) Use window.onerror to get file name, 1-indexed line number, and 0-indexed position of uncaught errors, as well as the error itself.
var errorData = [];
onerror = function(message, file, line, position, error) {
errorData.push({message:message, file:file, line:line, position:position, error:error});
};
See the fiddle for an example. Since the OP was Chrome-specific, this has only been tested to work in Chrome.
(2) Because of improvements to (1), this is no longer necessary, but I leave this second technique here for completeness, and since onerror is not guaranteed to work for all errors on all browsers. You will also sometimes see the following:
var errors = [];
function protectedFunction(f) {
return function() {
try {
f.apply(this, arguments);
} catch(e) {
errors.push(e);
throw e;
}
};
}
setTimeout = protectedFunction(setTimeout);
setInterval = protectedFunction(setInterval);
etc...
FYI, all this is very similar to what has been done in the Google Closure Compiler library, in goog.debug, created during Gmail development with the intent of doing exactly this. Of particular interest is goog.debug.ErrorHandler and goog.debug.ErrorReporter.
Update:
Perhaps the way the function is called is to blame, so here's to it:
2 JS files Main.js: self invoking (non-strict) function that adds an event listener for the '(on)load' event. The callback calls a loader function, that parses the location.pathname, and calls an init function, and detaches/removes the '(on)load' listener & returns null (explicitly).
PageSpecific.js: contains the _init function, adds a couple of event listeners to the body.
One of these listeners' callback (also returned from a closure) calls the strict function that uses argument.callee as a reference for recursion. The closure that returns the event handler may -depending on the browser- or may not bind and unbind other events, but I think that's irrelevant here, as this is to imitate an onchange event in IE <9
I hope this is reasonably clear, so its: anon. F => eventlistener => handler (named but declared in anon F) => pageloader => init => eventListener binding function returned by closure => calls strict function
Incidentally: Here's a trimmed down version of the _init function that is called, that I'm actually using. More specifically: the closure that binds the event Listener and - handler together. Its another one of my length questions, to which nobody seems to know the answer... hint ;-)
I'm debugging some fairly large (and complex) JavaScripts. In doing this, I noticed that I have a function, using strict mode that works fine but should, if I'm not mistaken, throw errors. Since the scripts are fairly sizeable and complex (event delegation, stacked closures etc), here's a simple example:
function withCalleeRecursion(foo)
{
'use strict';//strict throws typeError on arguments.callee
foo = foo.replace(/(a|b)+/gi, function (p1,p2)
{
if (p1.match(/(a|b){2,}/i))
{
return p1.replace(/(a|b)/gi,arguments.callee);//no errors
}
return (p2.match(/a/i) ? 'X':'Y');
});
return foo;
}
(function()
{//not strict
alert(withCalleeRecursion('Abba makes me barf'));
})();
In my actual script, this works perfectly fine. When I pasted this both in Firebug and chrome console, an error is thrown, though. I've tried this code here, so IE should throw errors, too, but when I run the code in IE's debugger, it works just fine. As far as I can work out, changing the doctype (tried html5 and html4) makes no difference.
Am I right in thinking that (most) browsers aren't as strict with the 'use strict'; directive as it's name suggests? It would seem that the browsers choose to ignore it when a possible error is detected when parsing the script. Is this true?
Meanwhile, I have made a slight change to the function, just out of precaution. Since I've seen quite a few questions here of people wondering how to get the callee reference in strict mode, I'm pasting it here, too:
function withCalleeRecursion(foo)
{
'use strict';
foo = foo.replace(/(a|b)+/gi, function abR(p1,p2)
{
if (p1.match(/(a|b){2,}/i))
{
return p1.replace(/(a|b)/gi,abR);
}
return (p2.match(/a/i) ? 'X':'Y');
});
return foo;
}
Name the callback, that's all.
It's probably because browser consoles use eval(), which changes things. Although putting "use strict"; at the start of a string of code that is passed to eval() works as expected, it's possible that console implementations prepend code to the string you've typed into the console, meaning that "use strict"; is no longer the first statement executed and is therefore ignored.
There's a reference to this and a suggested workaround in the following article:
http://javascriptweblog.wordpress.com/2011/05/03/javascript-strict-mode/
The suggested workaround is to wrap code in the console within a function that is immediately executed:
(function() {
"use strict";
nonExistentVariable = 1; // Error is now thrown
})();
Maybe this article can help you to understand more. Anyway the solution is the one you mention, the error is because access to arguments.caller and arguments.callee throw an exception in strict mode. Thus any anonymous functions that you want to reference will need to be named.