10

I am pretty new to JS "strict mode";, when I use code like:

function outer(){

"use strict";
    var ctype;

    function inner(){

        if(ctype!=undefined){
            function hello1(){
                console.log("hello1");
            }
            hello1()
        }else {
            function hello2(){
                console.log("hello2");
            }
            hello2();
        }

    }

    return inner;

}

var inner = outer();

inner();

I wonder why Chrome(ver 49) give no error, but Node.js can give "SyntaxError: In strict mode code, functions can only be declared at top level or immediately within another function."

This table points out that my Chrome should report error.

Jeremy
  • 1
  • 85
  • 340
  • 366
Kuan
  • 11,149
  • 23
  • 93
  • 201
  • @DanD. Thanks, so this means Chrome does not use my code but change the code then check it in strict mode? – Kuan Apr 20 '16 at 22:07
  • Adding [node.js] tag, because this question is comparing its behaviour with Chrome (rather than comparing to a spec or something). – Jeremy Apr 20 '16 at 22:09
  • Seems like a bug. The parser should throw a SyntaxError before it ever has a chance to run. Shouldn't even need to be invoked, as in this simpler demo: https://jsfiddle.net/6zr12q7v/ –  Apr 20 '16 at 22:11
  • Node.js probably hasn't updated to the newer version of the V8 engine (used by Chrome) which changed this behaviour (introduced this bug?). – Jeremy Apr 20 '16 at 22:12
  • @JeremyBanks My chrome is ver 49, Node.js is 0.12.9 – Kuan Apr 20 '16 at 22:13
  • It looks like that means your Node.js is using V8 version 3.28.71.19, while your Chrome is using V8 version 4.9.385. – Jeremy Apr 20 '16 at 22:15
  • 1
    ES6 didn't add a feature that allows it, did they? Since a block can now create a scope, I wonder if this syntax is now permitted. –  Apr 20 '16 at 22:15
  • ...maybe they did. At least according to [this page](http://es6-features.org/#BlockScopedFunctions), it would seem so. Would have to find it in the spec. –  Apr 20 '16 at 22:17
  • 3
    @squint Huh... according to [this post](http://www.2ality.com/2015/02/es6-scoping.html), you're right, and ES6 modified `function` declarations to use block scoping instead of function scoping. I thought they were only using block scoping for new constructs like `let`/`const`/`class`. Looks like we have an answer. – Jeremy Apr 20 '16 at 22:18
  • @JeremyBanks: Yep, looks that way. –  Apr 20 '16 at 22:19
  • @JeremyBanks So what is the answer? – Kuan Apr 20 '16 at 22:21
  • @Bergi I think you should avoid using your dupe-hammer targeting questions that you have yourself answered. Introduces bias, you know. I believe (and the votes support) that my answer here is better than yours in that question. Quality is now supposed to take precedence over age when deciding dupe priority, but that hasn't happened in this case. Consider reverting your hammering, abstaining from voting, as moderator are obligated to do in cases where they have a conflict of interest. Leave the decision to impartial community members. – Jeremy Apr 21 '16 at 04:01
  • @JeremyBanks: Do you think [What are the precise semantics of block-level functions in ES6?](http://stackoverflow.com/q/31419897/1048572) would have been a better dupe target? – Bergi Apr 21 '16 at 11:21
  • @Bergi Potentially. The question's less of an exact match, but from a quick look your answer there might give that best explanation. However, in any case [you should abstain from unilateral action (mod/hammer closing) in cases where you have a conflict of interest](http://meta.stackoverflow.com/a/277129). I am more concerned about you doing this in general than I am about this specific question (my answer below is passable, but not particularly good, and made a small mistake as Felix King pointed out). – Jeremy Apr 21 '16 at 17:09
  • @JeremyBanks: I understand your concerns, and I do reflect on potential for conflict of interest. Often I only post a comment with a link instead of closing questions when it's not a crystal clear duplicate, but in this case I felt it was warranted (very similar question, and the answer pointing to the best explanation). – Bergi Apr 21 '16 at 17:19

1 Answers1

10

The version of Node.js you're using (0.12.9) uses an older version of the V8 JavaScript engine (3.28.71.19) which does not follow the new function declaration scoping rules added in ECMAScript 6. The version of Chrome you're using (49) uses a new version of V8 (4.9.385) which does support the new rules, at least in strict mode.

Prior to ECMAScript 6, function declarations would be scoped to the containing function. For example:

ECMAScript 5

function main() {
  if (true) {
    function example() {};

    console.log(example); // function example() {}
  }

  console.log(example); // function example() {}
}

This was considered confusing behaviour, and so it was prohibited in strict mode, leading to the error you see in Node.

In ECMAScript 6, function declarations are instead scoped to the nearest block. A block is any series of statements contain between two brackets ({ ... }), such as following an if statement.

ECMAScript 6

function main() {
  'use strict';

  if (true) {
    function example() {};

    console.log(example); // function example() {}
  }

  console.log(example); // ReferenceError: example is not defined
}

This behaviour is more intentional and less confusing, so it is permitted in strict mode.

However, testing this is a little bit confusing, because Chrome currently enables some ES6 rules only in strict mode.

Jeremy
  • 1
  • 85
  • 340
  • 366
  • This rough answer is based off a quick skim of some resources; I haven't used this behaviour before and may have misunderstood it. Any clarification (or a better answer posted below) would be greatly appreciated. (Hint, hint, downvoter. ;) – Jeremy Apr 20 '16 at 22:36
  • thanks, but sorry, I did not quite get your point. I have not used any ES6 feature in my code. I guess my situation is even I only use ES5 syntax, but once I specify `"strict mode"`, it behaves diff in Chrome and Node – Kuan Apr 20 '16 at 22:57
  • 1
    @Kuan I understand, it's quite strange. Previously, Chrome was following the rules of ES5. It is now starting to follow some of the rules in ES6, first in strict mode. The fact that you're not using any brand-new ES6 features in your code doesn't matter; Chrome's still going to apply the new ES6 rules, which include a new behaviour for `function` definitions. This new behaviour allows them inside of blocks in strict mode (not at the top level of a function), which the old behaviour did not. (I'd like to try to improve my answer to be a bit clearer, but I have to leave for an appointment.) – Jeremy Apr 20 '16 at 22:59
  • Thanks, now I get it. So it is still safe to use code like my way right now. By other saying, my code still ok to follow strict mode rule under ES6 spec as long I run it in Chome? – Kuan Apr 20 '16 at 23:37
  • @Kuan In Chrome *and* Node. As stated in the answer, the observed behaviour is actual only for older Node 0.x versions. – Estus Flask Apr 21 '16 at 00:20
  • 2
    This is such a good and sensible features. Puts to rest the conflict between spec vs implementations by taking advantage of the new reality of block scope. –  Apr 21 '16 at 01:12
  • 1
    *"Prior to ECMAScript 6, function declarations would be scoped to the containing function."* Just to be clear: In ES5, function declarations inside blocks is actually a syntax error, but most engines decided to accept it one way or the other. Chrome and Firefox for example implemented very different behavior. See also http://www.ecma-international.org/ecma-262/6.0/#sec-block-level-function-declarations-web-legacy-compatibility-semantics . – Felix Kling Apr 21 '16 at 07:01