4

I'm trying to declare a bunch of constants using const. My problem is that testing the code in the Firebug console throws an error complaining about the 'redeclaration of const foo'.

I've attempted wrapping it in a try{}catch(e){} block but that didn't help, and even when trying to get around it using the following code (posted minus all the console.info() "debugging" calls for clarity) it still throws the error when run for the second time:

if(!chk_constsDeclaredYet) {
  var chk_constsDeclaredYet = true;
  const foo="bar";
}

My question is that while the const is within the if(){}, when the code is 'run' for the second time, why is const foo being even being looked at?

NB: the code is to be run inside the firebug javascript console and the workflow i'm trying to achieve is:

  1. paste code into firebug console
  2. hit run (creating constants)
  3. i make an edit to the code in the console without reloading the page (the constants are still defined in the page context)
  4. hit run again (using the if(){} to avoid redeclaring the constants if they've already been declared by the previous run)
  5. repeat from (3)

firebug output:

//FIRST RUN::
>>> console.group() console.info('--code start--'); ...console.info('--code end--'); console.groupEnd()
--code start--
chk_constsDeclaredYet = undefined
foo = undefined
--if()--
--if() running..--
--end if()--
chk_constsDeclaredYet = true
foo = bar
--code end--

//SECOND RUN::
>>> console.group() console.info('--code start--'); ...console.info('--code end--'); console.groupEnd()
TypeError: redeclaration of const foo { message="redeclaration of const foo", more...}
kwah
  • 1,149
  • 1
  • 13
  • 27
  • Note: I'm not worrying about support for const atm - just toying around / experimenting for now, though a pointer toward an indicator of which browsers support it would be appreciated =P If not, it'll just be the topic of my next search engine visit =] – kwah Nov 11 '10 at 19:27
  • https://developer.mozilla.org/en/JavaScript/Reference/Statements/const It is in Firefox, and has partial support in Opera and Safari (and probably Chrome, too), but not IE – Šime Vidas Nov 11 '10 at 19:40
  • @Šime Vidas thanks, I saw that but couldn't quite figure out exactly what "partial support" meant =P – kwah Nov 11 '10 at 19:42
  • Yea, I love it too when it says partial support. Basically, that means that you have to test for yourself :) – Šime Vidas Nov 11 '10 at 19:45
  • @Šime Vidas bleurgh testing.. ;) maybe later but i cba right now (was just a passive 'ooh, I wonder if this would work in a chrome/opera userscript' thought.. not important at all =] – kwah Nov 11 '10 at 19:53

3 Answers3

6

This is an old-ish answer. I wrote a slightly newer answer dealing with similar "const re-assignment/scope" issues where I show that the errors produced, if any, vary by execution method and browser.

Since const (which is not part of the ECMAScript 5th edition standard) is slated to have a different meaning in ECMAScript 6th edition, I would recommend avoiding it in current code.


const, like var is "function-scoped". I suspect the problem arises from the same kind of "to top of function" promotion on the binding that happens with var (this explains why the exception does not come from the assignment but from the declaration). That is, any subsequent const x = ..., wherever they appear, are considered to be invalid because a previous declaration already exists (by definition, there can only be one const of a given name per scope). However, const can take on any value so the assignment happens at the const x = ... site, just as the assignment occurs at the var x = ... site, even though the annotation/binding was promoted to the top of the scope.

Here is a simple test-case which demonstrates the problem more clearly:

function x () { if (true) { const a = 1 } else { const a = 2 }}
// => TypeError: redeclaration of const a @ <x-jsd:interactive-session

As you can see, the error occurs at the function declaration and not at the function execution. This is why the try/catch does not work. The behavior may also be influenced by the interactive tool you are dealing with depending on how it executes the code (e.g. it's the same execution context each time?).

However, this works fine and reinforces the initial proposition above:

(function x() { if (false) { const c = 1 }; return c })()
// => undefined

From https://developer.mozilla.org/en/JavaScript/Reference/Statements/const

(Bold Emphasis added)

Creates a constant that can be global or local to the function in which it is declared. Constants follow the same scope rules as variables.

The value of a constant cannot change through re-assignment, and a constant cannot be re-declared. Because of this, although it is possible to declare a constant without initializing it, it would be useless to do so.

A constant cannot share its name with a function or a variable in the same scope.

const is a Mozilla-specific extension, it is not supported by IE, but has been partially supported by Opera since version 9.0 and Safari.

Community
  • 1
  • 1
1

A const type should always equal a constant value. If an if statement can change the value of a constant, it should probably be a var, since its value can vary.

But the issue you are having appears to be because you are setting the value of a constant twice. You initialize the constant the first time the code runs. Since the constant has a value when it is ran again, you get the exception. If you move the const foo = 'bar'; to the top of your script, you won't see that problem. But once again, if you want that value to change based on logic, you'll want a var.

Edit

If you read the exception message, it says "redeclaration of const foo". This means you CANNOT declare foo twice. Now in the console, each time you run the code (without refreshing), the previous variables are still in scope. So in theory, you are declaring const foo multiple times even though you only see it once in your code. You can't wrap it in if/else blocks because it still requires const foo = 'bar', which is still an illegal declaration.

You might have to settle for refreshing the page between running your changed code.

Steve Hansell
  • 17,465
  • 1
  • 17
  • 14
  • @Steve Hansell I think you misunderstand - the idea is to only declare the constant if it hasn't already been declared, **NOT** to change the value based on logic.. maybe `if(!foo) { const foo = 'bar'; } else { /*do nothing because foo is already around */ }` makes my intention more clear? – kwah Nov 11 '10 at 20:02
  • I understand, but its still not a constant if it COULD not be set. But with that aside, and I haven't played with constants in JS much, this could be an issue of hoisting. So even though you declare the const in the if block, it has actually been raised to the top of its function. – Steve Hansell Nov 11 '10 at 20:05
  • 1
    More on hoisting: http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting – Steve Hansell Nov 11 '10 at 20:05
  • And it looks like if you declare a const foo; and then try to give it a value later, it will still throw an exception. The only way to give a constant a value is to declare it with const foo = 'bar'. If you write foo = 'bar', you are actually creating a global variable, not assigning to the constant that has the same name. – Steve Hansell Nov 11 '10 at 20:12
  • either I am misunderstanding you or you still don't seem to understand what it is I'm trying to achieve.. **nb: the code is to be run inside the firebug javascript console**.. the workflow i'm trying to achieve is 1) paste code into firebug console 2) hit run (creating constants) 3) i make an edit to the code in the console without reloading the page (the constants are still defined in the page context) 4) hit run again (using the if(){} to avoid redeclaring the constants if they've already been declared by the previous run) 5) repeat from (3) ... – kwah Nov 11 '10 at 22:10
  • **NOTE:** a) the constant will *ALWAYS* be set b) I do not wish to redefine it - this is precisely what I'm trying to *avoid* – kwah Nov 11 '10 at 22:11
0

I don't know if this is the best way, but eval will let you get around that:

if( !chk_constsDeclaredYet ){ eval("const foo = \"bar\"") }
cwallenpoole
  • 79,954
  • 26
  • 128
  • 166
  • Thanks - hadn't thought about this option but I'm still going to continue to avoid eval() like the plague and just use var instead unless something else come up ;) – kwah Nov 11 '10 at 19:59