It is actually quite similar, see an example here: https://jsfiddle.net/veraee/63gs19j3/11/
Just to make it clear, there are two types of "variables" in handlebars:
The model/context. This is the data you inject into the template on redering. Use this for your normal work.
These variables are referenced in the template via its name in {{}}:
{{firstname}}
and are exposed in the javascript of the helpers via "this":
this.firstname
The "@"-variables. This data is generated inside the helpers via javascript.
These variables are referenced in the template via its name with '@'-prefix:
{{@foo}}
and are exposed in the javascript of the helpers via "options.data":
options.data.foo
The citation from the handlebars doc in the OP is about number 2.
In a programming language the "{" enters a scope,
in handlebars a block "{{#myBlock..." enters a scope.
Similar to the programming language you can use all variables from the outer scope in the inner scope (foo, bla),
but if you redefine one in the inner scope (bla), the original value will
be shadowed in the inner scope. When leaving the inner scope and being again
in the outer scope, you get the old value of bla again.
But note these differences to programming languages:
This scoping does not happen by some magic of the programming language syntax,
you have to do in on your own, as the doc also mentions, i.e. this way:
data = Handlebars.createFrame(options.data);
As a bonus, you can reach the shadowed variables (which is usually not possible in programming languages)
by prefixing the name with "../" which means: look at the previous (aka. outer) scope (see the "{{@../bla}}" in the inner scope in the example).