Closure and function hoisting- not working on firefox - javascript

The following code gave an error, on some version in firefox browser - linksHandle is not defined.
The code is comprised of a function that at the bottom has a function named linksHandle. As far as I know this function is supposed to be hoisted when the the function that it is defined in, is invoked.
Therefore the function defined for the event 'mMenuReady' should be able to access it, because it enclosures all the function and variables that were defined in it's execution context.
Why do some firefox versions need the function declaration (linksHandle) to be defined before in order for the 'mmenu' callback to enclose the function?
document.addEventListener('readystatechange', function() {
if (document.readyState === 'interactive') {
if (typeof jQuery === 'function') {
// callback function that is invoked later by the event that is triggered -> $(window).trigger("mMenuReady")
$(window).on('mMenuReady', function() {
var links2 = Array.prototype.slice.call(document.querySelectorAll('#mm-mainMenu a'));
links2.forEach(linksHandle);
});
}
function linksHandle(elem) {
// function code
}
}
});

Function declarations inside blocks are only allowed since ES6. They do hoist inside your if body (not to the whole function), but not in older versions of FF that did implement them as "function statements" that were not hoisted (and actually completely invalid in strict mode) having caused issues like yours.

Related

If a strict mode function is executed using function invocation, its 'this' value will be undefined

I'm getting the following warning when using jshint.
Why?
If a strict mode function is executed using function invocation, its 'this' value will be undefined.
function demo() {
'use strict';
document.querySelector('#demo').addEventListener('click', test);
function test() {
console.log(this);
}
}
This works for me
function demo() {
'use strict';
var that = this; //new line
document.querySelector('#demo').addEventListener('click', test);
function test() {
console.log(that); //print that instead of this
}
}
Rather than try to suppress the warning, it's better to address the root cause.
Use of this can be confusing, and the value of this can unexpectedly change when refactoring code. Your code will be easier to read and maintain if you explicitly pass parameters.
The parameter passed to the test() callback is the Event object for the click:
function demo() {
'use strict';
function test(event) {
console.log('You clicked on:', event.target.outerHTML);
}
document.querySelector('#demo').addEventListener('click', test);
}
demo();
Console log output will be something like:
You clicked on: <h1 id="demo">Click Me</h1>
The Event object tells you the target element that the user clicked:
https://developer.mozilla.org/en-US/docs/Web/API/Event/target
Fiddle with the code:
https://jsfiddle.net/24epdxbz/2
From yehudakatz:
The ECMAScript 5 spec says that undefined is (almost) always passed, but that the function being called should change its thisValue to the global object when not in strict mode. This allows strict mode callers to avoid breaking existing non-strict-mode libraries.
Sometimes the scope in JS needs that instead of this, according to articles like this Scope in js
thus, you usually will need to use
var that = this;

Is it possible to change function runtime scope in JavaScript?

