Krzysztof Safjanowski
Krzysztof Safjanowski

Reputation: 7438

Expression evaluation - why parenthesis changes "this" reference?

Consider the example module creation, why parenthesis changes this reference?

As section 11.2.2 in JavaScript specification says:

The production NewExpression : new NewExpression is evaluated as follows:

  1. Let ref be the result of evaluating NewExpression.
  2. Let constructor be GetValue(ref).
  3. If Type(constructor) is not Object, throw a TypeError exception.
  4. If constructor does not implement the [[Construct]] internal method, throw a TypeError exception.
  5. Return the result of calling the [[Construct]] internal method on constructor, providing no arguments (that is, an empty list of arguments).

After some investigation there are no differences (are there?) between:

console.log(new (modules.getModule('foo')).method)
console.log(new (modules.getModule('foo')).method())

In both samples method were executed.

To be more interesting:

console.log(typeof new modules.getModule('foo').method) // function
console.log(typeof new (modules.getModule('foo')).method) // object

What is the source of those differences?

var modules = (function() {
    var definitions = {
        foo: {
            method: function() {
                this.thing = 'baz';
            }
        }
    };
    
    return {
        getModule: function(name) {
            if(name in definitions) {
                return definitions[name];
            }
        }
    };
}());


alert('this: ' + new modules.getModule('foo').method()) // undefined
alert('this: ' + new (modules.getModule('foo')).method()) // {this.thing = 'baz'}

Upvotes: 2

Views: 172

Answers (2)

Sylvia Stuurman
Sylvia Stuurman

Reputation: 137

The variable modules gets the value that is returned by the anonymous function: an object with a method getModule.

In the first case:

console.log(new (modules.getModule('foo')).method)

new is applied to the function method of object foo (which is the result of modules.getModule('foo'). The console should show the function method. In the second case:

console.log(new (modules.getModule('foo')).method())

Now, new is applied to the object foo (which is the result of modules.getModule('foo'). The object foo has a method method. But this time, method is executed. The console should show an object with property baz.

console.log(typeof new modules.getModule('foo').method) // function

Here, you ask the type of the method method of the object foo (the result of modules.getModule('foo')). The type is, obviously, a function.

console.log(typeof (modules.getModule('foo')).method) // object

Here, you ask for the type of the method method which is part of the object foo (which is the result of modules.getModule('foo'))

Upvotes: 0

Bergi
Bergi

Reputation: 664538

Parentheses don't change the this reference of a method call. Parentheses change the NewExpression that new evaluates.

If the new operator is in front of a property chain (an expression followed by accessors), it will evaluate the chain and instantiate the resulting constructor function.

If the new operator is in front of a call expression (an expression, possibly including accessors, followed by an arguments list), the call will provide the arguments for the new operation. Any trailing accessors will access properties of the newly instantiated object.

For your examples, that means

 new  modules.getModule ('foo') .method
 new  modules.getModule ('foo') .method()
// are evaluated as
(new (modules.getModule)('foo'))…
// on which then .method is accessed or called

new  (modules.getModule('foo')).method
new  (modules.getModule('foo')).method ()
// are evaluated as
new ((     …                  ).method)() // the empty parentheses are optional for `new`

(modules.getModule('foo')).method
// just evaluates to the `method` function
(modules.getModule('foo').method)

Upvotes: 4

Related Questions