1

Variables in JavaScript are lexically scoped. But, I wonder, is the this keyword, referring to the receiver of a method, an example of dynamic scoping. Or is this unrelated to the lexical/dynamic scoping discussion?

var foo = { x = “how am I scoped?” }
function bar() {
    console.log(this.x) // the free variable this is decided by the calling context, like I see dynamic scoping described
}
foo.bar()
  • this is more like an invisible function argument. It is not an example of dynamic scoping. Languages that feature dynamic scoping include shell/Bash and Perl via the local keyword, and a huge amount of template languages. – amon Aug 18 at 6:57
5

Dynamic scope seems to imply, and for good reason, that there's a model whereby scope can be determined dynamically at runtime, rather than statically at author-time. That is in fact the case.

Let's illustrate via code:

function foo() {
    console.log( a ); // 2
}

function bar() {
    var a = 3;
    foo();
}

var a = 2;

bar();

Lexical scope holds that the RHS reference to a in foo() will be resolved to the global variable a, which will result in value 2 being output.

Dynamic scope, by contrast, doesn't concern itself with how and where functions and scopes are declared, but rather where they are called from. In other words, the scope chain is based on the call-stack, not the nesting of scopes in code.

So, if JavaScript had dynamic scope, when foo() is executed, theoretically the code below would instead result in 3 as the output.

function foo() {
    console.log( a ); // 3  (not 2!)
}

function bar() {
    var a = 3;
    foo();
}

var a = 2;

bar();

How can this be? Because when foo() cannot resolve the variable reference for a, instead of stepping up the nested (lexical) scope chain, it walks up the call-stack, to find where foo() was called from. Since foo() was called from bar(), it checks the variables in scope for bar(), and finds an a there with value 3.

Strange? You're probably thinking so, at the moment.

But that's just because you've probably only ever worked on (or at least deeply considered) code which is lexically scoped. So dynamic scoping seems foreign. If you had only ever written code in a dynamically scoped language, it would seem natural, and lexical scope would be the odd-ball.

To be clear, JavaScript does not, in fact, have dynamic scope. It has lexical scope. Plain and simple. But the this mechanism is kind of like dynamic scope.

The key contrast: lexical scope is write-time, whereas dynamic scope (and this!) are runtime. Lexical scope cares where a function was declared, but dynamic scope cares where a function was called from.

Finally: this cares how a function was called, which shows how closely related the this mechanism is to the idea of dynamic scoping.

Source: You Don't Know JS: Scope & Closures, licensed under Creative Commons

  • This is confusing values and variables. this cares cares how a function is called the same way parameters care how a function is called - they get their value at runtime when the function is called. But the scope of all variables (including this) is lexical and not dynamic. – JacquesB Aug 22 at 6:55
1

No, this is lexically scoped to a function, just like a function parameter. It basically works like an implicit parameter.

Dynamic scoping means that if a variable is defined in a function, it is also visible inside functions called from this function (and functions called from those functions and so on).

This is not what happens with this though. Each function invocation have its own this binding which is set at the time of the function invocation (just like regular parameter values are set at the function invocation). The this of the calling function is not visible for the called function, so there is no dynamic scoping. (When a function is called without dot-notation, this is just set to global or undefined.)

A useful way of thinking of this is as a hidden parameter, like this:

Actual syntax:           What happens conceptually:

function bar() { }  ->   function bar(this) {}
foo.bar();          ->   bar(foo);             
bar();              ->   bar(undefined);  

So this is basically a syntactic trick to pass the stuff left of the dot as a parameter. But it doesn't affect scoping, and isn't anymore "dynamic" or "runtime" than regular parameters.

  • I think adding an example on what happens with const b = foo.bar; b(); would be useful – gcali Aug 22 at 17:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.