7

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?

SaganRitual
  • 3,143
  • 2
  • 24
  • 40

1 Answers1

0

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.

Scott Marcus
  • 64,069
  • 6
  • 49
  • 71
  • I've done some experimentation, and I'm reasonably convinced that this is the problem. Scott, if you'll allow me, I'll edit your question and add the details I've found during my experiments. I've marked your answer as accepted. Thanks much! – SaganRitual Mar 19 '18 at 08:33
  • 1
    @GreatBigBore The best thing to do is to edit your question and add an **UPDATE** section at the bottom with your information. – Scott Marcus Mar 19 '18 at 12:39
  • Will do, thanks. And thanks for pointing me in the right direction. – SaganRitual Mar 19 '18 at 12:40
  • @RandyCasburn I don't see how that spec. reference has anything to do with this. We are not talking about a method at all, nor is the `name` assigned after the function has been initialized (unless being overwritten). – Scott Marcus Mar 19 '18 at 12:43
  • 1
    This answer is plain wrong. Functions are extensible objects, there's no problem with that. It's just that `SetFunctionName` is not called at all when a property is assigned. – Bergi Mar 19 '18 at 14:28
  • @ScottMarcus - please refer to the [definition of a method in the spec](https://tc39.github.io/ecma262/#sec-method). Then, consider there are differences between the three different types of functions (normal, method, arrow) in JS. Since we now know that a _Method_ is being created (**not** a function object) we can proceed. Next, please refer to [Define Method](https://tc39.github.io/ecma262/#sec-runtime-semantics-definemethod). Note the conditional that determines whether the method will be created as a _Normal_ or _Method_ type. It clearly indicates....(cont'). – Randy Casburn Mar 19 '18 at 14:35
  • @ScottMarcus - if the method being created has a prototype then it is _Normal_ and if not, it is a _Method_ type. When an method is placed onto an object **at initialization**, the prototype of that function is the object being created. So, since it has a prototype it is created as a _Normal_ type. Since it is created as a _Normal_ type, it is given a name. If the _Method_ is added afterward as a property value, it has no prototype (it is undefined). That means it is a _Method_ type and it has **no name**. cont.... – Randy Casburn Mar 19 '18 at 14:39
  • @ScottMarcus - This is easily demonstrated. All one has to do is create a function, provide a prototype, and then attach that function to the object - it will have a name. As opposed to attaching a function without a prototype (as the question does), then it has no name. – Randy Casburn Mar 19 '18 at 14:39
  • @Bergi It has to be extensible (it is) and **not** have a name own. And that's the difference. When I assign to a property, it gets a name own first, so it doesn't get a `.name`. – SaganRitual Mar 19 '18 at 15:58
  • @Bergi Also, I hate that I posted a duplicate! How did you find that other one? – SaganRitual Mar 19 '18 at 15:59
  • @RandyCasburn Now I'm troubled. Scott's answer seems plausible, given my experiments, but your comments seem just as plausible. I don't know how to distinguish between the two. Thoughts? – SaganRitual Mar 19 '18 at 16:02
  • @GreatBigBore Well if that was the case, the answer was still wrong because it says that the extensibility check fails not the hasownproperty one. But no: those assertions never fail, the algorithms in the spec guarantee that - `setFunctionName` is never called on a non-extensible function or one with a name. And no, the function expression does not "get a name first", it does not have a `.name` at all (at least by the spec - Chrome doesn't seem to follow that and set the empty string). – Bergi Mar 19 '18 at 16:13
  • 1
    @GreatBigBore I found the duplicate because I had previously answered similar questions, and then just searched for https://stackoverflow.com/search?q=SetFunctionName%20[js]. – Bergi Mar 19 '18 at 16:14
  • @Bergi Ok, "get a name first" might be the wrong terminology. But when I do `var objectTwo = {}; objectTwo.banana = function() {}; console.log(Object.getOwnPropertyNames(objectTwo));` I see that it has an own name. That's what I meant. And the fact that it returns blank when I call `.name` seems to support the second half of Scott's assert. But I'm not arguing--I really just want to understand. – SaganRitual Mar 19 '18 at 16:15
  • 1
    @GreatBigBore That's what I mean where engines do not follow the spec - `objectTwo.banana` should not have a `.name` property at all. – Bergi Mar 19 '18 at 16:19
  • The question that must be answered it "when does the function object HasOwnProperty _name_ get set" or "under what circumstances does a name property get set on a function object". I'm attempting to show that in a fiddle - I'll post a link when I can. – Randy Casburn Mar 19 '18 at 16:49
  • 1
    @GreatBigBore - Take a look at this (long) explanation in the form of a fiddle: https://jsbin.com/ciyamed/edit?js,console – Randy Casburn Mar 19 '18 at 22:26
  • @RandyCasburn Wow, that's going to take me a while to get through, but it looks like exactly what I was looking for. Thanks much! – SaganRitual Mar 19 '18 at 22:28