Transcript from the "Function Declarations, Function Expressions, and Block Scope" Lesson
>> Kyle Simpson: Let's talk about the difference between function declarations, and what we're gonna call function expressions. Function expressions are a lot more common than you may realize from that name. You may not have a good name for them, but they happen all over our code. Let's look at line one, and let's look at this function bar here.
[00:00:20] Is this a declaration? And the answer to that question is, no it's not. And here's your what you need to know definition. We could break it down grammatically, but I'm just gonna give you the what you need to know definition. The way you know if something is a function declaration, or if it's not a function declaration it has to be a function expression.
[00:00:37] The way you know the difference is whether or not the function keyword it the very first word in the statement. Okay? The very first thing in the statement. I do not mean the first thing on the line, I mean the first thing in the statement. So statements can break over multi lines, you can have multiple statements on a line, what we're looking for is that the function word is the first word in the statement.
[00:00:59] When we go back to our previous slide, slide 15, line three we see a function bar where the function key word is the first thing on the line. So it declares that function in its current existing scope. So, where did bar get found in that example? When we did function bar, where was that bar identifier registered.
>> Speaker 2: Global.
>> Kyle Simpson: In the global scope. Here's the difference with function expressions. We look on slide 16, we see a function bar here. Is the function keyword the first word in the statement? The first thing in the statement? No, so therefore it must be an expression. Regardless of how it's gonna be used, it's an expression.
[00:01:34] So in expressions we often see expressions as anonymous functions. Many of you have probably used them all over the place, if you write jQuery code or any of those other ones. It's very idiomatic to just do function open, close, parentheses. And we put those in our set time outs, and our click handlers, and all over the place.
[00:01:49] It's called an anonymous function expression. I'll explain to you in just a moment why name function expressions are better, but this is a name function expression. Why? Because on line one we see a name in the identifier position, we see function bar, as opposed to just function open, close, parentheses.
[00:02:04] Everybody see that? Right there on line one. That bar name, however, because it's a function expression, that bar name does not get declared in the outer scope. Whereas in the previous slide, when it was a function declaration, the name was put into the enclosing scope, with function expressions that are named expressions, the name of the function expression is enclosed within its own scope.
[00:02:26] So the name bar here exists from lines one through nine inside of itself. We can reference bar here and that will work just fine. But when we try to reference bar on line 12 outside of the function, clearly, it does not exist and we get a reference there.
[00:02:41] It's the difference between a function declaration and a function expression.
>> Kyle Simpson: Questions?
>> Kyle Simpson: Now, why do we care about this named versus unnamed thing? So, when you are in the habit of using, and many of us do this and I do it, I fall back to this all the time.
[00:03:01] When you use anonymous function expressions in your code, three major things occur that I consider to be three major negatives to using an anonymous function expression. What I would say is function expressions themselves, hugely useful. Use them all over your code. In fact, some people do them entirely, they don't do any declarations.
[00:03:21] Function expressions are great. What's bad is an anonymous function expression as compared to a named function expression. Okay. And here's why, the three negative to an anonymous function expressions. The first one is that when you have an anonymous function, if this bar did not exist we have no way inside of the function to refer to ourself.
[00:03:41] Say if you wanted to create a recursion or if it was a click handler and you wanted to unbind the click handler or whatever. We have no way to refer to ourself from within inside of the function if we didn't give it a name, but by virtue of putting that bar name there on line one we now have the ability to reference that function from inside of itself if we need to do recursion or whatever.
[00:04:02] There is a whole bunch of people that think incorrectly that the this key word is a reference to yourself and it's not. So you have no way of doing a self reference unless you give it a name. And the nice thing about this name is that it's safe.
[00:04:15] When we put bar there it is only gonna exist within the function expression. It's not gonna exist outside so it didn't pollute the outer scope, just gave us a nice self-referential name, lexically speaking. That's the first thing. The second thing, how many of you have ever had an error in production code?
[00:04:34] Maybe none of you. Maybe you all write perfect code. I do lots of production code errors. In fact, most of the time I sort of fumble my way to accidentally getting correctly working code. So I have lots of function errors, which means I do lots of debugging. And I do lots of debugging against minified code.
[00:04:48] Anybody ever seen a stack trace with anonymous function repeated like 15, 20 times? Happens all the time to me. The problem with anonymous functions is that they don't play well in debugging because it just says anonymous function. And especially when it's modified code, it's just anonymous function, line one, character 32,341.
[00:05:04] How helpful is that? Not very. But if you give it a name, that name gets used in the debug stack traces. So that's the second benefit of always using named function expressions as opposed to anonymous function expressions. The third and final reason, slightly weaker, is that it self-documents code.
[00:05:23] When using an anonymous function expression, you are required to look at the outer context to understand what that function is doing. But if you give it a nice descriptive name like handler, it's immediate looking at that function what it's doing, it's handling some event, or whatever. So giving it a name makes the code a little bit more understandable.
>> Speaker 2: We have a question in the chat for what a best practice is when a function is assigned to an object.
>> Kyle Simpson: You mean- So the function being, I'm assuming the function being assignment is the property of an object.
>> Speaker 2: Right Yes.
>> Kyle Simpson: So I would say in that case you're definitely making a function expression, cuz it's not a declaration, it's getting assigned, so that expression of value of function has value.
[00:06:27] Which happens. I mean, I'm guilty of that, too. I write that. Yeah?
>> Speaker 3: Perhaps, I missed this. But, In the current case wouldn't foo, ignoring line two for a second, would foo refer to the current function within this function if it were anonymous?
>> Kyle Simpson: So that's the problem with those, when you have a function assigned as an expression.
[00:06:47] You might be able to assume that variable was present, but what happens if later on down in the code on line 13, the global variable foo got rewritten to a different value. Now all of a sudden you don't have that variable to reference yourself with, and you don't know that you don't have the variable.
[00:07:01] So you can't rely on something out in your outer lexical scope. You can only rely on something that binds to your own scope. And, by virtue of what you just said, the fact that we do some shadowing here also kills that idea.
>> Kyle Simpson: Okay, great questions. Any questions so far about function expressions versus declarations?
[00:07:25] Any more that I'm missing?
[00:08:04] Meaning that the variable that you declare inside of your catch clause exists only inside of the catch clause, and not outside of the catch clause. So here we can reference the err inside of our catch, but when we try to reference that name outside, it's not available. Little bit of a caveat.
[00:08:22] iE6 screwed this up. IE6 did allow you to access the variable outside, but as of IE7 and above and all other browsers it obeys the spec, and it blocks scopes into the catch clause. Okay? So, we have this name that's existing inside of this catch clause, we have an example of block scope, is that anything more than just academic trivia?
[00:08:46] Put them into a bookmark there cuz we're gonna come back to this a little bit later, okay?
>> Kyle Simpson: As another side note on this, by the way, I just thought of this [COUGH] as another side note. Many of you probably use lint tools like JSLint and things like that, and I have seen many times where if you have the rule turned on that says you wanna be warned about duplicate variable definitions, and you have a bunch of try catches.
[00:09:08] If you do try catch err, try catch err and you repeat those all in the same scope, your linters will all complain at you saying that you're re-declaring that err over and over again, when in fact you're clearly not. Because it's block scoped, so it's not a multiple declaration.
[00:09:24] For some reason unbeknownst to me, I cannot understand, but for a decade or more we've had linters that are not intelligent enough to understand the nuance of block scoped catch variables and they all complain about it so your only two solutions are, and I used to do this, err1, err2, you know which sucks.
[00:09:46] Or, simply turn off that linting roll which is what I now do, because it's annoying that it complains to you about stuff that's not actually a problem.