views:

119

answers:

5

In Javascript, we can call methods on string literals directly without enclosing it within round brackets. But not for other types such as numbers, or functions. It is a syntax error, but is there a reason as to why the Javascript lexer needs these other types to be enclosed in round brackets?

For example, if we extend Number, String, and Function with an alert method and try calling this method on the literals, it's a SyntaxError for Number and Function, while it works for a String.

function alertValue() {
    alert(this);
}

Number.prototype.alert = alertValue;
String.prototype.alert = alertValue;
Function.prototype.alert = alertValue;

We can call alert directly on a string object:

"someStringLiteral".alert() // alerts someStringLiteral

but it's a SyntaxError on numbers, and functions.

7.alert();
function() {}.alert();

To work with these types, we have to enclose it within brackets:

(7).alert(); // alerts "7"
(function() {}).alert(); // alerts "function() {}"

Update:

The link by @Crescent and answers by @Dav and @Timothy explain why 7.alert() fails, as it's looking for a numerical constant, and to get past it, insert extra whitespace or an extra dot.

7 .alert()
7..alert()
7. .alert();

Is there a similar syntactical reasons for why functions need to be enclosed in parentheses before invoking a method on them?

I am not well versed with interpreters and lexers to know if it's a problem that can be solved with some sort of a lookahead, as Ruby is a dynamic language and takes care of this problem. For example:-

7.times { |i| print i }

Update 2:

@CMS's answer has been spot on in understanding why functions were not working above. The statements below work:

// comma operator forces evaluation of the function
// alerts "function() {}"
<any literal>, function() {}.alert();​​​​​​​

// all examples below are forced to be evaluated as an assignment expression
var a = function() {}.alert(); 

var b = {
    x: function() { return "property value" }.alert()
}

[ function() { return "array element" }.alert() ];​
+3  A: 

Hint: 7. is a valid numerical constant.

Amber
+1  A: 

The lexer is expecting a decimal value when it sees a number immediately proceeded by a period. Though you could get "creative" and put a space between the number, as in 7 .alert() but people would probably hunt you down for it when they get stuck maintaining your code!

Timothy
`7 .alert()`, and `7..alert()` are clever ways of getting past this problem (though nightmarish for maintenance programmers when things do go south), but I'm wondering if this is something that can be fixed at the lexer end when parsing the tokens, and have some sort of a lookahead while constructing the syntax tree. Ruby is a dynamic language and uses a similar syntax, ex) `5.times { |i| print i }`
Anurag
A: 

Use the decimal point where the number expects it, and the dot operator will work as the object expects it.

7.0.toFixed(0)

kennebec
+6  A: 

For the Number literal all other answers have pointed you in the right direction, 7. is a valid DecimalLiteral.

The grammar tells you that the decimal digits after the first dot are optional:

DecimalLiteral ::
DecimalIntegerLiteral . DecimalDigitsopt ExponentPartopt 

* Note the opt suffix

Now, for the function, the problem is that it is being evaluated in statement context.

There are two valid grammatical ways to create function objects (the third way to create functions is using the Function constructor, but for now it's out of the topic).

FunctionDeclaration:

function name(/*[param] [, param] [..., param]*/) {
   // statements
}

Function Expression:

var foo = function /*nameopt*/(/*[param] [, param] [..., param]*/) {
  // statements
};

The grammar is almost identical, the difference is where the function keyword appears.

A function declaration occurs when the function keyword is found directly on global code or in the FunctionBody of a function.

A function expression occurs then the function keyword is found on a expression context, like in the above example, the second function is part of an AssignmentExpression.

But you have actually two problems, first, the name of a FunctionDeclaration is mandatory.

Second, when the FunctionDeclaration statement is evaluated the dot will simply cause a SyntaxError because the dot isn't expected.

Wrapping the function between parentheses (formally called The Grouping Operator) makes the function to be evaluated in expression context, a function expression.

For example, the following is valid:

0,function () {}.alert();

The above works because the comma operator evaluates expressions.

You should know also that the FunctionDeclarations are evaluated at "parse time" (more precisely, when the control enters into an Execution Context, by the Variable Instantiation process), and the identifiers (function names) are made available to the entire current scope, for example:

foo(); // alerts "foo", function made available at 'parse time'
foo = function () { alert('bar') }; // override with a FunctionExpression
function foo () { alert('foo'); } // FunctionDeclaration
foo(); // alerts "bar", the overriden function

Recommended article:

CMS
Really appreciate this detailed answer. It has been very helpful in improving my understanding of Javascript.
Anurag
You're welcome @Anurag, please comment if you have any doubt.
CMS
A: 

I prefer to use parenthesis to disambiguate:

(7).alert();
trinithis