1

I look at spec and I can't understand why this code cause an error:

L: function a(){
  console.log(1);
  break L; /// Uncaught SyntaxError: Undefined label 'L'
  console.log(2);
}

a(); 

In the same time with code blocks it works well:

M: {
  console.log(1);
  break M;
  console.log(2)
}

By the way I can just mark function by label without using that label inside function. And I won't get an error:

L: function a(){
  console.log(1);
  console.log(2);
}

a(); 

Main question: What is the main purpose of labelled function statements if we can't use label inside function?

MaximPro
  • 563
  • 8
  • 21
  • 1
    more info here https://medium.com/front-end-weekly/a-silent-resurrection-of-javascript-label-in-es6-era-3013e8cd544b – Matt Pengelly Nov 29 '22 at 14:20
  • @MattPengelly in your link it is not about labelled function statement – MaximPro Nov 29 '22 at 14:22
  • One heads-up is the label statement is not recommended to use in any modern Javascript convention. MDN specifically states as below: Labeled loops or blocks are very uncommon. Oftentimes function calls can be used instead of loop jumps. it looks like it allows the use of break within it, and also you and do something like `break `. still very confusing as to the purpose of this – Matt Pengelly Nov 29 '22 at 14:22
  • @BenAston but why I can define function statement with label (without using label inside function)? – MaximPro Nov 29 '22 at 14:23
  • @maximPro yes it is. Please read it all before saying it doesnt answer your question. – Matt Pengelly Nov 29 '22 at 14:23
  • this is a dupe of https://stackoverflow.com/questions/61511633/labelled-arrow-functions the reason for this syntax is for backwards compatibility only. – Matt Pengelly Nov 29 '22 at 14:26
  • Does this answer your question? [Labelled arrow functions](https://stackoverflow.com/questions/61511633/labelled-arrow-functions) – Matt Pengelly Nov 29 '22 at 14:27
  • @MattPengelly a lot info from you I am trying handle it all :) – MaximPro Nov 29 '22 at 14:29
  • @MattPengelly well, I've read all that you dropped to me. You say labelled function statement it is backwards compatibility. But I don't see sense when it was implemented. What sense in labelled function statement if I cant use label inside that function... This thing I can't understand. – MaximPro Nov 29 '22 at 14:36
  • @MattPengelly the same comparison: take this gun but you shouldn't shot with it. – MaximPro Nov 29 '22 at 14:38
  • 1
    And if you think you need to use break with labels, I would suggest you rethink your design of the code. – epascarello Nov 29 '22 at 15:26

1 Answers1

3

TLDR

By the mid-1990s goto was frowned-upon as being "dangerous" and confusing and was thus elided from new languages. But abruptly exiting or "jumping" out of block constructs (blocks, try, with, if, loop constructs) was still a desirable feature, and so labels and break/continue were included as a kind of watered-down replacement.

Note how labels and break/continue do not enable the looping behavior of the more powerful goto: they only allow you to effectively jump forward, out of a block.

Functions are also block-like constructs. According to the spec, functions were not statements and therefore could not be labelled, but standards were seen as mere flexible guidelines during the early years of the Web; thus browser vendors inconsistently enabled the labelling of functions ("labelled function statements") [which would provide an alternative syntax to return?] This behavior was not in the original standard, but was backwards-standardized in ES2015 to throw an error if a labelled function statement was referenced by break <label>/continue <label>, although a labelled function statement without a reference to the label would be allowed for backwards compatibility reasons.

This explains the behavior you observe.

Details

According to the spec, you can label all statements in JavaScript. This has been so since ECMAScript version 2.0, when labels were added to the language.

These are the kinds of statement in JS:

BlockStatement, VariableStatement, EmptyStatement, ExpressionStatement, IfStatement, BreakableStatement, ContinueStatement, BreakStatement, ReturnStatement, WithStatement, LabelledStatement, ThrowStatement, TryStatement, DebuggerStatement.

Note that blocks are statements.

But labels are only useable with break and continue, and so labelling some of these statements (eg. Variable, Return, Throw), while permitted, is pointless.

This means that labels can be applied to constructs with which break and continue cannot be used, making labels in those positions de facto only useable as pseudo-comments.

The language was designed this way because it simplified engine development by avoiding the need for "dead label analysis".

"Function statements" were functions "in the statement position". ECMAScript 5.1 had the following to say:

Several widely used implementations of ECMAScript are known to support the use of FunctionDeclaration as a Statement. However there are significant and irreconcilable variations among the implementations in the semantics applied to such FunctionDeclarations. Because of these irreconcilable differences, the use of a FunctionDeclaration as a Statement results in code that is not reliably portable among implementations. It is recommended that ECMAScript implementations either disallow this usage of FunctionDeclaration or issue a warning when such a usage is encountered. Future editions of ECMAScript may define alternative portable means for declaring functions in a Statement context.

This means that in some older browsers function "declarations" could sometimes be treated as a statement, and therefore they could be labelled. What the precise behavior of historical browsers was with labelled functions I do not know. Modern browsers raise a syntax error if you attempt to refer to the label, as you found.

So the following (a try statement) is valid JS:

let foo = 'a'
myLabel: try {
    if(foo === 'a') break myLabel
    console.log('this will not be printed')
} catch {}
console.log('all done')

...and the following (switch statement) is valid:

let foo = 'a'
myLabel: switch(foo) {
  case 'a': 
    break myLabel
    console.log('this will not be printed')
}
console.log('all done.')

But also, the following (return statement) is valid (but pointless):

function foo() {
  myLabel: return 'result of foo'
}

console.log(foo())

...and the labelling of this variable statement is pointless too:

myLabel: var foo = 'value of foo'

console.log('works fine, but the label was pointless')
Ben Aston
  • 53,718
  • 65
  • 205
  • 331