Does ES2015 exported class create a closure? - javascript

As it's currently compiled via Babel + Webpack, module's exported class will create a closure: variables created inside the module will be shared between class instances.
bar.js:
let foo;
export default class Bar {
set foo(value) {
foo = value;
}
get foo() {
return foo;
}
}
app.js:
import Bar from './bar.js';
var barOne = new Bar();
var barTwo = new Bar();
barOne.foo = 'quux';
console.assert(barTwo.foo === 'quux');
I wonder if this behavour correct according to the spec.

I wonder if this behavour correct according to the spec.
Yes. JavaScript has lexical scope. That doesn't change with classes.
Keep in mind that classes are more or less just syntactic sugar for constructor function + prototype. Would you have had the same question if you wrote
let foo;
function Bar(){};
Bar.prototype = {
set foo(value) {
foo = value;
}
get foo() {
return foo;
}
};
module.exports = Bar;
instead?

Related

Is there a way to have a constructor within an already existing constructor?

I have the following function that acts a base constructor:
function Foo() = {}
...
exports Foo
It's an empty constructor, and is used by other functions which I don't want to change. Also, Foo is only being exported from the file.
Now I need to create a different constructor perhaps within Foo.
The following is what I'd have with enum as a standalone constructor. But how do I make it a part of Foo?
function enum(data) {
this.data = data
}
enum.prototype.getVal() { return this.data; }
var obj = new enum(5);
obj.getVal();
Taking a completely wild guess from your commnets.
exporting from the file is of no real concern. If you're just using Foo as a namespace to "hang" functions off of you can do this:
Enum.js
// all constructors should be capitalized
function Enum() { }
Enum.prototype.whatever ...
exports Enum
Foo.js
const Enum = require('./Enum');
// it's unclear why `Foo` is even a function to be honest
function Foo() { }
Foo.Enum = Enum;
exports Foo
someOtherFile.js
const Foo = require('./Foo');
const myEnumInstance = new Foo.Enum();

How to change variable value from another module in javascript

I am using Webpack as bundler and I can't find answer to my question anywhere.
For example, I have file index.js which contains:
import { Func } from './func.js'
export let foo = 'bar';
Func();
console.log(foo);
I have another file, let's say func.js which contains:
import { foo } from './index.js';
export const Func = () => { foo = 'baz' }
But it isn't possible to change value of variable from another module.
How do you handle such situations if I don't want to move function 'Func' to index.js?
I also know it is possible to mutate objects but using an object is not always an option.
foo is a primitive. You can't change it's the value assigned to it in the other module when it's a primitive, you can only change the value foo has been assigned in scope of the current module.
You could always do it with functions however:
let foo = 'initial value';
export const getFoo = () => foo;
export const setFoo = (val) => (foo = val);
import { getFoo, setFoo } from 'someModule';
getFoo(); // initial value;
// call this when necessary
setFoo('jim');
import { getFoo } from 'someModule';
// when called after `setFoo` has been called in the other module
getFoo(); // 'jim'

Typescript object literal "this" keyword

What's the expected behavior when using this inside a function in an object literal?
For example, let's say I have a type foo that only has a function named bar and no other property on it. But in the fooObj.bar method, I'm able to access this.baz (where baz is not a property on type foo) I see no error. Shouldn't typescript error out, as fooObj does not have baz on it?
type foo = {
bar(): void;
}
var fooObj: foo = {
bar: () => {
// TS does not error out when I access this.baz
console.log(this.baz);
}
}
Setting the "noImplicitThis": true compiler option is how you would enable this functionality now. This pull request enabled typed this in object literals. Aleksey L originally suggested this compiler option in a comment on the question, but at the time it didn't function that way.
You’re using an arrow function, which has lexical this.
The shorthand for a non-arrow function property in an object literal is even shorter, though:
var fooObj: foo = {
bar() {
console.log(this.baz);
}
}
This answer was true at the time of the question. This have since changed with new versions of typescript and target javascript versions.
You are asking typescript to infer that this is fooObj.
Typescript binds this by creating a local variable _this, that is bound to the this-context where the fat-arrow is declared. And in your case, this is the global scope, which is any. This is what it gets compiled into:
var _this = this;
var fooObj = {
bar: function () {
// TS does not error out when I access this.baz
console.log(_this.baz);
}
};
This is how it looks like within a class:
class Bar
{
private var = 23;
public makeSound = () => console.log(this.var)
}
// Compiles into:
var Bar = (function () {
function Bar() {
var _this = this;
this.var = 23;
this.makeSound = function () { return console.log(_this.var); };
}
return Bar;
}());

Define a const in class constructor (ES6) [duplicate]

This question already has answers here:
Declaring static constants in ES6 classes?
(19 answers)
Closed 7 years ago.
Is there a way I can define a const in the constructor of a class?
I tried this:
class Foo {
constructor () {
const bar = 42;
}
getBar = () => {
return this.bar;
}
}
But
var a = new Foo();
console.log ( a.getBar() );
returns undefined.
You use static read-only properties to declare constant values that are scoped to a class.
class Foo {
static get BAR() {
return 42;
}
}
console.log(Foo.BAR); // print 42.
Foo.BAR = 43; // triggers an error
Simply defining a constant in the constructor won't attach it to the instance, you have to set it using this. I'm guessing you want immutability, so you can use getters:
class Foo {
constructor () {
this._bar = 42;
}
get bar() {
return this._bar;
}
}
Then you can use it like you normally would:
const foo = new Foo();
console.log(foo.bar) // 42
foo.bar = 15;
console.log(foo.bar) // still 42
This will not throw an error when trying to change bar. You could raise an error in a setter if you want:
class Foo {
constructor () {
this._bar = 42;
}
get bar() {
return this._bar;
}
set bar(value) {
throw new Error('bar is immutable.');
}
}
The problem is with "bar" scoping - it scoped to constructor:
'use strict';
class Foo {
constructor () {
const bar = 42;
this.bar = bar; // scoping to the class
}
getBar () {
return this.bar;
}
}
var a = new Foo();
console.log ( a.getBar() );

JavaScript design patterns: Injecting a dependency that is not yet created

I have a CommonJS module:
// main-module
module.exports = function () {
var foo,
someModule = require('other-module')(foo);
// A value is given to foo after other-module has been initialised
foo = "bar";
}
As you can see, this requires other-module:
// other-module.js
module.exports = function (foo) {
function example() {
console.log(foo);
// > "bar"
}
}
I would like the example function inside of other-module to be aware of the foo variable inside of main-module, even though is it established after the module is required.
When other-module runs, foo will not be undefined. However, the point is that by time my example function runs, foo will have been given a value of bar.
The pattern above obviously does not work. What design pattern do I need to implement?
I'm not super-familiar with CommonJS, so this might not be the idiomatic way to do it, but using a function instead of a variable should work:
// main-module
module.exports = function () {
var foo,
someModule = require('other-module')(function() { return foo; });
foo = "bar";
}
// other-module.js
module.exports = function (fooFn) {
function example() {
console.log(fooFn());
}
}
The foo value (a string) will be passed by value, so it's undefined inside other-module. You could use an options object that is passed by reference:
var options = {},
someModule = require('other-module')(options);
options.foo = "bar";

Categories