How can I share an object between my modules? - javascript

So I have a helper module H, which I require from my main script M:
M.js:
var o = {foo: 'bar'};
require('H.js').doFunc();
H.js:
module.exports = {doFunc: function(){ /* how can I set X to o.foo in M's scope? */ return X; }};
Thus one can expose things in this direction. What can I do to share things back the other way?

You can't quite do it the way you intend because node's architecture creates separate, individual global objects per module. However, you can get something similar using the mechanics of how this works in javascript.
In plain js, given:
function doFunc () {
return this.foo;
}
var o = {foo:'bar'};
You can do:
o.dofunc = doFunc;
o.doFunc(); // returns 'bar'
Alternatively:
doFunc.call(o); // also returns 'bar'
So, applying this technique to modules:
In H.js:
module.exports.doFunc = function () {return this.foo}
In M.js:
var o = {
foo: 'bar',
dofunc : require('H.js').doFunc
};
o.dofunc();

M.js
var o = {foo: 'bar'};
require('H.js')(o);
H.js
module.exports = function (o) {
console.log(o);
}

M:
o = {foo: 'bar'};
require('./H.js')(); // 'bar'
H:
module.exports = o.foo;
By not declaring the variable, it gets stored on the global object which is shared.

Related

Hoisted method in javascript

I would like to learn the proper way to hoisted method in object.
My goal is the put the object definition at the end of the code, and the object usage at the beginning. Let's say:
doing stuff with o
let o = {
bar: 1,
foo: function() {
console.log(this.bar);
},
}
Of course, if I'm writing :
o.foo();
let o = {
bar: 1,
foo: function() {
console.log(this.bar);
},
}
o is undefined because the object is declared after the usage.
I thought about this trick :
o = returnO();
o.foo();
function returnO() {
return o = {
bar: 1,
foo: function() {
console.log(this.bar);
},
}
}
which works but I would like to know if there's not a better way to avoid the statement
o = returnO();
Thanks.
I don't know what's the purpose of what you want to accomplish, maybe you're practicing.
The problem is the post declaration of the variable, the engine at this point of the interpretation of your code o.foo();, the variable o was declared but not yet initialized.
You can use a function declaration (Hoisted function) as follow, and use it as a constructor which is similar to what you want to accomplish:
new A().foo();
function A() {
this.bar = 1;
this.foo = function() {
console.log(this.bar);
};
}

How to read this ES6 code utsing destructuring and initialization?

