Module pattern: setting module-wide arrays - javascript

Despite having read all the usual tutorials on the Javascript module pattern, there's still something about scoping which I clearly don't understand.
pushing to a module-wide list does what I'd expect it to do. Setting a module-wide list with = doesn't. What am I doing wrong here?
var MODULE = MODULE || {};
MODULE.Submodule = (function(){
var foo = [],
bar = [];
var init = function() {
foo = ['a','b','c']; // This doesn't set MODULE.Submodule.foo
bar.push('a'); // But this works as expected
console.log('foo: ' + MODULE.Submodule.foo); // foo:
console.log('bar: ' + MODULE.Submodule.bar); // bar: a
}
return {
init: init,
foo: foo,
bar: bar
}
}());
MODULE.Submodule.init();

This JSFiddle http://jsfiddle.net/SwBLk/1/ may help explain what's happening:
var MODULE = MODULE || {};
MODULE.Submodule = (function(){
var foo = [],
bar = [];
var init = function() {
console.log(MODULE.Submodule.foo === foo);
foo = ['a','b','c']; // This doesn't set MODULE.Submodule.foo
console.log(MODULE.Submodule.foo === foo);
bar.push('a'); // But this works as expected
console.log('foo: ' + MODULE.Submodule.foo); // foo:
console.log('bar: ' + MODULE.Submodule.bar); // bar: a
}
return {
init: init,
foo: foo,
bar: bar
}
}());
MODULE.Submodule.init();
The first boolean check return TRUE because both objects reference the same object.
The second boolean check returns FALSE because you have replaced foo with a new object and the references don't point to the same underlying object anymore.
You are replacing the foo array with a reference to a new array when you re-assign a new array to it.
When you execute the IIFE, you assign a reference to a very specific version of "foo" on the return statement. That is the reference you then access when you call MODULE.Submodule.foo. When you go in and replace foo = [ "a", ... ], you are replacing the object foo, but not replacing the reference to it in the MODULE.Submodule object.
EDIT: How do you get around this issue?
1) Ideally, you don't replace the entire array, but only clear & re-initialize it on init() call:
MODULE.Submodule = (function(){
var foo = [],
bar = [];
var init = function() {
// Clear our the array if it already contains data (in case your calling "init" more than once)
// This method is documented to be the fastest way to clear out an array, referenced here:
// http://stackoverflow.com/questions/1232040/how-to-empty-an-array-in-javascript
while(foo.length > 0) {
foo.pop();
}
foo.push('a');
foo.push('b');
foo.push('c');
bar.push('a'); // But this works as expected
console.log('foo: ' + MODULE.Submodule.foo); // foo:
console.log('bar: ' + MODULE.Submodule.bar); // bar: a
}
2) Your second option is to use what I'd call "dynamic getters" (working fiddle: http://jsfiddle.net/6zVcP/):
var MODULE = MODULE || {};
MODULE.Submodule = (function(){
var foo = [],
bar = [];
var init = function() {
foo = ['a','b','c']; // This doesn't set MODULE.Submodule.foo
bar.push('a'); // But this works as expected
console.log('foo: ' + MODULE.Submodule.foo()); // foo:
console.log('bar: ' + MODULE.Submodule.bar()); // bar: a
}
var modify = function()
{
foo = [];
foo.push("test");
}
return {
init: init,
modifyArrays: modify,
foo: function() { return foo; },
bar: function() { return bar; }
}
}());
MODULE.Submodule.init();
MODULE.Submodule.modifyArrays();
console.log(MODULE.Submodule.foo());
This allows you to do whatever you want to the foo object while the getter will always return the latest reference.

Related

Javascript Proxy set() local property on inherited objects

According to MDN,
handler.set() can trap Inherited property assignment:
Object.create(proxy)[foo] = bar;
In which case, how does one both monitor and allow local assignments on inherited objects?
var base = {
foo: function(){
return "foo";
}
}
var proxy = new Proxy(base, {
set: function(target, property, value, receiver){
console.log("called: " + property + " = " + value, "on", receiver);
//receiver[property] = value; //Infinite loop!?!?!?!?!
//target[property] = value // This is incorrect -> it will set the property on base.
/*
Fill in code here.
*/
return true;
}
})
var inherited = {}
Object.setPrototypeOf(inherited, Object.create(proxy));
inherited.bar = function(){
return "bar";
}
//Test cases
console.log(base.foo); //function foo
console.log(base.bar); //undefined
console.log(inherited.hasOwnProperty("bar")) //true
After some additional thought, i noticed that it intercepts 3 ops:
Property assignment: proxy[foo] = bar and proxy.foo = bar
Inherited property assignment: Object.create(proxy)[foo] = bar
Reflect.set()
but not Object.defineProperty() which appears to be even lower level than the = operator.
Thus the following works:
var base = {
foo: function(){
return "foo";
}
};
var proxy = new Proxy(base, {
set: function(target, property, value, receiver){
var p = Object.getPrototypeOf(receiver);
Object.defineProperty(receiver, property, { value: value }); // ***
return true;
}
});
var inherited = {};
Object.setPrototypeOf(inherited, Object.create(proxy));
inherited.bar = function(){
return "bar";
};
// Test cases
console.log(base.foo); // function foo
console.log(base.bar); // undefined
console.log(inherited.bar); // function bar
console.log(inherited.hasOwnProperty("bar")) // true
I see two options (maybe):
Store the property in a Map, keeping the Maps for various receivers in a WeakMap keyed by the receiver. Satisfy get by checking the Map and returning the mapping there instead of from the object. (Also has.) Slight problem is that you also need to proxy the receiver (not just base) in order to handle ownKeys. So this could be unworkable.
Temporarily get the proxy out of the inheritance chain while setting.
Here's that second one:
var base = {
foo: function(){
return "foo";
}
};
var proxy = new Proxy(base, {
set: function(target, property, value, receiver){
const p = Object.getPrototypeOf(receiver); // ***
Object.setPrototypeOf(receiver, null); // ***
receiver[property] = value; // ***
Object.setPrototypeOf(receiver, p); // ***
return true;
}
});
var inherited = {};
Object.setPrototypeOf(inherited, Object.create(proxy));
inherited.bar = function(){
return "bar";
};
// Test cases
console.log("base.foo:", base.foo); // function foo
console.log("base.bar:", base.bar); // undefined
console.log("inherited.bar:", inherited.bar); // function bar
console.log("inherited has own bar?", inherited.hasOwnProperty("bar")); // true

Default enum value in javascript class constructor

I have a simple class, and I'm trying to figure out how to set a default value in the constructor:
var Foo = function(bar = Foo.someEnum.VAL1) {
this.bar = bar;
someEnum = {VAL1 : 1, VAL2: 2};
}
and to use it like:
var myFoo = new Foo(Foo.someEnum.VAL1);
but this is apparently wrong. What's the correct way to set a default enum value, or do I need to set the default to null, and check for the null in the constructor and set accordingly?
To clarify, bar is an enum for the Foo class. There are other properties in the class that are not shown. Also, updated class code.
You can try this if you want to make bar an optional parameter:
function Foo(bar) {
this.bar = bar || Foo.enum.VAL1; //If bar is null, set to enum value.
}
//Define static enum on Foo.
Foo.enum = { VAL1: 1, VAL2: 2, VAL3: 3 };
console.log(new Foo().bar); //1
console.log(new Foo(Foo.enum.VAL3).bar); //3
Do you just want bar to be defined inside the function?
var Foo = function() {
var bar = {VAL1 : 1, VAL2: 2};
}
or for a blank starting object:
var Foo = function() {
var bar = {};
}
Or are you wanting it to be set from a parameter that's passed into the function?
var Foo = function(barIn) {
var bar = barIn;
}
Another option - create the object (enum) from values passed in:
var Foo = function(val1, val2) {
var bar = {VAL1 : val1, VAL2: val2};
}
The possibilities go on, but it's not entirely clear what you're trying to achieve.
I'm not entirely sure what you are trying to do but maybe it is this...
var Foo = function (bar = 1) {
this.bar = bar;
}
Foo.VAL1 = 1;
Foo.VAL2 = 2;
Now you can do:
foo1 = new Foo();
alert(foo1.bar); //alerts 1;
foo2 = new Foo(Foo.VAL2);
alert(foo1.bar); //alerts 2;

Callback when 'class' instance is created

var foo = (function(){
var c = function(requests) {
bar();
};
c.prototype = {
bar: bar
};
return c;
})();
var f = new foo();
f.baz(function(){
console.log('new instance of foo created');
});
http://jsfiddle.net/LecJM/
I would like to create a callback function which is called when a new instance of the "class" foo is created. Is this possible? Obviously the code above won't compile, I just want to give you an idea of what I'm trying to achieve.
var Foo = function (createdCallback) {
createdCallback();
return this;
};
var bar = new Foo(function () {
console.log("instance created");
});
Is this what you want to achieve?
Something like this?
var foo = (function(){
var c = function(requests) {
// Initialize your instance
// ...
// Notify
notifyCreated(this);
};
c.prototype = { ... };
var createdCallbacks = [];
c.onCreate = function(callback) {
createdCallbacks.push(callback);
}
function notifyCreated(instance) {
// Note: forEach requires ES5 or a shim
// Just use whatever you want to loop over the array
createdCallbacks.forEach(function(callback) {
callback(instance);
});
}
return c;
})();
// Add callback *before* creating instances
foo.onCreate(function(instance){
console.log('new instance of foo created', instance);
});
// Create an instance
var f = new foo();
Basically, you add a method to foo (and not to foo.prototype) to add a callback. Inside your constructor, you call all registered callbacks (here demonstrated with a separate internal function). To use, you first register a callback and then start creating instances.
EDIT: As requested, with just one callback:
var foo = (function(){
var c = function(requests) {
// Initialize your instance
// ...
// Notify
notifyCreated(this);
};
c.prototype = { ... };
// Use a dummy callback by default
var notifyCreated = function(){};
c.onCreate = function(callback) {
notifyCreated = callback;
}
return c;
})();
Demo
EDIT 2: Heck, if you're only going to need one callback, you might as well get rid of the onCreate function and just expose the callback as a variable. There are a few downsides to this though:
You can't do input checking, for example you can't test if the callback is actually a function before storing it.
Others can trigger the callback externally through foo.onCreate(anInstance).
If those are not problematic (for example if you're not exposing foo anyway), feel free to use this extremely simple snippet:
var foo = (function(){
var c = function(requests) {
// Initialize your instance
// ...
// Trigger callback
c.onCreate(this);
};
c.prototype = { ... };
// Expose callback on "class"
c.onCreate = function(){};
return c;
})();
// Set callback *before* creating instances
foo.onCreate = function(instance){
console.log('new instance of foo created', instance);
};
// Create an instance
var f = new foo();
Demo
Try this
var foo = function() {
this.baz();
};
foo.prototype.baz = function () {
console.log('new instance of foo created');
};
var f = new foo();

Creating a new empty object in JavaScript

When trying to create a new object, one that is empty for manipulation, I can't get the old data out.
Here is an example of what I've tried:
function Foo() {
this.Bar = Bar;
// etc..
}
var Bar = {
__words : {},
addWord : function (word, amount) {
this.__words[word] = amount;
}
// etc..
}
Now, when I create a new object:
var foo = new Foo();
var bar = foo.Bar;
bar.addWord("hello",7);
bar.addWord("world",9);
var lorem = new Foo();
var words = lorem.Bar.__words; // This will display {hello:7,world:9} from the
// previous object
I also tried using Object.create() but it was still the same, showing the __words from the previous object.
The object referred to by Bar is shared between each Foo instance.
I don't really see the point of putting the logic into two objects, you can do this with just one constructor function:
function Foo() {
this.__words = {};
}
Foo.prototype.addWord = function(word, amount) {
this.__words[word] = amount;
}
var foo = new Foo();
foo.addWord("hello",7);
var bar = new Foo();
bar.addWord("world",9);
If you really have to separate the functionality, then make Bar a constructor function as well and create a new instance inside the Foo constructor method:
function Foo() {
this.bar = new Bar();
}
function Bar() {
this.__words = {};
}
Bar.prototype.addWord = function(word, amount) {
this.__words[word] = amount;
}
There are couple of problems in the code.
function Foo() {
this.Bar = Bar;
// etc..
}
var Bar = {
__words : {},
addWord : function (word, amount) {
this.__words[word] = amount;
}
// etc..
}
Bar should be defined before Foo but it might work without problems because variables are actually defined at function scope level which might not be a problem in this particular example.
That said, you're copying an object inside the Foo constructor. Objects are mutables so it won't work. One change to the object will change the other. What you might be able to do is this. Here's an other way to do that without new.
var Bar = {
create: function () {
var obj = Object.create(this);
obj.__words= {};
return obj;
},
addWord: function (word, amount) {
this.__words[word] = amount;
}
...other methods...
};
var Foo = function () {
this.Bar = Bar.create();
// etc..
};
But I have no idea how is supported Object.create in other browsers. This is syntax is the equivalent to what Felix wrote, you could clearly remove the need of two object.
The only big difference is that instead of writing new Bar(). You are actually creating a prototype object and creating an object from the prototype. The create is the constructor.
Foo could be written in the same way and you'd get something like this.
var Bar = {
create: function () {
var obj = Object.create(this);
obj.__words= {};
return obj;
},
addWord: function (word, amount) {
this.__words[word] = amount;
},
...other methods...
};
var Foo = {
create: function () {
var foo = Object.create(this);
foo.Bar = Bar.create();
return foo;
},
....
};
var foo1 = Foo.create();
var foo2 = Foo.create();
foo1.bar.addWord("allo", 3);
I guess it all depends on what you're trying to do but the create method as some advantages over the "new" operators. You could for example create async methods that after object creations execute a callback.
Over here for more informations. In the end it's pretty much the same thing.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create
I can't get the old data out.
Well you are not clearing var Bar which is global actually - meaning you are creating a new Object with var lorem = new Foo(); but giving him the same Bar Object with Foos constructor.
this.Bar = Bar;
if you want a new Object you should give Foo its own Bar Object.
Or include:
__words : {},
addWord : function (word, amount) {
this.__words[word] = amount;
in Foo itself.
Anyways if you just need this once, you could simply empty the words array in Bar.

javascript private inner functions in module with access to "this" scope [duplicate]

This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
Javascript outer scope variable access
I have a javascript module that looks something like below. The main issue I'm having is how to access variables in the "this" scope from the private someOtherFunc. Is there a way to access this.myvar in the private someOtherFunc
var mymodule = (function(){
return {
MyObj : function() {
this.myvar = 123;
this.publicfunc = function() {
someOtherFunc();
};
var someOtherFunc = function() {
//this doesn't seem to work
this.myvar = 456;
};
}
}
}
The idea is that I want to be able to do something like
new mymodule.MyObj().publicfunc, but make the someOtherFunc private
Forget my previous answer. You can do this just by adding a private version of this.
var mymodule = (function() {
return {
MyObj : function() {
this.myvar = 123;
var that = this;
this.publicfunc = function() {
someOtherFunc();
};
var someOtherFunc = function() {
that.myvar = 456;
};
return this;
}
};
});
Bear in mind that, with your code, every time you call MyObj you get a new object.
So this would do what you want:
>var o = new mymodule().MyObj()
>o.myvar
123
>o.publicfunc()
>o.myvar
456
but not this
>var m = new mymodule()
>m.MyObj().myvar
123
>m.MyObj().publicfunc()
>m.MyObj().myvar
123
If that's not what you want, consider doing something like this
var mymodule = (function() {
var myObj = null;
this.MyObj = function() {
if(myObj != null)
return myObj;
myObj = {};
myObj.myvar = 123;
myObj.publicfunc = function() {
someOtherFunc();
};
var someOtherFunc = function() {
myObj.myvar = 456;
};
return myObj;
};
});
Declare myvar using the var keyword, making it private, then access it without the this.:
function MyObj(){
var myvar = 123;
this.publicfunc = function() {
someOtherFunc();
};
var someOtherFunc = function(){
alert(myvar);
};
}
var o = new MyObj();
o.publicfunc();
If you need public access to myvar then create a public getter/setter.
jsFiddle Demo
​
I think what you're looking for is a way to encapsulate myvar for changes. When some of the other answers run, myvar usually will stay as 123 because the initially returned object from mymodule holds on to that initial value.
Return a function that gets the value of myvar even after it's been modified, and I think that helps your problem.
Here's the code that works for me:
var mymodule = (function(){
var myvar = 123,
publicfunc = function() { myvar = 456 },
getMyVar = function() { return myvar; };
return {
someOtherFunc : publicfunc,
myPublicVar : getMyVar
};
}());
mymodule.someOtherFunc();
alert(mymodule.myPublicVar());​ //gives you 456
JSFiddle here.
I hope this helps.
Why not build it a little more deliberately?
// this one returns an object used like:
// myModule.myInt; /* 456 */
// myModule.myFunc(); /* 124 */
var myModule = (function () {
var secretData = 123,
publicData = 456,
publicFunc = function () { return privateFunc(secretData); },
privateFunc = function (num) { return num + 1; },
public_interface = {
myInt : publicData,
myFunc : publicFunc
};
return public_interface;
}());
I went through the trouble of explicitly naming the returned, public object, but it's now very clear what is and isn't public, and yet, each one of those things will have access to the variable versions of one another, with the one exception being that if you change myModule.myInt or publicData, they will no longer be equal.
To demonstrate what I mean in the comments below, creating multiple instances with their own private data/methods, I just add in one more layer of function-scope:
var myModule = (function () {
var static_int = 789,
makeInstance = function (/* any constructor values */) {
var secretData = 123,
publicData = 456,
publicFunc = function () { return privateFunc(secretData); },
privateFunc = function (num) {
console.log(static_int);
return num + 1;
},
public_interface = {
myInt : publicData,
myFunc : publicFunc
};
return public_interface;
};
return makeInstance;
}());
You now use it like:
var newModule = myModule(/* instance parameters */);
newModule.myFunc();
...or
var num = myModule(/* instance parameters */).myFunc();
If you wanted to save memory, you could have static helper functions inside of the static-layer:
var myModule = (function () {
var static_int = 789,
static_addOne = function (num) { return num + 1; },
static_divideBy = function (dividend, divisor) { return dividend/divisor; },
makeInstance = function (/* any constructor values */) {
var secretData = 123,
publicData = 456,
publicFunc = function () { return privateFunc(secretData); },
privateFunc = function (num) {
console.log(static_int);
return num + 1;
},
public_interface = {
myInt : publicData,
myFunc : publicFunc
};
return public_interface;
};
return makeInstance;
}());
And now you have "private" functions which are only written one time (ie: you save memory), but any instance can use those functions.
Here's the catch:
Because of how scope and closure work, the static functions have NO access to values inside of the instance (functions inside have access to the static functions, not the other way around).
So, any static helper functions MUST have the values passed to them as arguments, and if you're modifying a number or a string, you MUST return the value out of that function.
// inside of a private method, in any instance
var privateString = "Bob",
privateFunc = function () {
var string = static_helper(privateString);
privateString = string;
//...
};
You don't return anything from MyObj.
return this;
should fix it.
Use the bind method:
var someOtherFunc = function() {
this.myvar = 456;
}.bind(this);

Categories

Resources