2

When I try to specify the parameter names when calling a function from inside a JavaScript class constructor, I get a ReferenceError.

I will show the problem based on a simplified example:

Suppose I have an object and methods like so:

class Person {

    constructor(name, age) {
        this.name = name,
        this.age = age
    }

    addYear() {
        this.age++;
    }

    changeName(newName) {
        this.name = newName;
    }

    changeAge(newAge) {
        this.age = newAge;
    }

    changeAgeAndName(newName, newAge) {
        this.changeName(newName=newName);
        this.changeAge(newAge=newAge);
    }

}

All works fine if I instanciate it, and if mingle with it a bit this is the code and output:

let p1 = new Person('George', 20);
console.log(p1);

p1.changeName(newName='Robert');
console.log(p1);

p1.changeAgeAndName(newName='Joe', newAge=23);
console.log(p1);
Person { name: 'George', age: 20 }
Person { name: 'Robert', age: 20 }
Person { name: 'Joe', age: 23 }

Now, let's say I want to use changeAgeAndName inside the constructor like so (specifying the parameter names for the variables):

    constructor(name, age) {
        this.changeAgeAndName(newName=name, newAge=age);
    }

I get an error:

this.changeAgeAndName(newName=name, newAge=age);
ReferenceError: newName is not defined

But if I don't specify the parameter names, all is fine:

    constructor(name, age) {
        this.changeAgeAndName(name, age);
    }

Eventual Output:

Person2 { name: 'Mike', age: 40 }

If I specify the parameter names outside the constructor, all is fine as well. I like to be as explicit as possible when coding, so I was wondering what I may be doing wrong. Also, I was curious about perhaps yet another example of peculiar JS behavior. Thank you!

Omri
  • 483
  • 4
  • 12
  • 2
    JavaScript doesn't have named arguments (like some other languages such as Python). Related: [Is there a way to provide named parameters in a function call in JavaScript?](https://stackoverflow.com/q/11796093) – Nick Parsons Nov 05 '22 at 07:49
  • 1
    @Nick Parsons Thanks, I guess that's acutally the bigger issue. – Omri Nov 05 '22 at 08:25

3 Answers3

4

When you called:

p1.changeAgeAndName(newName='Joe', newAge=23);

you thought you were specifying that the newName argument should be 'Joe'. What you are actually doing is implicitly declaring a new variable, setting the result to 'Joe', and passing the string 'Joe' as the first argument to the function. That function does not care about anything other than argument position.

If you want to name your variables for clarity, do:

changeAgeAndName({newName, newAge}) {
   //...
}

and call it with:

this.changeAgeAndName({newName: 'Joe', newAge: '23'});

As noted by @NickParsons, all parts of a class's body are in 'strict mode'. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode#strict_mode_for_classes

Therefore, your ReferenceError is caused by your implicit variable declaration (doing newName='Joe') without first declaring the variable with let newName. Implicit variable declaration is not allowed in strict mode.

Andrew Parks
  • 6,358
  • 2
  • 12
  • 27
  • 1
    Indeed. He was treating javascript as though it was python. I believe in python what he did works. – Tal Kohavy Nov 05 '22 at 07:44
  • 1
    Nice answer. One other thing that might add some clarity is that in standard scripts, declaring a new variable such as `foo = 123;` without var/let/const works fine, but since this code is written within a _class_, OPs code will automatically run in strict-mode, where assigning to an undeclared variable will throw a ReferenceError as shown. That's why the code doesn't throw when using `this.changeName(newName=newName);` within `changeAgeAndName` as `newName` is already declared in the parameter list. – Nick Parsons Nov 05 '22 at 07:59
  • 1
    @NickParsons excellent point about strict mode. I'll paste your comment into the answer – Andrew Parks Nov 05 '22 at 08:02
  • @Andrew Parks Thanks for the answer and good suggestion. I was also wondering, and I hope I didn't miss it in the link you sent, if someone knows *why* this behavior exists by default for class constructors. Out of curiousity. – Omri Nov 05 '22 at 08:21
  • @Omri it applies to all functions, whether class constructors or not, if you are in 'strict mode'. Since I'm guessing your entire javascript is not declared to be in strict mode, you only see errors when you do implicit variable declaration inside classes and other places as listed here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode#strict_mode_for_classes – Andrew Parks Nov 05 '22 at 08:22
1

So it turns out I was actually fooling myself and the problem goes beyond the constructor and strict mode. The problem is that JS doesn't at all have named arguments (I should have known that but I didn't mess with it until just recently). The arguments will still be read in the default order as declared when creating the function. I think it would be better for JS to reject any sort of attempt to name variables when calling a function. That would provide clarity.

If you want to be explicit in naming arguments, the solution is "parameter destructing" as described here: Is there a way to provide named parameters in a function call in JavaScript?

Thanks to @Nick Parsons and @Andrew Parks

Omri
  • 483
  • 4
  • 12
-1

Do:

p1.changeAgeAndName(newName,newAge)
chrslg
  • 9,023
  • 5
  • 17
  • 31
Dat co
  • 1
  • 1
  • 1
    OP already knows that they can use this to get their code to work: _"But if I don't specify the parameter names, all is fine"_. They're mainly asking why they get the _ ReferenceError_ when other similar code (written within `changeAgeAndName`) seems to work fine. – Nick Parsons Nov 05 '22 at 07:55