I ran into some code that looks like the following:
const {
foo = []
} = this.options
Assuming in this case that this.options is an JavaScript Object, how does this work? Does all of this.options get assigned to foo and if this.options is undefined, does foo just get initialized to an empty array? I found this code confusing because this.options is not an Array but is instead an Object of key/val pairs.
Sometimes it helps to just try things out. What you'd observe is that a default value is assigned to foo in case it is missing within the to be assigned object
function one() {
const options = {};
const {
foo = []
} = options;
console.log(foo);
}
function two() {
const options = {foo: 'bar'};
const {
foo = []
} = options;
console.log(foo);
}
function three() {
const options = {};
const {
foo = 'foo',
bar = 'bar',
baz = 'baz'
} = options;
console.log(foo, bar, baz);
}
one();
two();
three();
From MDN (emphesis mine ) :
The destructuring assignment syntax is a JavaScript expression that makes it possible to unpack values from arrays, or properties from objects, into distinct variables.
Not all of this.options get assigned to foo, it's unpacking foo from the Object :
const options = {
foo: ['foo', 'bar'],
bar: 'hello world'
}
const {
foo = []
} = options;
console.log(foo);
And foo = [] is there to be a fallback to have an empty array if this.options does not have a property foo :
const options = {
bar: 'hello world'
}
const {
foo = []
} = options;
console.log(foo);
If this.options is ` undefined, you'll get errors,
options is not defined
const {
foo = []
} = options;
console.log(foo);
Or:
Cannot destructure property foo of 'undefined' or 'null'.
const options = undefined;
const {
foo = []
} = options;
console.log(foo);
If you run it through babel you'll see that if this.options.foo exists then it will be bound to the name 'foo' in that scope and if it doesn't then foo is set to an empty array.
Here is an in-depths article for you to better understand ES6 Destructuring https://hacks.mozilla.org/2015/05/es6-in-depth-destructuring/
Destructuring assignment allows you to assign the properties of an
array or object to variables using syntax that looks similar to array
or object literals. This syntax can be extremely terse, while still
exhibiting more clarity than the traditional property access.
For your sample script the assignment are looking for an object with property "foo" from right side. If it can not find it, it will assign foo with default value as empty array.
If the left side is null or undefined, the operator will throw an error "Uncaught TypeError: Cannot destructure propertyfooof 'undefined' or 'null'."
// A regular JS object.
this.options = {
'a': 'a',
'foo': 'bar',
'z': 'z'
}
console.log('this.options.foo =', this.options.foo);
// this.options.foo === 'bar'
// Get 'foo' out of this.options. Becomes undefined if 'foo' doesn't exist in this.options.
const { foo } = this.options;
console.log('foo =', foo);
// foo === this.options.foo === 'bar';
const { nope } = this.options;
console.log('nope =', nope);
// typeof nope === 'undefined'
// Get 'zzz' if it exists, otherwise fall back to 1234.
const { zzz = 1234 } = this.options;
console.log('zzz =', zzz);
// zzz === 1234;
// zzz was set by its default fallback value.

Weird behavior with variable scope and 'require' in NodeJS

I have 2 files:
testrequire.js
let a = {};
function foo() {
a = 'test';
};
module.exports.foo = foo;
module.exports.a = a;
test.js
let a = require('./testrequire');
a.foo();
console.log(a);
When I run test.js, this is the result:
{ foo: [Function: foo], a: {} }
But I expect it to be like this:
{ foo: [Fuction: foo], a: 'test' }
However, when I change testrequire.js like this:
let a = {};
function foo() {
a.b = 'test';
};
module.exports.foo = foo;
module.exports.a = a;
The result is:
{ foo: [Function: foo], a: { b: 'test' } }
And it is perfectly like what I expected.
The question here is: Why function foo() can modify a's properties while it cannot modify a?
P/S: I did try var instead of let and the result is still the same. So it is definitely not ES6 let fault.
It's a pointer thing. It's the same in C/C++, Java etc. We've gotten so used to closures that we've sort of expect regular pointers to work the same. But pointers/references are simple indirections.
Let's walk through your code:
let a = {};
Create an object ({}) and point the variable a to that object.
function foo() {
a = 'test';
};
Declare a function foo() that overwrites the value of a with a string. Now, if you remember your C/assembly then you'd remember that the value of a pointer is the address of the thing it points to. So the original value of a is not {} but the address to that object. When you overwrite a with a string that object still exist and can be garbage collected unless something else points to it.
module.exports.foo = foo;
module.exports.a = a;
Export two properties, 1. foo which points to a function and 2. a which points to the same object that a is pointing to. Remember, just like in C/Java this does not mean that module.exports.a points to a but that it points to {}. Now you have two variables pointing to the same object {}.
Now, when you do:
a.foo();
All you're doing is changing the enclosed variable a to point to a string instead of the original object. You haven't done anything to a.a at all. It's still pointing to {}.
Workarounds
There are two ways to get what you want. First, the OO way. Don't create a closure for a, make it a regular object property:
function foo() {
this.a = 'test';
};
module.exports.foo = foo;
module.exports.a = {};
This will work as expected because modules in node.js are proper singletons so they can be treated as regular objects.
The second way to do this to use a getter to get the enclosed a. Remember that closures only work with functions, not objects. So just assigning the variable to a property like you did results in a regular pointer operation not a closure. The workaround is this:
let a = {};
function foo() {
a = 'test';
};
function getA() {
return a; // this works because we've created a closure
}
module.exports.foo = foo;
module.exports.getA = getA;
Now you can do:
a.foo();
a.getA(); // should return 'test'
foo can modify the variable a to point to something else.
But this has no effect on the object exported. When the require is done, the calling module receives whatever a pointed to at the time. After that, it does not care about what happens to (the variable) a.
In your second example, you are not assigning a to a new object, but you are modifying the existing object (by adding a new field). That will of course be visible to anyone who got hold of that object before.
This is (very roughly) analogous to
function callerDoesNotSeeThis(a){ a = 1 }
function callerSeesThis(a){ a.foo = 1 }

Change value of object property inside javascript object affect other object

I would like to change a property of an object, inside an object. But, when I did that, other object property that created using the same prototype also changed.
The code is as follows:
var a = {
x: { y: 'foo' }
}
var b = Object.create(a)
var c = Object.create(a)
console.log(a.x.y) // 'foo'
console.log(b.x.y) // 'foo'
console.log(c.x.y) // 'foo'
b.x.y = 'bar'
var d = Object.create(a)
console.log(a.x.y) // 'bar'
console.log(b.x.y) // 'bar'
console.log(c.x.y) // 'bar'
console.log(d.x.y) // 'bar'
I think the problem is because all objects referring the same x, therefore changing y from any object reflected in all objects. Can anyone explain what really happened here, perhaps with reference and suggestion for a workaround?
x is an object, that is why it is referenced by a pointer and not by the value like the string is.
Try the following as a workaround:
b.x = { y: 'bar' } // instead of b.x.y = 'bar'
this creates a new object x which will be different from others
Try this:
var a = function() {
this.x = { y: 'foo '};
}
var b = new a();
var c = new a();
b.x.y = 'bar';
You will just reference the same object (reference pointer to a place in memory), and modify the object that each object reference to. What you probably want to do is create a new object that are isolated.
Object.create creates a new object and sets its prototype to the first parameter passed in. In your case that's an instance of an object (a), but you use that same instace as the prototype for b & c. So... when accessing members of the prototype of b, you really accessing members of a (via prototypical inheritance). Modifying that applies to all inheriting objects
In order to achieve the inheritance you tried, while using Object.create AND separating all instances, do this:
function a() {
this.x = { y: 'foo' }
}
var b = Object.create(new a())
var c = Object.create(new a())
//console.log(a.x.y) // a is not an object, it's a function, or a "type"
console.log(b.x.y) // 'foo'
console.log(c.x.y) // 'foo'
b.x.y = 'bar'
var d = Object.create(new a())
//console.log(a.x.y) // a is not an object, it's a function, or a "type"
console.log(b.x.y) // 'bar'
console.log(c.x.y) // 'foo'
console.log(d.x.y) // 'foo'

Referring to previously defined properties within an object literal

When using object constructors, properties can be directly assigned to the value of previously defined properties:
var foo = new (function() {
this.bar = 5;
this.baz = this.bar;
})();
alert(foo.baz) // 5
I would like to refer to a previously defined property within an OBJECT LITERAL:
var foo = {
bar : 5,
baz : bar
}
alert (foo.baz) // I want 5, but evaluates to undefined
I know that I could do this:
var foo = {
bar : 5,
baz : function() {
alert(this.bar); // 5
}
But I want to assign baz directly to a value rather than a function. Any ideas?
No, you won't be able to use any properties of the object literal before it has been created. Your closest option is probably to use a temporary variable like so:
var tmp = 5,
foo = {
bar : tmp,
baz : tmp
}
If you are free to use ECMAScript 5 features, you could write a getter function for the baz property that instead returns the value of bar:
var yourObject = {
bar: 5
};
Object.defineProperty(yourObject, 'baz', {
get: function () { return yourObject.bar; }
});
You can also just build a literal by parts:
var foo = {bar:5};
foo.baz = foo.bar;
If you need to fit this inside an expression (instead of through multiple statements) you can try abusing the comma operator or you can make a helper function:
(Warning: untested code)
function make_fancy_object(base_object, copies_to_make){
var i, copy_from, copy_to_list;
for(copy_from in copies_to_make){
if(copies_to_make.hasOwnProperty(copy_from)){
copy_to_list = copies_to_make[copy_from];
for(var i=0; i<copy_to_list.length; i++){
base_object[copy_to_list[i]] = base_object[copy_from];
}
}
}
}
var foo = make_fancy_object(
{bar: 5},
{bar: ["baz", "biv"]}
);
//foo.baz and foo.biv should be 5 now as well.

Categories

Resources