With ES6, I can create a new object with functions like the following:
var obj = {
something() {}
};
That makes sense. But I can also do this:
var obj = {
'something'() {}
};
Or I can do this:
var obj = {
['something']() {}
};
Is there a difference between these three syntaxes? Why are all of these syntactically valid?
Is there a difference between these three syntaxes?
Not wrt to the results in your example.
However, the different syntaxes do have different characteristics. The way the property name is defined is not specific to method definitions btw, the rules apply to all property names:
Property names that are valid identifier names or number literals don't need to be quoted:
{
foo: ...,
10e4: ...,
if: ...,
}
Anything else needs to be quoted:
{
'foo+bar': ...,
'abc def': ...,
'123,45': ...,
}
The square bracket syntax is new in ES6 and allows you do dynamically compute property names:
{
[getPropertyName()]: ...,
['item' + (i * 3)]: ...,
}
Why are all of these syntactically valid?
Because the grammar allows it:
MethodDefinition :
PropertyName ( StrictFormalParameters ) { FunctionBody }
GeneratorMethod
get PropertyName ( ) { FunctionBody }
set PropertyName( PropertySetParameterList ) { FunctionBody }
PropertyName :
LiteralPropertyName
ComputedPropertyName
LiteralPropertyName :
IdentifierName
StringLiteral
NumericLiteral
ComputedPropertyName :
[ AssignmentExpression ]
(not sure what kind of answer you expect here)
If you consider methods to be equivalent to assigning a function to the property, it seems to make sense to apply the same rules for property names to function/method names.
First and second are the same, and do the same as
obj.something = function something() {}
the third one creates an anonymous function and stores it in obj.something. It's an equivalent to this:
obj['something'] = function() {}
Quotes allow to create keys (and hence function names) that are not valid identifiers in JS, for example:
var obj = {
'123'() {}
};
creates a function with the name 123, believe it or not.
The square brackets syntax allows arbitrary expressions, so you can do
var obj = {
['myfunc_' + getFuncName()] () {}
}
and similar cool things.
Related
This question already has answers here:
How does this object method definition work without the "function" keyword?
(2 answers)
Closed 6 years ago.
I'm curious about the following two methods of declaring functions within a variable in Javascript. What's the difference between the two function declarations below? Both seem to work. Are there any drawbacks to using one over the other? They seem to be constructed slightly differently when looking at the debugger.
In addition, I'm fairly certain the first method is called 'object literal notation'. Is there a formal name for the second method?
var myVar = {
testProperty: 'testProperty',
// Object literal notation?
testFunc: function()
{
console.log('testFunc called');
},
// What's this called? 'Named function declaration'?
testFunc2()
{
console.log('testFunc2 called');
}
}
// Both work...
myVar.testFunc();
myVar.testFunc2();
There are multiple ways to define properties (PropertyDefinition) in an object initializer (ObjectLiteral):
A "simple" PropertyDefinition
PropertyName[?Yield] : AssignmentExpression[In, ?Yield]
For example:
var obj = {a: 1};
obj.a; // 1
Method definitions
This includes getters and setters, added by ECMAScript 5
get PropertyName[?Yield] ( ) { FunctionBody }
set PropertyName[?Yield] ( PropertySetParameterList ) { FunctionBody }
For example:
var obj = {n: 0, get a() { return ++obj.n; }};
obj.a; // 1
obj.a; // 2
And also methods and generator methods, added by ECMAScript 6
PropertyName[?Yield] ( StrictFormalParameters ) { FunctionBody }
* PropertyName[?Yield] ( StrictFormalParameters[?Yield] ) { GeneratorBody }
For example:
var obj = {a(n) { return 2*n; }};
obj.a(1); // 2
obj.a(2); // 4
PropertyDefinition using computed property names, added by ECMAScript 6
[ AssignmentExpression[In, ?Yield] ] : AssignmentExpression[In, ?Yield]
For example:
var prop = "a",
obj = {[prop]: 1};
obj.a; // 1
"Shorthand" PropertyDefinition, added by ES6
IdentifierReference
For example:
var a = 1,
obj = {a};
obj.a; // 1
They have different syntax and provide different functionalities, but the result is always the creation of a property in the resulting object. In your case, a method definition is basically the same as a "simple" PropertyDefinition where the AssignmentExpression is a function expression. However, with the later you can specify a custom name to the function.
Also see
ECMAScript 5 - Object Initialiser
ECMAScript 6 - Object Initialiser
MDN - Object initializer
if you declare a function inside an object, that function is called the method of the object, but there are still called function, you should probably use the first method rather than using the second one
When specifying a key in an object's key-value pair (using the notation below) the interpreter (apparently) allows the use of strings:
var x = { 'color': '#fff' };
However specifying a key dinamically using a function (that returns a string) is not allowed:
function s()
{
return 'color';
}
var x = { s(): '#fff' };
I guess strings, when using that notation, must be static values.
However I cannot find JavaScript language specifications regarding that...
In this case you should use this method:
var x = {};
x[s()] = "#fff";
x[foo()] = "#000";
According to this MDN article (I highlight with bold):
The syntax for an object using an object initializer is:
var obj = { property_1: value_1, // property_# may be an identifier...
2: value_2, // or a number...
// ...,
"property n": value_n }; // or a string
where obj is the name of the new object, each property_i is an identifier (either a name, a number, or a string literal), and each value_i is an expression whose value is assigned to the property_i.
So in this literal notation it is not allowed to evaluate expressions, e.g. via function calls to determine the property identifiers.
In the ECMAScript Language Specification it is more formally put:
PropertyName:
IdentifierName
StringLiteral
NumericLiteral
ECMAScript 2015
With ECMAScript 2015 more becomes possible as explained in this MDN article:
Starting with ECMAScript 2015, the object initializer syntax also supports computed property names. That allows you to put an expression in brackets [ ], that will be computed as the property name.
// Computed property names (ES6)
var i = 0;
var a = {
["foo" + ++i]: i,
["foo" + ++i]: i,
["foo" + ++i]: i
};
The formal definition in the ECMAScript 2015 Language Specification has:
PropertyName:
LiteralPropertyName
ComputedPropertyName
ComputedPropertyName:
[ AssignmentExpression ]
So with ES6 you would rewrite your example like this:
function s()
{
return 'color';
}
var x = { [s()]: '#fff' };
"Associative arrays" do not exist in Javascript. You're probably referring to Objects. This is a pretty common mistake when learning JS.
An Object can be initialized using { }, or with the new operator like so :
var x = {};
var y = {foo : 'bar'};
var z = new Object();
When accessing an object properties, you can either use the . operator, or brackets.
var somevalue = y.foo; // 'bar'
var someother = y['foo'] // 'bar'
In your current situation, you'd want something like that :
var x = new Object(); // or {}
x[s()] = "#fff";
Object type
If you check the type of x, it will return 'object'.
var typeOfX = typeof x; // 'object'
I know there are a ton of EloqJS questions on here, but I haven't seen this one asked and it is throwing me for a loop. I'm curious how the key squirrel in the object entry (squirrel: squirrel) is not replaced by the argument passed by the function in this code:
function addEntry(squirrel) {
var entry = {events: [], squirrel: squirrel};
for (var i = 1; i < arguments.length; i++)
entry.events.push(arguments[i]);
journal.push(entry);
}
addEntry(true, "work", "touched tree", "pizza",
"running", "television");
I would expect for it to entry to be {events: [], true: true}, but that is not what this is returning. What am I obviously missing here? Original code at http://eloquentjavascript.net/04_data.html#arguments_object
Because object keys are literals: they don't change at runtime or act as variables to be replaced with a value.
If you look at the object initialiser section of the spec (11.1.5), it specifies that an object literal consists of a property name/value list, with property name defined to be one of:
IdentifierName
StringLiteral
NumericLiteral
The rules for IdentifierName are defined in section 7.6 to include most unicode characters, but not reserved words or whitespace.
Any IdentifierName can be wrapped in quotes to become a valid StringLiteral, although the inverse is not always true (StringLiterals containing punctuation or whitespace need the quotes to be valid). You can define the object just as well with:
{'events': [], 'squirrel': squirrel}
As of ES6, there is now a notation to specify that a key should be the value of a variable rather than an identifier or literal of its own. This is defined in section 12.2.6 as a ComputedPropertyName, using the [expr] syntax. For the object to have a true: true property, you could use the ES6 code:
{'events': [], [squirrel]: squirrel}
That's because Object Literal Notation can't receive variables as object keys.
For example:
var name = "John";
var person = {
name: name
}
// person = { name: "John" }
var name = "John";
var person = {};
person[name] = name;
// person = { "John": "John" }
<script>
(function() {
$('html').addClass('js');
var contactForm = {
container: $('#contact'), <-- THIS COMMA
init: function() {
$('<button></button>', {
text: 'Contact Me'
})
.insertAfter('article:first')
.on('click', this.show);
}, <---------------------------------- AND THIS COMMA
show: function() {
contactForm.container.show();
}
};
contactForm.init();
})();
</script>
In the above script, I noticed:
container: $('#contact'),
Is that one way to declare a variable? Doing the following breaks the script:
var container = $('#contact');
Also, what is with the commas after the init function and the container variable (if it is a variable)?
This way you declare an object:
var contactForm = {
// properties:
property1 : value,
property2 : value,
property3 : value,
// methods:
method1 : function() {
// ...
},
method2 : function() {
// ...
}
};
You can find more information about JavaScript objects in MDN ...and in the comments below :)
That block of code (beginning with var contactForm = {) is declaring an object using object literal notation. Commas separate the different key-value pairs in the object literal notation.
var obj = { key1: value1, key2: value2 };
Commas are used to separate name/value pairs when defining objects:
var newObj = {
name: value,
another_name: another_value
};
That is called Object literal notation.
Which is basically a Comma-Separated list of name/value pairs, wrapped in curly braces.
Advantage of this notation is that all data within the braces are encapsulated and not defined globally which avoids conflicts with other scripts or libraries.
Those are key:value pairs of the object contactForm you defined separated by commans
var obj = { key1 : value1,
key2 : value2
method1 : function(){
// definition
},
method2 : function(){
// definition
}
}
The thing that confused me in my early days is that what is function(){....} doing inside a variable(in this case an object) definition until I discovered functions are objects too in js which may not be the same case in language you have used earlier .
and this function function(){....} without name is called a anonymous function.
Here's a good thread on anonymous function Why do you need to invoke an anonymous function on the same line?
I've seen that can use object literal notation to define an object with a getter like so:
var foo = {
get bar() { return 3; }
}
Is there some object literal syntax that allows me to set other property descriptor attributes of "bar" above, such as 'enumerable'?
Or, is 'get/set' the only property descriptor attributes supported for ECMAScript5 object literal notation?
A good question...but I don't think it can be done.
get and set are defined as operators that act the same way as property descriptors:
Object.defineProperty(foo, "bar", {value : 3,
writable : true,
enumerable : true,
configurable : true,
get : function(){ return value; },
set : function(newValue){ value = newValue;}
});
Looking through the javascript operator list I don't see anything similar for other descriptors apart from get and set.
No, you're gonna have to use Object.defineProperty.
This is what the specs say:
ObjectLiteral:
{ }
{ PropertyNameAndValueList }
{ PropertyNameAndValueList , }
PropertyNameAndValueList:
PropertyAssignment
PropertyNameAndValueList , PropertyAssignment
PropertyAssignment:
PropertyName : AssignmentExpression
get PropertyName() { FunctionBody }
set PropertyName( PropertySetParameterList ) { FunctionBody }
The only thing you may also want to use is the setter syntax, but you've probably known that.
Some browsers support a __proto__ property that lets you set an objects prototype, but it's not specified and might be removed every day.
Use it like a regular property name:
var foo = {
__proto__: bar
};