I am working on a JavaScript library while I need to load different modules accordingly, I use the callback to load different scripts:
Just add the main script in the page:
<script type="text/javascript" src="main.js"></script>
main.js:
(function () {
var actionForT2 = function (fun) {
fun && fun.apply(this);
}
var loadCallback = function (name, obj) {
if (name == "t2") {
actionForT2(obj);
}
}
window.__jsload = loadCallback;
var loadJs = function (js) {
var head = document.head || document.getElementsByTagName('head')[0];
var script = document.createElement("script");
script.setAttribute("src", js);
script.setAttribute("type", "text/javascript");
head.appendChild(script);
}
loadJs("js/t2.js");
})();
t2.js:
__jsload('t2', function () {
console.info("t2 loaded");
console.info(loadJs);
})
Now the t2.js will be loaded as expected. And I got the output:
t2 loaded
ReferenceError: loadJs is not defined
That's to say, the loadJs function is not accessed for the function defined in t2.js, then I wonder if I can change the Runtime context of the loaded function, for example, when the loaded function is being called:
fun && fun.apply(this);
Is it possible to make the call of fun under the context of current anonymous function, then all the defined variables, functions like the loadJs and etc can be accessed by fun without export them to the window?
Why I am interested in this kinds of solution is that I found google map use the callback, for example, when using google map v3, the following script will be loaded to the page:
https://maps.gstatic.com/maps-api-v3/api/js/18/3/main.js
Then another module map will be loaded:
https://maps.gstatic.com/cat_js/maps-api-v3/api/js/18/3/{map}.js
While when deep in to the codes, I found that the {map}.js can access the variables defined in the main.js. But I can not find the how the magic happen.
Is it possible to change function runtime scopeā€¦I wonder if I can change the Runtime context of the loaded function, for example, when the loaded function is being called
No. Javascript is lexically scoped, so scope is entirely dependent on where functions are created in the code, not where they called from or how they are called
> fun && fun.apply(this);
That will only set one parameter of the function's execution context, its this parameter.
There is general confusion about the term context. In ECMA-262 (the standard for the language underpinning javascript) context is used exclusively in execution context, which is the entire environment inside a function and includes its this parameter.
The call and apply methods allow you to specify the value of a function's this, that's it, they have no other effect on the execution context (which is why this should never be called "context"). They do not have any effect on identifier resolution, which proceeds based on scope regardless of the value of this or where a function has been called from.
You might find the following article interseting: Identifier Resolution, Execution Contexts and scope chains.
You could make it global. Since you already have window.__jsload you could attach it there.
__jsload.loadJs = loadJs;
// // access like this :
// console.info(__jsload.loadJS);
Or you could pass it to your callback when you call it from __jsload.
__jsload('t2', function (loadJs) {
console.info("t2 loaded");
console.info(loadJs);
}

All function expressions suddenly arent recognized as functions

I have a massive javascript file with many function expressions. All of a sudden console gives me the following errors:
In IE
The value of the property 'myFunc' is null or undefined, not a Function object
In Firefox
TypeError: myFunc is not a function
This is how I call the function:
myFunc();
This is the function:
myFunc = function() {
//do stuff
}
This is happening on ALL function expressions. If I change one to a function declaration it works, but then will fail on some other function expression call inside of it. What the heck?
Possibility 1
If you are calling the function expression before it is defined, you will get this error. If you however turn it into a function declaration, the function declaration would get hoisted to the top of the scope, and could be called before or after the actual declaration occurs. So:
functionFoo();
var functionFoo = function() {
};
Will give this error, because you are trying to call the function before it is defined. But:
functionFoo();
function functionFoo() {
}
Will work, because actual function declarations are hoisted to the top of the scope, and can be used anywhere.
Possibility 2
If you are calling the function expression from a different scope that is outside where the function expression is defined, you will get this error. function expressions, like other variables, can only be used within the scope they are defined. So:
$( document ).ready( function() {
var functionFoo = function() {
};
} );
functionFoo();
Will give you an error, because the defining of the function happens in a different scope than the call. But:
$( document ).ready( function() {
var functionFoo = function() {
};
functionFoo();
} );
Will work just fine, because both the defining and the call happen in the same scope.

How is JavaScript's strict mode implemented

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.

Typeof of a javascript function is defined before the function is declared?

Could somebody please explain or link to a resource that will tell me why :
<script type=" type="text/javascript">
if(typeof window.myfunc == "function"){
alert("Why does myfunc already exist ?");
}
function myfunc(){
}
</script>
will pop up an alert while the myfunc function has not been defined yet ?
I think I found an issue in Chrome, Safari and IE (not FF) which is linked to this behavior. It keeps me from extending the prototype of a function when the js file that contains the function is included more than once in a web page. I'd like to know more about this before calling it a bug and reporting it.
Thank you !
Named function declarations, including the function body, get hoisted to the
top of the scope in JavaScript. I'd recommend reading this article about
JavaScript scoping and
hoisting.
If you did something like this, where you assigned the function to a named
variable, only the variable declaration would be hoisted, but it wouldn't have
a value until the assignment actually took place:
if (typeof myFunc == 'function') {
// will not be reached
}
var myFunc = function() { ... }
The above effectively gets treated as:
var myFunc; // myFunc is undefined
if (typeof myFunc == 'function') {
// will not be reached
}
myFunc = function() { ... }
One word: hoisting
A quote from JavaScript Garden:
"The above function gets hoisted before the execution of the program starts; thus, it is available everywhere in the scope it was defined in, even if called before the actual definition in the source."
More info here: http://bonsaiden.github.com/JavaScript-Garden/#function.general

Categories