If new A() and new B() are both idempotent, wouldn't you always expect new A(); new B(); to succeed if new B(); new A(); succeeds?
The following produces a confusing error (confusing because I'm not trying to reference host, but supply it as a parameter to B, which it has as a constructor parameter):
#app.js
const DEFAULT_HOST = "localhost";
class B {
constructor(host = null) { console.log(`HOST: ${host}`); }
}
class A {
constructor() { new B(host = DEFAULT_HOST) }
}
console.log(new A());
console.log(new B(host=123));
% npm app.js
constructor() { new B(host = DEFAULT_HOST) }
^
ReferenceError: host is not defined
at new A (/home/sir/app.js:8:32)
at Object.<anonymous> (/home/sir/app.js:11:13)
at Module._compile (internal/modules/cjs/loader.js:1158:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1178:10)
at Module.load (internal/modules/cjs/loader.js:1002:32)
at Function.Module._load (internal/modules/cjs/loader.js:901:14)
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:74:12)
at internal/main/run_main_module.js:18:47
But switch the last two, lines, and there is no exception. Huh?!
console.log(new B(host=123));
console.log(new A());
The following works fine, so I have to assume it's some magic to do with classes:
const DEFAULT_HOST = "localhost";
function b(host = null) { console.log(`HOST: ${host}`); }
function a() { b(host = DEFAULT_HOST) }
console.log(a());
console.log(b(host=123));
You have your notation mixed up, for one. Parameters to JS functions do not have names - to pass a value, just pass the value, regardless of the name the function's internals uses for it. This:
console.log(new B(host=123));
should be
console.log(new B(123));
for B's constructor to see the argument 123 and put it into the host identifier.
As for why the error is thrown - you cannot retrieve a value from an identifiers that do not exist, but (in sloppy mode) you can assign a value to an identifier that doesn't exist, resulting in the property being put onto the global object. For example:
// OK
bar = '123';
// forbidden
console.log(foo);
(Always declare variables before using them, and consider always using strict mode.)
This throws an error in strict mode:
new B(host = DEFAULT_HOST)
because it's equivalent to: (first resolving the expression in the argument list):
const result = (host = DEFAULT_HOST);
new B(result);
And constructors run in strict mode, which forbids assigning to nonexistent identifiers:
class B {
constructor() {
function foo() {
console.log(this);
}
foo();
}
}
new B();
But the code outside of a class may not be strict mode - such as in your code. In sloppy mode, assigning to a nonexistent identifier is permitted (just very strange to do).
// IN SLOPPY MODE, so the following is permitted (but strange):
bar = 'bar';
class B {
constructor() {
// IN STRICT MODE, so the following is forbidden:
foo = 'foo';
}
}
new B();
Your code with new B(host = DEFAULT_HOST) fails in strict mode (inside the constructor) because the host identifier doesn't exist, and doesn't throw in sloppy mode (outside the constructor).
When you call a function (a class method, a class constructor, an anonymous function) the javascript is evaluated before the value is passed. JavaScript evaluates the assignment then passes the result of the assignment to the function.
'use strict'
const DEFAULT_HOST = 'localhost';
let foo;
class B {
constructor(host) {
console.log(`B created with "${host}"`);
}
}
new B(foo = DEFAULT_HOST);
In this case js tries to assign DEFAULT_HOST to a variable named host. But what is host? host is undefined. So it throws an error.
'use strict';
const DEFAULT_HOST = 'localhost';
class B {
constructor(foo) {
console.log(`B created with "${foo}"`);
}
}
// Uncaught ReferenceError: foo is not defined
new B(foo = DEFAULT_HOST);
The default parameter value is assigned to a parameter if it's not passed to the function.
'use strict';
const DEFAULT_HOST = "localhost";
class Foo {
constructor(host = DEFAULT_HOST) {
// ...start server
console.log(`Server running on "${host}"`)
}
}
var foo = new Foo(); // Server running on "localhost"
var foo = new Foo('127.0.0.1'); // Server running on "127.0.0.1"
function saveHost(host = DEFAULT_HOST) {
// ...do save stuff
console.log(`${host} saved to disk`)
}
saveHost() // localhost saved to disk
saveHost('127.0.0.1') // 127.0.0.1 saved to disk
The closest thing JavaScript has to named parameters is destructuring. Where we pass in an object and it is destructured...
function foo({x: someX, y: someY}) {
console.log(`SomeX is ${someX}. SomeY is ${someY}`);
}
foo({x: 5, y: 6})
See also: Parameter names with ES6?
If you want to fix the code in your comment. Try the following.
const DEFAULT_HOST = "localhost";
class B {
constructor(host = null) {
console.log(`HOST: ${host}`);
}
}
class A {
constructor() {
new B(DEFAULT_HOST);
}
}
new A(); // HOST: localhost
new B(123); // HOST: 123
new B(); // HOST: null