var fn;
function foo() {
var a = 2;
function baz() {
console.log( a );
}
fn = baz; // assign baz to global variable
}
function bar() {
fn(); // look ma, I saw closure!
}
foo();
bar(); // 2
I have no idea with this code:
First, why the author have to wrap fn in a function that call fn(function bar), why don't call is simply outside the global scope like "fn();",I've tried it and result in the same
Second, is it a good way to assign a function to a variable?, I thought it could only copy the structure of the function and reconstruct in the variable(which means it have no reference or simply cannot remember the environment by foo)
Third, The author said that engine remain all the closure whenever there is one who using what concern to that environment, but here, I just see that there's a assignment, so is it right if fn is using baz that leads to the remain of environment of foo to fn?(which make fn can use the variable "a")
Related
I'm guessing this is some sort of "scope" issue but I want to understand why this is the case.
function foo(){
return true;
}
function bar(){
var foo = foo();
console.log(foo);
}
var foo = foo();
console.log(foo); //returns true as expected
But when I do the following
function foo(){
return true;
}
function bar(){
var foo = foo();
console.log(foo);
}
bar(); //returns Uncaught TypeError: foo is not a function
It seems that a variable name cannot be the same as a function name. But that isn't true is it?
There are two concepts which should help you understand this:
The first is shadowing which means that if you define a variable name inside a scope, it will hide the same variable name (or function name) from a parent scope.
The second is hoisting which causes any variable declarations to be moved to the top of the local scope.
This code
function foo(){}
function bar(){
var foo = foo();
console.log(foo);
}
is equivalent to this
var foo;//will initialize foo to undefined
foo = function(){}
var bar;
bar = function(){
var foo;//will shadow parent foo and initialize foo to undefined
foo = foo(); //fails because you are trying to call an undefined object
console.log(foo);
}
Hopefully, this makes it obvious why 'foo is not a function'. Your bar function is declaring a new foo variable (which hides the global one inside the bar scope) and then tries assigning this variable by calling itself.
You can read more about the intricacies of var here:
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/var
In JavaScript, the names created by function declarations like your foo declaration are very much the same as variables. And so your second example fails for the same reason that this example alerts 2, not 1:
var a = 1;
function bar() {
var a = 2;
alert(a);
}
...because the a inside bar shadows the a outside bar that would otherwise be in scope since bar closes over it.
It seems that a variable name cannot be the same as a function name. But that isn't true is it?
A variable name can indeed be the same as a function name, but if you do that in a nested scope (as you have), the variable name shadows the function name.
In your bar:
function bar(){
var foo = foo();
console.log(foo);
}
...the foo in foo() is the variable declared in bar, not the function declared in the outer scope. As such, it has the default value variables have (undefined), which can't be called, so you get the error.
In a comment you've asked:
Since that is the case that i'm overwriting foo in the second example, aren't I also overwriting it in the first example?
No, and that takes us back to the first statement above: The name created by a function declaration is very much like a variable. As such, if you declare a function, then later declare a variable with the same name, the variable declaration is ignored. So your first example is interpreted as though there were no var foo at all at global scope. So var foo = foo(); is really foo = foo(), which works just fine, calling foo and then using the return value to update foo. (Doing it again would fail, because the value the function returns isn't callable.)
Check the commend on the code
function foo(){
return true;
}
function bar(){
var foo = foo(); // before calling foo(), foo is defined as a variable, so foo is not a function anymore in this scope.
console.log(foo);
}
bar(); //returns Uncaught TypeError: foo is not a function
Original Question:
JSHint complains when my JavaScript calls a function that is defined further down the page than the call to it. However, my page is for a game, and no functions are called until the whole thing has downloaded. So why does the order functions appear in my code matter?
EDIT: I think I may have found the answer.
http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting
I am groaning inside. Looks like I need to spend ANOTHER day re-ordering six thousand lines of code. The learning curve with javascript is not steep at all, but it is very loooooong.
tl;dr If you're not calling anything until everything loads, you should be fine.
Edit: For an overview which also covers some ES6 declarations (let, const): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Scope_Cheatsheet
This weird behavior depends on
How you define the functions and
When you call them.
Here's some examples.
bar(); //This won't throw an error
function bar() {}
foo(); //This will throw an error
var foo = function() {}
bar();
function bar() {
foo(); //This will throw an error
}
var foo = function() {}
bar();
function bar() {
foo(); //This _won't_ throw an error
}
function foo() {}
function bar() {
foo(); //no error
}
var foo = function() {}
bar();
This is because of something called hoisting!
There are two ways to define functions: Function declaration and function expression. The difference is annoying and minute, so let's just say this slightly wrong thing: If you're writing it like function name() {}, it's a declaration, and when you write it like var name = function() {} (or an anonymous function assigned to a return, things like that), it's a function expression.
First, let's look at how variables are handled:
var foo = 42;
//the interpreter turns it into this:
var foo;
foo = 42;
Now, how function declarations are handled:
var foo = 42;
function bar() {}
//turns into
var foo; //Insanity! It's now at the top
function bar() {}
foo = 42;
The var statements "throws" the creation of foo to the very top, but doesn't assign the value to it yet. The function declaration comes next in line, and finally a value is assigned to foo.
And what about this?
bar();
var foo = 42;
function bar() {}
//=>
var foo;
function bar() {}
bar();
foo = 42;
Only the declaration of foo is moved to the top. The assignment comes only after the call to bar is made, where it was before all the hoisting occurred.
And finally, for conciseness:
bar();
function bar() {}
//turns to
function bar() {}
bar();
Now, what about function expressions?
var foo = function() {}
foo();
//=>
var foo;
foo = function() {}
foo();
Just like regular variables, first foo is declared at the highest point of the scope, then it is assigned a value.
Let's see why the second example throws an error.
bar();
function bar() {
foo();
}
var foo = function() {}
//=>
var foo;
function bar() {
foo();
}
bar();
foo = function() {}
As we've seen before, only the creating of foo is hoisted, the assignment comes where it appeared in the "original" (un-hoisted) code. When bar is called, it is before foo is assigned a value, so foo === undefined. Now in the function-body of bar, it's as if you're doing undefined(), which throws an error.
The main reason is probably that JSLint does only one pass on the file so it doesn't know you will define such a function.
If you used functions statement syntax
function foo(){ ... }
There is actually no difference at all where you declare the function (it always behaves as if the declaration is on the beginning).
On the other hand, if your function was set like a regular variable
var foo = function() { ... };
You have to guarantee you wont call it before the initialization (this can actually be a source of bugs).
Since reordering tons of code is complicated and can be a source of bugs in itself, I would suggest you search for a workaround. I'm pretty sure you can tell JSLint the name of global variables beforehand so it doesn't complain about undeclared stuff.
Put a comment on the beggining of the file
/*globals foo1 foo2 foo3*/
Or you can use a text box there for that. (I also think you can pass this in the arguments to the inner jslint function if you can meddle with it.)
There are way too many people pushing arbitrary rules about how JavaScript should be written. Most rules are utter rubbish.
Function hoisting is a feature in JavaScript because it is a good idea.
When you have an internal function which is often the utility of inner functions, adding it to the beginning of the outer function is an acceptable style of writing code, but it does have the drawback that you have to read through the details to get to what the outer function does.
You should stick to one principle throughout your codebase either put private functions first or last in your module or function. JSHint is good for enforcing consistency, but you should ABSOLUTELY adjust the .jshintrc to fit your needs, NOT adjust your source code to other peoples wacky coding concepts.
One coding style that you might see in the wild you should avoid because it gives you no advantages and only possible refactoring pain:
function bigProcess() {
var step1,step2;
step1();
step2();
step1 = function() {...};
step2 = function() {...};
}
This is exactly what function hoisting is there to avoid. Just learn the language and exploit its strengths.
Only function declaration are hoisted not function expression (assignment).
Maybe the title sounds a little bit weird (please improve it) -- but I need a solution for the following scenario. I have the following code:
var Foo = function () {
this._hello = "world!";
};
Foo.prototype.bar = function () {
console.log(this._hello);
};
var f = new Foo();
f.bar(); // => "world!"
f.bar.apply(this); // => undefined
I know that apply changes the context, so inside of bar, this will be the global object (at the second call).
But what I need is to access this from Foo function. A solution that I see would be:
var Foo = function () {
var self = this;
self._hello = "world!";
self.bar = function () {
console.log(self._hello);
};
};
However, I would choose not to have method declarations inside of another function.
I'd prefer to define methods same column level (just for code style):
var Foo = ...;
Foo.prototype.method = ...;
Is this possible? How?
You can use the bind() method to tackle these kinds of problems. Instead of something.method(f.bar) call something.method(f.bar.bind(f)) to get the bar method always called on the expected context (f).
If you don't want to use bind in every location where you pass bar around as a callback, you can also put it in the constructor to create a dedicated bound function for every instance by default:
function Foo() {
this._hello = "world!";
this.bar = this.bar.bind(this);
}
Foo.prototype.bar = function () {
console.log(this._hello);
};
var f = new Foo;
something.method(f.bar); // works!
It's not possible to do this by assigning a function to the prototype like this.
Unless you assign something to f.bar directly (as in your second example, and Bergi's answer), the value you will get for f.bar is a reference to the function you assigned to the prototype's property Foo.prototype.bar. This will be exactly the same function object for any other object that has Foo.prototype as a prototype. There is no reference to f in this function object.
So when you call f.bar(), how does this refer to the value of f? It is a special syntax, that basically equates to f.bar.apply(f). It is only the fact that you use this method-call syntax that sets this to the value of f. Any other reference to f.bar will just evaluate to the prototype's single, shared function object.
If you call it with f.bar.apply(somethingElse), this is now set to somethingElse, and all association with f is lost.
It's not a question of apply(...) changing scope. fn.apply(x) sets this to x within fn, whereas y.fn() sets this to y.
Similarly, in your example if you assign f.bar to a variable and then invoke it via the variable instead of using the method-call syntax f.bar(), your this will be the window object (if running in a browser) and again you'll get undefined.
var func=f.bar; // now func === Foo.prototype.bar
func(); // => undefined
See also How to find the object a function belongs to?
I have a 3rd party JavaScript application that declares several global functions and needs some variables to be defined for those functions to work. I would rather not define those variables in the global scope. Here's an example:
Suppose you have a globally defined function foo that prints out the value of an externally-defined variable named x.
function foo() {
console.log(x);
}
I think the only way for x to have a value within foo is if x is also a global. I hope I'm wrong. What I would really like to do is this:
(function () {
var x = 'someValue';
var bar = magicallyFixSoXIsntGlobal(foo);
bar();
}());
I guess I could do:
(function () {
var x = 'someValue';
var bar = Function(foo.toString());
bar();
}());
But that seems pretty much like eval. (If it comes down to that, I'd rather have global spam than use eval.)
Is there any way to "fix" the global functions so they can refer to the enclosed values?
Assign a value to a window's property, call the function, then use the delete operator to truly remove the variable.
window.foo = 'bar';
magic();
delete window.foo; // Because `foo` is defined without `var`, it can be deleted
The obvious solution is to modify foo to take x as a variable. But, assuming you can't / don't want to do that, I can think of two options:
declare x as a global variable.
Wrap foo's declaration in a closure.
The first will certainly make foo work, but please don't take Rob W's approach. Remember that the reason people tell you "don't clutter the global namespace" isn't because they're worried about unused variables hanging around (that can be a problem, but it's not specific to the global namespace). It's because having multiple functions using the same namespace increases the chances that one will overwrite the other's variables. Here's how you would use the global x without bludgeoning the rest of your code.
!function(x, exists) {
window.x=10;
foo();
if(exists)
window.x=x;
else
delete window.x;
}(window.x, 'x' in window);
The second option shouldn't be too hard if foo is currently just a function declaration within your code. In that case, change it to:
!function() {
var x=10;
function foo() {
[...]
}
// foo can only be called within this block
}();
Or if foo must be global, but you still don't want x to be global:
!function() {
var x=10;
window.foo=function() {
[...]
};
// now foo can be called anywhere, and will still use the x declared above
}();
But if foo is included via an external script then it gets more complicated, and I don't think you could use this method without modifying foo. (You could also use ajax and eval, or just eval, but I think you're right in avoiding that function.)
This is what function parameters are for
function foo(x) {
console.log(x)
}
You could hack it horrible if you wanted by converting the function to a string and creating a new function which accepts parameters, but that's dirty as hell
Let's say I have these two functions:
function fnChanger(fn) {
fn = function() { sys.print('Changed!'); }
}
function foo() {
sys.print('Unchanged');
}
Now, if I call foo(), I see Unchanged, as expected. However, if I call fnChanger first, I still see Unchanged:
fnChanger(foo);
foo(); //Unchanged
Now, I assume this is because foo is not being passed to fnChanger by reference, but I may be wrong.
Why does fnChanger not change foo to print Changed!?
Furthermore, how can I get fnChanger to change foo without too much messy syntax?
PS: I'm using node.js to test all this stuff, so that's where the sys.print comes from.
The assignment to the fn argument just makes that identifier to point to the anonymous function, foo in the outer scope is not affected.
When you pass an object as an argument, one can say "references are passed by value". The assignment just replaces the location where the fn identifier refers to.
That's how the evaluation strategy works in JavaScript.
Just before the assignment in the fnChanger functions, the two identifiers, the global foo and the fn argument, point to the same function object:
---------------------------------------------
foo -----> |function foo { sys.print('Un changed!'); } |
---------------------------------------------
^
|
fn -------------
After the assignment, fn will simply point to the new function:
---------------------------------------------
foo -----> | function foo { sys.print('Unchanged!'); } |
---------------------------------------------
---------------------------------------
fn ------> | function { sys.print('Changed!'); } |
---------------------------------------
How could you change it?
Well, assuming that foo is a function in the global scope, you could do something like this:
function fnChanger(obj, name) {
obj[name] = function() { sys.print('Changed!'); };
}
function foo() {
sys.print('Unchanged');
}
fnChanger(this, 'foo');
foo(); // Changed!
The above will work because in the fnChanger function, we require a base object and a property name, functions declared in the global execution context are bound as properties of the Global object, therefore we can re-assign its value in that way.
The line fnChanger(this, 'foo'); should be executed also in the Global scope, it will pass the this value (which refers to the Global object in this scope) and a property name, allowing you to make an assignment to the GlobalObject.foo identifier.
If that code were inside a function, there is no way we can get a base object, because in this "Function Code Execution Context", function declarations (variable declarations and function formal parameters also) are bound as properties of a non-accessible object, called the Variable Object (a chain of these Variable Objects, forms the Scope Chain), and if it were the case, the only workaround would be to use eval.
More info:
ECMA-262-3 in detail. Chapter 8. Evaluation strategy.
As #CMS pointed out you cannot assign it within the function due to the scope. However you could reassign it like this:
var fnChanger = function() {
return function() {
alert('changed!');
}
}
var foo = function() {
alert('Unchanged');
}
foo = fnChanger();
foo();
example
There is an elegant way around it
function fnChanger(fn_holder) {
fn_holder["fn"] = function() { console.log('Changed!'); }
}
function foo() {
console.log('Unchanged');
}
const foo_holder = {fn: foo}
fnChanger(foo_holder);
foo_holder["fn"](); // Changed!
Short explanation:
Pass a higher level reference foo_holder to fnChange like a hash (or an array).
Reassign your element in side the hash foo_holder["fn"] to a new value. This will change where the inner reference is pointing instead of creating a new reference.
Then you're good to have fun with your updated function.
In Javascript, functions are first class objects that can be treated just as another variable. But for the function to return its result, it has to be first invoked.
When you are invoking the fnChanger(foo), the fn variable actually gets overridden with foo().
But you are not getting the result because that function was never invoked. Try returning it and invoking as given below and you will the required answer.
function fnChanger(fn) {
fn = function() {
console.log('Changed!');
}
return fn;
}
function foo() {
console.log('Unchanged');
}
fnChanger(foo)();
foo();