Jump to content

Understanding let


Jambutters
 Share

Recommended Posts

let Unit = function(img, hp, type, spdX, spdY){
        let entity = new PIXI.Sprite(PIXI.loader.resources[img].texture);
        entity.img = img;
        entity.hp = hp;
        entity.type = type;
        entity.spdX = spdX;
        entity.spdY = spdY;
        rootStage.addChild(entity); 
        return entity;

    };
    

    let setup = () => {
       let player = Unit("./img/player.png",150, player);  //error , player is undefined.
       console.log(player);
       updateLoop();     
    };

 

I get an error of player is undefined as you can see but I'm not exactly sure as to why. This is more of an understanding javascript question, sorry.

Link to comment
Share on other sites

Yeah this isn't really anything to do with let, this is just standard coding (and pretty sure this error is the same in just about every classical—or pseudo-classical in JS's case—language).

If you're really interested in how let works, you'll need to understand block scoping:

Consider the following, using a function, which is a special case (I'll explain in a mo):

var foo = 'hello'
console.log(foo)

function bar () {
  var foo = 'world'
  console.log(foo)
}

bar()
console.log(foo)

// hello
// world
// hello

Function is a slightly special case in that functions have their own stack, so inside the `bar` function the variable foo is defined and pushed to the stack, the global scope (outside of the function) also has a stack and also has a foo variable but, due to that local stack you get the output and see that the global `foo` variable is untouched. Var and let do not differ here, the only case where you run in to trouble is if you omit the variable declaration altogether i.e.

// WARNING: anti-pattern ahead
// here be dragons

var foo = 'hello'
console.log(foo)

function bar () {
  foo = 'world'
  console.log(foo)
}

bar()
console.log(foo)

// hello
// world
// world

In this case the function still has a local stack but we have not declared a new variable in there so JS looks to its parent scope (in this case, the global, but if you nest functions you'll have a larger scope hierarchy) and finds a variable there called 'foo' and mutates it, hence why 'world' is logged twice. In fact, JS is even naughtier and will declare a variable on the root scope if it does not find one (not on a parent scope, on the root! which is equivalent here but often is not). This is the source of many an awkward bug, its awkward because its damn near impossible to spot what on Earth went wrong or why a mutation occurred, thankfully linters can pick this up very easily and you should use one if you do not already for easily-rectified sources of bugs like the above.

But what is this of let and var?

Well, we've touched on how functions create their own stack but in many other languages you'll find that any block can define its own scope, not so with JS, until now and the advent of let.

Consider the following:

var foo = 'hello'
console.log(foo)

{
  var foo = 'world'
  console.log(foo)
}

console.log(foo)

Hmm, if you've ever used C or Java or most other languages, you might assume that the output would be 'hello, world, hello' and each block has its own scope, but, not so in JS! Indeed, JS will let you redeclare variables with no issue and that is what happens here, effectively the block (delimited by { and }) is invisible to the JS engine, you could delete those lines and the code would execute exactly the same. Note that, again, double declarations can easily be caught by a linter and I'd recommend using one, if you need a simple style guide to follow that sets this all up checkout standard, its a no-frills set of rules that uses eslint to perform the linting, if you struggle setting this up on the command line then just about any editor you use will have relevant plugins to install, as standard is a no-config set of linting rules, you just have to switch the plugin on and get coding.

But what would the output be when using let:

var foo = 'hello'
console.log(foo)

{
  let foo = 'world'
  console.log(foo)
}

console.log(foo)

Now the output is 'hello, world, hello', as we might expect. By using let we alleviate some naming conflicts which is important as it stops some sneaky little bugs and means that we can be clearer about which variables we are mutating. This example hopefully illustrates that variables declared with let are block-scoped (as is const, although our worry of unexpected mutation is clearly not an issue with const) but variables declared with var are not.

Let is very useful inside any loops (which create blocks but are not functions) as it isn't immediately obvious that for loop iterator variables get punted out of their block, i.e. :

for (var i = 0; i < 10; i++) {
  ...do some work
}

console.log(i)

If you're anything like me then you probably would expect the program above to log `undefined`, but it does not, if you had used let then it would have (in fact, the engine should throw an error).

Note that talk of scope and declarations can get tricky in just about any language, but it can be particularly quirky (or unexpected) in JS, despite the fact that JS is extremely permissive. If you ever hear of JS foot-guns, or which there are many, understanding and using scope can be one of them, JS is both permissive and dangerous in its use of memory, which is great in a way as we can get up and running very quickly, but bad in other ways because your program is more likely to crash, and, perhaps more importantly, more likely to mysteriously crash (or get into an unexpected state, which is more likely and arguably more annoying).

If you haven't heard of Douglas Crockford then its worth seeing if you can get a copy of JS: The Good Parts, this work is old now and not all of it still holds true as recommendations in modern JS engines but it gives you a great grounding as to why certain patterns have gained prevalence in JS and why many constructs have made it into ES5 and now ES6 (and beyond).

Link to comment
Share on other sites

Note that while that line errors using `let`, it would not error when using `var` due to hoisting. Also, JavaScript is a function-scoped language. Only `let` and `const` are block-scoped. Catching errors like the one in this post are why I usually recommend using let/const whenever possible. Hosting is dumb.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...