theEpsilon
theEpsilon

Reputation: 1919

Error sometimes thrown with JavaScript code block

I'm using code blocks to simulate static function variables a la C. Here's the basic setup:

{
    let bob = 5;
    function b() {
        console.log(bob++);
    }
}

Now in chrome this compiles just fine with no complaints. In Safari however, I get a

SyntaxError: Unexpected identifier 'bob'. Expected a ':' following the property name 'let'.

I have no idea what's causing this discrepancy as both Chrome and Safari handle ECMAScript 6.

Upvotes: 2

Views: 214

Answers (2)

gcochard
gcochard

Reputation: 11734

Based on the error message, Safari is interpreting your block as an object literal. The syntax is valid, but Safari seems to be choking on it.

When I tried this out in my own console, I was able to get it working by placing a statement immediately following the block:

{ let bob = 5; function b(){ console.log(bob++); } } console.log('foo');

However, this seems to hoist the function out of the block scope without creating a closure, causing a ReferenceError claiming that it can't find variable bob.

I tried explicitly wrapping it in a function and starting that function with the use strict declaration, in case it was not running in strict mode as that can change the behavior:

function strict() {
  'use strict';
  {
    let bob = 5;
    function b() {
      console.log(bob++);
    }
    return b;
  } 
}
strict()();

And it worked!

TL:DR; block handling is weird in loose mode. Use strict mode when defining functions inside blocks.

Upvotes: 1

T.J. Crowder
T.J. Crowder

Reputation: 1074666

The problem is that the code is in loose mode and you're declaring a function in a block. Function declarations in blocks were only standardized in ES2015 and, well, they make sense in strict mode but in loose mode they're...weird.

In strict mode, your code works, possibly as you expect or possibly not. bob is accessible to b...and neither bob nor b accessible outside that block, unless you do something to expose them outside it.

Here's an example you can use to test it on both Safari and iOS Safari (I have only the latter available to me). This version gives an error:

<script>
window.onerror = e => {
    document.body.insertAdjacentText("beforeend", String(e));
};
</script>
<script>
{
    let bob = 5;
    function b() {
        document.body.insertAdjacentText("beforeend", bob++);
    }

    b();
}
</script>

The error is:

ReferenceError: Can't find variable: bob

This version works:

<script>
window.onerror = e => {
    document.body.insertAdjacentText("beforeend", String(e));
};
</script>
<script>
"use strict"; // <============================
{
    let bob = 5;
    function b() {
        document.body.insertAdjacentText("beforeend", bob++);
    }

    b();
}
</script>

I have also replicated the behavior in the latest version of JavaScriptCore (Apple's JavaScript engine), v265499. I installed¹ and ran it locally (changing console.log/insertAdjacentText to print, which is available in most of the raw engine executables) and get the same error in loose mode and not in strict mode.


If you want b available outside the block, you're probably better off doing something like this:

"use strict";
const b = (() => {
    let bob = 5;
    return function b() {
        console.log(bob++);
    };
})();
b();

Which is a lot bulkier, but... Alternatively, with let:

"use strict";
let b;
{
    let bob = 5;
    b = function b() {
        print(bob++);
    };
}
b();

¹ Using the very handy jsvu utility.

Upvotes: 3

Related Questions