Since ECMAScript 6, most function objects have a name property defined.
Now, if an anonymous function expression is assigned to a variable or is part of a property definition inside an object initializer, the identifier of the variable or the name of the property will be the value of the name property of the function object.
const a = function () {};
console.log(a.name); // a
const object = {
b : function () {}
};
console.log(object.b.name); // b
I don't have problems to understand the spec regarding the documented semantic of this behaviour, but I can't really see, why an assignment to a left hand side member expression as in the following example does not affect the name property of the function, which seems to be the case.
const object = {};
object.c = function () {};
console.log(object.c.name); //
As part of the member expression, there is obviously an indentifier which could (and should?) be used as the value of the name property. Even if the property name would be an expression inside brackets, this should be no problem, since using computed property names inside an object initializer does not prevent the name property of the anonymous function to be defined either.
It would be great, if someone could confirm that the observed behaviour is conforming to the spec and anyway, explain in short the specific semantics that apply to this syntax.
This first snippet is described under assignment operators:
e. If IsAnonymousFunctionDefinition(AssignmentExpression) and IsIdentifierRef of LeftHandSideExpression are both true, then
i. Let hasNameProperty be HasOwnProperty(rval, "name").
ii. ReturnIfAbrupt(hasNameProperty).
iii. If hasNameProperty is false, perform SetFunctionName(rval, GetReferencedName(lref)).
When you assign to a MemberExpression, as in your last snippet, IsIdentifierRef(LeftHandSideExpression) is false and no conversion takes place.
If you search the standard for IsAnonymousFunctionDefinition you'll find a couple of other cases where this logic is used (object initializers, destructuring assignments).
IsIdentifierRef is defined twice (here and here), and both definitions boil down to "if an expression is IdentifierReference return true otherwise return false", where IdentifierReference is an Identifier (the yield stuff is for backward compatibility with non-strict code).
Related
Consider the following code.
const foo = () => {};
console.log(foo.name); // prints foo
const bar = foo;
console.log(bar.name); // prints foo again
Please point out what is wrong with my reasoning about the statement const foo = () => {};. The expression () => {} evaluates to an anonymous function object and the statement binds the name foo to that object. Surely the value of the expression () => {} does not know it has name foo, but somehow it knows after foo is bound to it. But how did that happen? I assume that = does not alter the right-hand side and lines 3 and 4 behave as I expected.
According to the specification, when a variable declaration is evaluated and the initializer is an anonymous function definition, then that definition is evaluated in a special way, passing along the name of the variable to be used as function name:
LexicalBinding : BindingIdentifier Initializer
1. Let bindingId be StringValue of BindingIdentifier.
2. Let lhs be ! ResolveBinding(bindingId).
3. If IsAnonymousFunctionDefinition(Initializer) is true, then
a. Let value be ? NamedEvaluation of Initializer with argument bindingId.
...
Something similar happens when evaluating an assignment expression.
Line 1 of your code technically isn't an anonymous function, it's a "implicitly named" function expression (or as the mdn also calls it, an "unnamed function").
From the mdn:
The variable to which the function expression is assigned will have a name property. The name doesn't change if it's assigned to a different variable. If function name is omitted, it will be the variable name (implicit name). If function name is present, it will be the function name (explicit name). This also applies to arrow functions (arrows don't have a name so you can only give the variable an implicit name).
See also: Function.name#function_expression
Consider this code example:
let test = {
test: function(){}
}
test.print = function(){}
console.log(test.print.name) // Outputs ""
console.log(test.test.name) // Outputs "test"
console.log(test) // Outputs { test: function(){}, print: function(){} }
Why is there a difference between the name properties of these two functions?
The technical explanation: NamedEvaluation
The specification tells you:
({ test: function(){} })
13.2.5.5 Runtime Semantics: PropertyDefinitionEvaluation
PropertyDefinition : PropertyName : AssignmentExpression
Let propKey be the result of evaluating PropertyName.
[…]
If IsAnonymousFunctionDefinition(AssignmentExpression) is true, then
Let propValue be ? NamedEvaluation of AssignmentExpression with argument propKey.
Else,
Let exprValueRef be the result of evaluating AssignmentExpression.
Let propValue be ? GetValue(exprValueRef).
[…]
In step 3, IsAnonymousFunctionDefinition of the expression function(){} returns true, because it is a function definition, and it lacks a BindingIdentifier.
Therefore, a NamedEvaluation is performed: the function is created with "test" as the value of the name property.
test.print = function(){};
13.15.2 Runtime Semantics: Evaluation
AssignmentExpression : LeftHandSideExpression = AssignmentExpression
Let lref be the result of evaluating LeftHandSideExpression.
[…]
If IsAnonymousFunctionDefinition(AssignmentExpression) and IsIdentifierRef of LeftHandSideExpression are both true, then
Let rval be NamedEvaluation of AssignmentExpression with argument lref.[[ReferencedName]].
Else,
Let rref be the result of evaluating AssignmentExpression.
Let rval be ? GetValue(rref).
Perform ? PutValue(lref, rval).
[…]
In step 3,
IsAnonymousFunctionDefinition of the expression function(){} returns true, just like it does in the other snippet,
but IsIdentifierRef of the expression test.print returns false: it’s a MemberExpression, not an Identifier.
Therefore, step 4, the “Else” case, is taken and no NamedEvaluation is performed.
The rationale: security
The only difference is the additional IsIdentifierRef step, which is also the key to finding the rationale (on esdiscuss.org):
2015-07-25 14:22:59 UTC a.d.bergi at web.de writes:
[If] a function (or class) definition is assigned to a property of an object, [the function automatically being given the name of the target identifier] doesn’t happen:
var o = {};
o.someProperty = function() { … };
o.otherProperty = class { … };
I don’t see any reason for not doing this. [It] just seems to be advantageous and make the language more consistent. I’m quite sure it wouldn’t break any compatibility.
This is one of the replies:
2015-07-26 19:48:15 UTC Allen Wirfs-Brock writes (emphasis mine):
[T]he possibility that the property key is a symbol is a primary reason that this expression form does not set the name property.
There may also be security concerns. The name property potentially leaks via the function object the name of the variable it is initially assigned to. But there isn’t much someone could do with a local variable name, outside of the originating function. But a leaked property name potentially carries a greater capability.
Allen goes on, 2015-07-26 20:33:07 UTC:
TC39 reached consensus on automatically assigning the name property for expression forms like:
Identifier = FunctionExpression
and so it is part of ES2015. We did not have consensus on doing the same for:
MemberExpression . IdentifierName = FunctionExpression
or
MemberExpression [ IdentifierName ] = FunctionExpression
so it is not part of ES2015. There were various objections that would have to be overcome before we could adopt that.
Another comment, 2016-12-13 09:03:40 UTC by T.J. Crowder states that it’s unclear what those “various objections” were.
They link to the original proposal (archived from wiki.ecmascript.org, 2016-09-15, last available version) which does list your expected behavior in an example:
obj.property = function(){}; // "property"
But keep in mind that this was still an unfinished proposal back then.
When the proposal got accepted into the specification, it seems that it underwent some changes, but the proposal article didn’t get changed to reflect these changes.
This thread mentions certain TC39 meeting notes where this is discussed, but no link is provided.
Allen is claiming that this kind of information leak is the reason why TC39 couldn’t reach a consensus on allowing this behavior.
They mention 2017-01-28 15:46:54 UTC:
[F]or cache[getUserSecret(user)] = function(){}; it would leak the secret user info as the value of name.
Even though, as T.J. Crowder mentions 2017-01-28 16:11:26 UTC, the same thing is possible with:
cache = {
[getUserSecret(user)]: function() {}
};
There isn’t an easy workaround with anonymous functions
“Is there a way to get the name of a function declared [like test.print = function(){};]?”
Not really.
You could iterate through the object’s entries and find candidates for key names where the value matches the function.
But if the function is referenced by another key name, you may get multiple results.
Also, this gets more complicated if the key is a symbol.
const test = {
test: function(){}
}
test.print = function(){};
test.someOtherAliasForPrint = test.print;
test[Symbol("someSymbolForPrint")] = test.print;
console.log(
"Possible function names:",
Object.entries(Object.getOwnPropertyDescriptors(test))
.filter(([key, {
value
}]) => value === test.print)
.map(([key]) => key)
);
console.log(
"Possible function symbols:",
Object.getOwnPropertySymbols(test)
.filter((symbol) => test[symbol] === test.print)
.map((symbol) => String(symbol)) // Or `.map(({description}) => description)`
);
Your best bet is to define the method like this:
test.print = function print(){};
However, consider using method syntax instead:
({
test(){},
print(){}
})
How you are defining print is using anonymous functions and accessing their name property will return ''. Read more here
If you still want to make the code work you can change your code like this :
test.print = function print() {
}
This question already has an answer here:
Definition of name property in assignment expression
(1 answer)
Closed 4 years ago.
I'm not having a problem, not trying to fix anything. I'm just curious why Javascript works this way. I've poked around with google, but "js function no name" gets a lot of hits about how to define and use anonymous functions (not what I'm looking for). And there's hardly anything out there about declaring functions with the syntax that is causing my confusion--I don't even know what that syntax is called.
The issue: I'm trying to figure out why declaration syntax has any effect on the function name when the function is inside an object. If I declare an object with a function inside like this:
var objectOne = { apple: function() {} }
The apple() function gets a name. That is, console.log(objectOne.apple.name) displays "apple".
But if I declare an object with a function inside like this:
var objectTwo = {}
objectTwo.banana = function() {}
Then banana() doesn't get a name. That is, console.log(objectTwo.banana.name) displays nothing. Same for the following, and similar permutations.
var objectThree = { catapult: null }
objectThree.catapult = function() {}
I can name the functions explicitly, of course, but again, I'm not trying to fix anything, I just wonder why the two syntaxes produce different results. I've always thought of them as functionally interchangeable.
I notice that both of these forms, not inside an object, get names automatically:
function one() {}
var two = function() {}
Why is the object.property = function(){} form treated differently from the other forms?
From the ECMA Specification on how the name of a function gets set:
SetFunctionName (F, name, prefix)
The abstract operation SetFunctionName requires a Function argument F, a String or Symbol
argument name and optionally a String argument prefix. This operation
adds a name property to F by performing the following steps:
Assert: F is an extensible object that does not have a name own property.
Assert: Type(name) is either Symbol or String.
Assert: If prefix was passed then Type (prefix) is String.
If Type(name) is Symbol, then
Let description be name’s [[Description]] value.
If description is undefined, let name be the empty String.
Else, let name be the concatenation of "[", description, and "]".
If prefix was passed, then
Let name be the concatenation of prefix, code unit 0x0020 (SPACE), and name.
Return DefinePropertyOrThrow(F, "name", PropertyDescriptor{[[Value]]: name, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true}).
Assert: the result is never an abrupt completion.
My guess is that the second example fails the first test for setting the function name (Assert: F is an extensible object) and so no name gets set.
While working on another problem, I created this fiddle:
http://jsfiddle.net/tr2by/
function foo() {
// console.log(_.isBoolean(this));
console.log(this === true);
}
foo.call(true); // object:[object Boolean]
foo.apply(true); // object:[object Boolean]
Is this an example of auto-boxing?
Going from a value type to a reference type.
Here is a wikipedia def.
First of all I assume you are talking about automatic conversion of primitive values to objects. This happens in two cases in JavaScript:
When you pass a primitive value as the this value to .call or .apply (not in strict mode though).
When you are trying to access a "property" of a primitive value, e.g. "foo bar".split().
In the first case the conversion is permanent, i.e. this will indeed reference an object, in the second the conversion only takes place internally for the duration of the evaluation
If you are not interested in the details of the conversion, you can ignore the rest of the answer.
1. Primitive value as this
When a function is exectued and its this value is not an object, it is converted to one, at least in non-strict mode. This is described in §10.4.3 Entering Function Code [spec] in the ECMAScript 5.1 documentation:
The following steps are performed when control enters the execution context for function code contained in function object F, a caller provided thisArg, and a caller provided argumentsList:
If the function code is strict code, set the ThisBinding to thisArg.
Else if thisArg is null or undefined, set the ThisBinding to the global object.
Else if Type(thisArg) is not Object, set the ThisBinding to ToObject(thisArg).
[...]
As you can see in step three the value is converted to an object by calling ToObject [spec].
2. Property access
Something similar happens when you are trying to access properties (§11.2.1 Property Accessors [spec]). The quoted part here explains how the expression foo[bar] is evaluated, i.e. how property access with the bracket notation is evaluated. The part we are interested in applies to dot notation as well.
The production MemberExpression : MemberExpression [ Expression ] is evaluated as follows:
Let baseReference be the result of evaluating MemberExpression.
Let baseValue be GetValue(baseReference).
[...]
8. Return a value of type Reference whose base value is baseValue and whose referenced name is propertyNameString, and whose strict mode flag is strict.
The important step is the last one: No matter to what MemberExpression evaluates, it is converted to a value of type Reference [spec]. This is a datatype only used in the specification and contains additional information about how the actual value should be retrieved from the reference (not to be confused with object references in actual JavaScript code!).
To get the "real" value/result from such a reference, the internal function GetValue(V) (§8.7.1) [spec] is called (just like in step 2 in the above algorithm), where it says:
The following [[Get]] internal method is used by GetValue when V is a property reference with a primitive base value. It is called using base as its this value and with property P as its argument. The following steps are taken:
Let O be ToObject(base).
[...]
Example:
Assume we have the expression
var foo = "BAR".toLowerCase();
This is an assignment expression which is evaluated as follows:
The production AssignmentExpression : LeftHandSideExpression = AssignmentExpression is evaluated as follows:
Let lref be the result of evaluating LeftHandSideExpression.
Let rref be the result of evaluating AssignmentExpression.
Let rval be GetValue(rref).
[...]
Step 1: The left hand side is evaluated, which is the identifier foo. How exactly identifiers are resolved is not important for this.
Step 2: The right hand side is evaluated, i.e. "BAR".toLowerCase(). The internal result of that evaluation will be a reference value, similar to:
REFERENCE = {
base: "BAR",
propertyNameString: "toLowerCase",
strict: false
}
and stored in rref.
Step 3: GetValue(rref) is called. The base of the reference is the value "BAR". Since this is a primitive value, ToObject will be called to convert it to a temporary String object. Furthermore, the reference is actually a property access, so GetValue will eventually call the method toLowerCase on the String object and return the method's result.
Javascript boxes the this argument provided to call and apply in non-strict mode. From MDN:
if the method is a function in non-strict mode code, null and undefined will be replaced with the global object, and primitive values will be boxed.
The other answers provide detailed information about when autoboxing occurs, but here's a couple more things to remember:
Autoboxing does not occur when using the in operator, which throws a TypeError if the value received is not an object. A simple solution is to manually box the object with Object(value).
Some form of autoboxing occurs when iterating using for...of or the spread syntax [...value] which allows strings to be iterated.
I am a newbie to js although years of experience with Java
I suppose when I declare a function,it is essentially a special type of object,and got some builtin fields that are accessible directly such as "arguments" and "length"
I notice I can access something like "arguments" inside the scope of a function
i.e.
function add(a,b) {
return arguments[0]+arguments[1]
}
Also I can access something like "length" outside the scope
//2
alert(add.length)
the above snippet should be the right way to use
however
function sum(a,b) {
// error
return length
}
// null
alert(sum.arguments)
I suppose arguments and length are not of the same basic, is it right idea?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
After some research,I got the root cause of the confusion.
The issue revolves around property VS variable in JavaScript
Property belong to Object while Variable belong to Context.
The two ideas can be interchangeable sometimes:
The global context happen to be window
<script>
//property
window.foo="a"
//variable
var bar="b"
//a
alert(foo)
//b
alert(bar)
</script>
In most scenario,say,function context,they are completely different ideas,partly because you can never access the function object in your code.Thus,contrary to a global setting ,assign a property is impossible! What is possible is just declare a variable in scope
In my question
"arguments" is a variable
while "length" is a property
I failed to distinguish the two
For more information,please refer to
this post
Functions are objects in JavaScript. Proper, real objects.
length
The length property of a function is the number of declared arguments it has (this is also called the "arity" of the function). The arity (length) of add is two because you declared two formal arguments for it: a and b.
arguments
The arguments pseudo-array is not part of the function object. It's an object created when the function is called, and only in scope within that function call's context. It contains all of the arguments that the function was actually called with, which can be different from the number of arguments it declares. Each separate call to the function gets its own separate arguments object.
In JavaScript's "loose mode" (the only mode it had before 2009's ECMAScript 5th edition specification), there's a live connection between the arguments pseudo-array and the declared arguments:
// In loose mode only
function foo(a) {
console.log("a = " + a);
console.log("arguments[0] = " + arguments[0]);
a = 42;
console.log("a = " + a);
console.log("arguments[0] = " + arguments[0]);
}
foo(67);
In loose mode, that outputs:
a = 67
arguments[0] = 67
a = 42
arguments[0] = 42
In "strict" mode (which is the preferred mode to use), that link doesn't exist (we'd see arguments[0] = 67 at the end), which is useful for JavaScript engines for optimization purposes.
Yes, length and arguments are different concepts.
Contrary to Java, where fields (attributes) of your object, methods, and local variables are all accessible in the same scope (just via an identifier), JavaScript does distinguish heavily between scope variables (with closures, lexically referenced) and object properties (possibly inherited) - not only in concept, but also in syntax. Properties are always1 accessed via . or [] notation.
The .length of Function instances is such a property. You will always have to write add.length or sum.length (or …["length"]). If you just use length inside of the function, it doesn't automatically refer to the property - it's a variable reference, in this case to an undeclared one.
The arguments object in contrast works like a variable. It cannot be accessed as a property of the function2. Admittedly, it's a bit special, because you cannot see the declaration of the variable - it is created implicitly in every function scope. It works a bit as if there was a const arguments = {0: a, 1: b}; statement in the first line of the function.
1: Not exactly true, there's a with statement that explicitly lifts object properties into a scope. It's despised due to its ambiguity and deprecated in strict mode though.
2: There also is a deprecated .arguments property on functions, accessing it on strict mode functions throws. If not accessed during the invocation, it's null.