Trouble passing Vector Array through JS classes - javascript

I am experiencing trouble passing and array of Vectors (subNodes) held in a class "Node". To class Main. Any blatant mistakes?
// Node class
// other class code
var subNodePos =[]; // array has been correctly populated in node class
// init array in construct
// array has been correctly populated in node class
Node.prototype.getSubNodeArray = function () { return this.subNodePos; }
// Main class
function draw()
{
// code ...
// can access all object variables and methods except for this one array
var famPos = node.getSubNodeArray(); // passing array // fails here 'undefined'
var temp = famPos[0]; // error
}

You have declared subNodePos as a private variable of your node constructor.
But this variable is not available to this method:
Node.prototype.getSubNodeArray = function () { return this.subNodePos; }
You can fix it in at least 2 ways:
Solution 1. Make subNodePos a public member
Make it a property of this in your constructor:
function Node() {
...
this.subNodePos = [];
...
}
Node.prototype.getSubNodeArray = function () { return this.subNodePos; }
Solution 2. Define getSubNodeArray at object level
This is when you want to keep subNodePos private. Then you can keep the var declaration, but must reference it without this:
function Node() {
...
var subNodePos = [];
...
this.getSubNodeArray = function () { return subNodePos; }
...
}

Related

Getters can set private variables

I think I've misunderstood something about OOP in JavaScript. As I understand it, the point of setting up a private variable with its own getter and setter functions is to protect it from accidental alteration elsewhere in the program. But I'm finding it very easy to accidentally change private variables using their getter functions, which means I must be doing something wrong.
function Phone(os) {
this.os = os;
var _phoneBook = []; // phoneBook is intended to be private
this.newNumber = function(phoneNumber) { // set new number
_phoneBook.push(phoneNumber);
}
this.listNumbers = function() { // get all numbers
return _phoneBook;
}
}
var andy = new Phone("Android");
andy.newNumber("555-123-4567");
console.log(andy.listNumbers()); // => ["555-123-4567"]
// You shouldn't be able to set a private property through a getter function, but you can.
var extendedPhoneBook = andy.listNumbers().push("123-456-7890");
console.log(andy.listNumbers()); // => ["555-123-4567", "123-456-7890"]
That Array is being passed as a reference, so when calling listNumbers() you are returning the Actual Array to the exterior. You can return copies of the Array with something like this:
function Phone(os) {
this.os = os;
var _phoneBook = []; // phoneBook is intended to be private
this.newNumber = function(phoneNumber) { // set new number
_phoneBook.push(phoneNumber);
}
this.listNumbers = function() { // get all numbers
return _phoneBook.slice(0);
}
}
you might consider changing function listNumbers to something like enumerateNumbers that takes a function that gets passed a string for each number in your list to hide the internal array, something like this:
function Phone(os) {
this.os = os;
var _phoneBook = []; // phoneBook is intended to be private
this.newNumber = function(phoneNumber) { // set new number
_phoneBook.push(phoneNumber);
}
this.enumerateNumbers = function(cb) { // get all numbers
for (var idx in _phoneBook) {
cb(_phoneBook[idx]);
}
}
}
var andy = new Phone("Android");
andy.newNumber("555-123-4567");
andy.enumerateNumbers(function(num) { console.log(num); })

Trouble setting variables defined in parent class when creating a new subclass object

I have been trying to learn the ins and outs of Javascript's inheritance structure and I came across this problem. I'm trying to create several subclass objects and assign values to them immediately by passing a variable upon their creation.
For instance, below the parent class GamePiece receives a random property as a variable upon its creation and sets it as myProperty which works fine of course when creating a new GamePiece object. However, if I wanted to set that variable on the creation of a Pawn object, it does not pass into its parent and remains unset. The obvious fix is to just define the variable again on the subclass but that, correct me if I'm wrong, seems to defeat the purpose of defining a parent class. You can also successfully set the variable by passing the parameter as Pawn.prototype = new GameObject("foo"); but that doesn't help when creating multiple Pawn() objects. Is there a common way of doing this that I am missing?
var GamePiece = function (randomProperty) {
this.myProperty = randomProperty || "never set";
this.print = function () {
console.log(this.myProperty);
}
}
var Pawn = function (randomProperty) {
this.print = function () {
console.log(this.myProperty);
}
}
//Setting a value on creation
piece = new GamePiece("foo");
piece.print(); // Produces "foo" naturally
//Setting the prototype
Pawn.prototype = new GamePiece();
//Try to pass value through the creation of subclass
pawn = new Pawn("foo");
pawn.print(); // Produces "never set"
You must call you parent class in context of current this, using call or apply method:
var GamePiece = function (randomProperty) {
this.myProperty = randomProperty || "never set";
this.print = function () {
console.log(this.myProperty);
}
}
var Pawn = function (randomProperty) {
Game.call(this, randomProperty);
// or Game.apply(this, [randomProperty]);
}
But much it's better to keep methods in prototype. So next code will be better:
var GamePiece = function (randomProperty) {
this.myProperty = randomProperty || "never set";
//... some another properties initialization
};
GamePiece.prototype.print = function () {
console.log(this.myProperty);
};
var Pawn = function (randomProperty) {
Game.call(this, randomProperty);
// or Game.apply(this, [randomProperty]);
//... some Pawn properties initialization
};
Pawn.prototype = Object.create(Game.prototype, { constructor: { value: Pawn }});
Pawn.prototype.someMethod = function() {
// Some Pawn method logic
};
But ES6 is coming (will become recommendation in June 2015), so you can start preparation for using them. See here, here and here

Can a javascript function be a class and an instance of another object?

If you look at this code:
function supportAggregate(Meanio) {
Meanio.prototype.aggregated = function(ext, group, callback) {
// Aggregated Data already exists and is ready
if (Meanio.Singleton.config.clean.aggregate === false){
return callback('');
}
if (aggregated[group][ext].data) return callback(aggregated[group][ext].data);
// No aggregated data exists so we will build it
sortAggregateAssetsByWeight();
// Returning rebuild data. All from memory so no callback required
callback(aggregated[group][ext].data);
};
Meanio.prototype.aggregatedsrc = function(ext, group, callback) {
// Aggregated Data already exists and is ready
if (Meanio.Singleton.config.clean.aggregate !== false){
if(ext==='js'){
if(group==='header'){
return callback(['/modules/aggregated.js?group=header']);
}else{
return callback(['/modules/aggregated.js']);
}
}else if(ext==='css' && group==='header'){
return callback(['/modules/aggregated.css']);
}
return callback([]);
}
if (aggregated[group][ext].src) return callback(aggregated[group][ext].src);
// No aggregated data exists so we will build it
sortAggregateAssetsByWeight();
// Returning rebuild data. All from memory so no callback required
callback(aggregated[group][ext].src);
};
// Allows rebuilding aggregated data
Meanio.prototype.rebuildAggregated = function() {
sortAggregateAssetsByWeight();
};
Meanio.prototype.Module.prototype.aggregateAsset = function(type, asset, options) {
options = options || {};
if (!options.inline && !options.absolute && !options.url) {
asset = path.join(Meanio.modules[this.name].source, this.name, 'public/assets', type, asset);
}
Meanio.aggregate(type, asset, options, Meanio.Singleton.config.clean);
};
Meanio.onModulesFoundAggregate = function(ext, options) {
var config = Meanio.Singleton.config.clean;
var aggregator = new Aggregator(options, false, config);
for (var name in Meanio.modules) {
aggregator.readFiles(ext, path.join(process.cwd(), Meanio.modules[name].source, name.toLowerCase(), 'public'));
}
};
Meanio.aggregate = function(ext, asset, options, config) {
var aggregator;
options = options || {};
if (!asset) {
return;
}
aggregator = new Aggregator(options, true, config);
if (options.inline) return aggregator.addInlineCode(ext, asset);
else if (options.url) return aggregator.getRemoteCode(ext, asset);
else if (options.singlefile) return aggregator.processDirOfFile(ext, asset);
else return aggregator.readFile(ext, path.join(process.cwd(), asset));
};
Meanio.prototype.aggregate = Meanio.aggregate;
}
module.exports = supportAggregate;
(https://github.com/linnovate/meanio/blob/master/lib/aggregation.js#L213)
You can see that there are two types of functions for Meanio that are created. Also, by the way, you can see where this is instantiated here: https://github.com/linnovate/meanio/blob/master/lib/mean.js#L114
But I'm just confused. Sometime, Meanio functions are defined like this:
Meanio.prototype.myfunction = function() {}
and sometimes they are defined like this:
Meanio.myfunction = function() {}
I just don't get it; although I have a feeling that dependency injection is somehow involved.
How can this be? How can an object be both a class and an instance of itself?
This code is very confusing to me, and I would really appreciate it if someone could shed some light on this for me. I'm not asking you to heavily research the code, but if you could give me a general understanding, that would be great.
Thanks in advance!
How can an object be both a class and an instance of itself?
That's not what's going on here. The object passed to the function is an instance.
The function does however modify both the instance that you pass to it, and the class of that instance.
If you create two instances of the same class, and pass one of them to the function, the other instance is not modified, but the class that is common to them is modified. Example:
function MyClass() {}
var a = new MyClass();
var b = new MyClass();
supportAggregate(a);
Now both a.rebuildAggregated and b.rebuildAggregated exist, as that is added to the class. The a.onModulesFoundAggregate exists because it's added to the instance, but b.onModulesFoundAggregate doesn't exist.
(Note: The example won't actually work, as there is more going on. The class has to have some more properties to work with that function, the example is only to show the difference between properties added to the prototype and to the instance.)
Let's say I have a constructor
// First I will define a constructor
function MyClass() {
this.classproperty = 1;
}
In Javascript the constructor is also an object instance. When I use "this" keyword inside a constructor I'm telling that I want to create a new property inside a special object present in all javascript objects called prototype.
// Then I add a new property without using prototype obj
MyClass.newProperty = 2;
alert(MyClass.classproperty); // alert 1
alert(MyClass.newProperty); // alert 2
// It will work because I'm using the MyClass main Object
When you create a new instance from Myclass Obj. The new created object will inherit the prototype object from parent (the one used to instantiate), but not the properties added straight to MyClass obj:
var instance = new MyClass();
alert(instance.newProperty); // undefined because the new instance will
// inherit only what is inside prototype obj
I have to add it to the prototype object in order to new instances inherit the property;
Myclass.prototype.newProperty = 2;
var instance = new Myclass();
alert(instance.newProperty) // alert 2

Making JavaScript private methods accessible to its public methods

I understand there are couple of patterns to make JavaScript 'class-like'.
I would like to take the 'extending by prototype' way... simply because it looks more neat. I am not worried about performance much here...
In the below example I have a class (basically function) MetricsChart. I have couple of public methods and one private method (basically a reusable method).
Here from the public method (drawOrAdd) I can't access the private method (_convertArrayToTable), how can I do that?
function MetricsChart(containerId, chartType) {
this._container = document.getElementById(containerId);
this._chartType = chartType;
this._isChartDrawn = false;
this._chartData = null;
var _convertArrayToTable = function (data) {
return google.visualization.arrayToDataTable(data);
}
}
MetricsChart.prototype.drawOrAdd = function(data)
{
if (!this.isChartDrawn()) {
var chart = new google.visualization.LineChart(this._container);
if (chart) {
var table = _convertArrayToTable(data);
console.log(table);
this._isChartDrawn = true;
}
}
}
MetricsChart.prototype.isChartDrawn = function () {
return this._isChartDrawn;
}
MetricsChart.prototype.getChartData = function () {
}
One way I accidentally found was to enclose the public methods inside the MetricsChart class itself...
It works for me :): I can access the public methods outside and the public method can access the private method (serves the purpose).
Below code... Is this right? Am I doing anything wrong?
function MetricsChart(containerId, chartType) {
this._container = document.getElementById(containerId);
this._chartType = chartType;
this._isChartDrawn = false;
this._chartData = null;
var _convertArrayToTable = function (data) {
return google.visualization.arrayToDataTable(data);
}
MetricsChart.prototype.drawOrAdd = function (data) {
if (!this.isChartDrawn()) {
var chart = new google.visualization.LineChart(this._container);
if (chart) {
var table = _convertArrayToTable(data);
console.log(table);
this._isChartDrawn = true;
}
}
}
MetricsChart.prototype.isChartDrawn = function () {
return this._isChartDrawn;
}
MetricsChart.prototype.getChartData = function () {
}
}
So, here a couple of things, in order to understand what you have done precisely.
First of all:
function foo() {
var i = 0;
function bar() {
return true;
}
}
What's happening here: every time the function foo is called, it creates in its scope a new variable i, and a new function bar. The function bar and the variable i are in its scope, it means they're local: there is no way, with this code, to access to either i or bar outside the function foo. Also because, once the function foo is terminated, both i and bar are disposed.
So, this is why you cannot access from your "public" method to the "private" one, and I hope it's more clear now. The only way for a function to access to a function or variable is that there is a reference shared in the same scope. So, this is what you have done in your last example: you define your "public" methods in the same scope where you define your "private" method. In this way they can access each other. However, the way you have done, has a big downside. As I said previously, the function bar is created every time the function foo is called. In a "class" example, it means:
function MyClass() {
function myprivate() {}
MyClass.prototype.mypublic = function () { return myprivate() }
}
It means that every time you're creating an instance of MyClass, you're creating two new functions, and you're rewrite all the time the prototype of your "class". This is far from be a good approach. In fact, if you have something like:
var a = new MyClass();
var _mypublic = a.mypublic;
var b = new MyClass();
console.log(_mypublic === b.mypublic) // false
console.log(_mypublic === a.mypublic) // false too!
So, you guess right but you executed wrong. What you need here is a the "module pattern": nowadays you can use CommonJS module in nodejs or AMD in browser and so on, but the basic idea is defined a "scope" and exports from this scope only what you want. In your case, you could have:
// this is your "module"
;(function(exports) {
// your local (private) function
var _convertArrayToTable = function (data) {
return google.visualization.arrayToDataTable(data);
}
function MetricsChart(containerId, chartType) {
this._container = document.getElementById(containerId);
this._chartType = chartType;
this._isChartDrawn = false;
this._chartData = null;
}
MetricsChart.prototype.drawOrAdd = function(data) {
if (!this.isChartDrawn()) {
var chart = new google.visualization.LineChart(this._container);
if (chart) {
var table = _convertArrayToTable(data);
console.log(table);
this._isChartDrawn = true;
}
}
}
// you decided to exports to the main scope what you want
exports.MetricsChart = MetricsChart;
}(this)); // here `this` is the global object
And that's it. You have created a closure, using the "module pattern", and from the "public" method you can access to the "private" function, because they're defined in the same scope. But because you do not do that in the "class" constructor, you don't redefine them every time you instantiate a new object. Therefore, the previous example written in this way, will give the right result:
var a = new MyClass();
var _mypublic = a.mypublic;
var b = new MyClass();
console.log(_mypublic === b.mypublic) // true
console.log(_mypublic === a.mypublic) // true
What you've done isn't necessarily "wrong"...it just looks weird. Also, you won't be able to access "MetricsChart.prototype.*" until after you've created an instance of "MetricsChart". Depending on how you are using this object, it may not matter.
That being said, another way is to keep your original structure, but move the following outside of the constructor:
var _convertArrayToTable = function (data) {
return google.visualization.arrayToDataTable(data);
}
It would still be private to your module which should be good enough (you are using modules right?).
What you have done works perfectly.
You can't inherit private methods in any OOP language in terms of overriding them or accessing them directly. They are private. So it makes no sense to have them prototyped for inheritance purposes. You have wrapped them in function scope so they are as "private" as they need to be.
To access the private methods use privilege methods. Check this document: http://javascript.crockford.com/private.html.
About your code check this answer:
Setting javascript prototype function within object class declaration
p.s.
function Test()
{
var p = function(pv)
{
//
}
this.e = function (ap) { p(ap) }
}
var n = new Test();
n.e("e"); // It is legal call
n.p(); // will throw
But if you declare a private function in c-tor it will be executed on first creation of object of this type. When declare a methods in prototype this methods are add before any code execution. In general the browser first check the js file to collect all methods for prototype and than execute any code. So when you declare a prototype methods into c-tor this methods will be available only after first creation of the object of those type. ( Sorry for my English ).
Check this situation:
function Test()
{
alert(this.ST_A);//alert undefined
alert(this.ST_B);//alert 2
Test.prototype.ST_A = 1;
alert( this.ST_A)//alert 1
}
Test.prototype.ST_B = 2;
In first pass the browser will populate Test with ST_B and ST_B will be available anywhere any time. After than in second pass the browser will start to execute the code in this time ST_A will not be visible until the browser execute the Test.prototype.ST_A = 1;

Javascript function (type) to store & use data

I really never used a javascript function type or class before, I understand Java and Python, but not javascript. So, I build a class like this:
function FormStore (type) {
this.setup = () =>{
this.store = {};
this.ERR_LINE_PREFIX = '#err_';
this.NO_DISPLAY_CLASS = 'no-display';
this.settings = {
'myID':{'hide':false},
}
}
this.checkVal= () => {
var geoArr = ['id_xx','myID', (...)];
var id;
$.each( geoArr, function(val) {
id = geoArr[val];
console.log(this.store) //-> returns undefined, below line is error
if (!(this.store[id])) {
return false;
}
});
};
var FS = new FormStore();
FS.setup();
The store is filled by components on document.ready. There is a function that looks up if the aligned components (glyph, label, input) have some classes or values and for the specific component fills a dict: {label:false,glyph:false, input:false}. However, for some reason it doesn't matter. Even if I enter some values in to the store right away (in setup) or create them on the fly, in checkVal the store doesn't exist, it's undefined.
Please, anybody, what am I not understanding about javascript type and classes here? I am googling this a lot and trying to find good resources but, "javascipt variable class" (or type) just yields a lot of DOM manipulation.
edit
There is a context problem in checkVal, you are using a non-arrow (and not explicitly bound) callback function and trying to access this inside of it. Change that to an arrow function as well, and the parent context (this) will be preserved:
$.each( geoArr, (val) => {
id = geoArr[val];
console.log(this.store)
if (!(this.store[id])) {
return false;
}
});
And while you are at changing that section, it's not going to work. You will not get access to $.each's return value. You should rely on native array APIs for this task and use Array.every to determine if all geoArr items are in the store (assuming that's your goal):
// returns false if not all geoArr items are in the store
geoArr.every(id => this.store[id])
original
I don't see you calling checkVal() anywhere, but based on the error you are getting it is called prior to setup() (since setup initializes the store). You could solve that problem straight away by moving this.store = {} out of setup (right at the top), e.g.:
function FormStore(type) {
this.store = {};
...
Having said that, I would suggest either defining your methods on the prototype, or utilizing ES6 classes. Here is a simplified version of both:
ES5 class
function FormStore(type) {
// make sure user didn't forget new keyword
if (this === window) {
throw new Error('FormStore must be called with "new" keyword')
}
// initialize state, this is the constructor
this.type = type;
this.store = {};
// any other state the class manages
}
FormStore.prototype = {
setup: function() {
// do setup stuff
// "this" points to instance
console.log('setup', this.type)
},
checkVal: function() {
}
}
var formStore = new FormStore('foo')
console.log(formStore.store) // <-- not undefined
formStore.setup()
ES6 Class
class FormStore {
constructor(type) {
this.type = type;
this.store = {};
}
setup() {
console.log('setup', this.type)
}
checkVal() {
}
}
const formStore = new FormStore('bar')
console.log(formStore.store) // <-- not undefined
formStore.setup()
It has to do with scoping. Your $.each in checkVal has a normal function. Inside the function the scope if this is different. If you want to keep the original scope you could use a fat arrow function like you do when defining the methods.
this.checkVal= () => {
var geoArr = ['id_xx','myID', (...)];
var id;
$.each( geoArr, val => {
id = geoArr[val];
console.log(this.store) //-> returns undefined, below line is error
if (!(this.store[id])) {
return false;
}
});
}
When you run your original code and place a breakpoint on the line with console.log you can see in the inspector that this is set to the Window object and no longer points to your FormStore.
function FormStore () {
this.setup = function(){
this.store = {};
this.ERR_LINE_PREFIX = '#err_';
this.NO_DISPLAY_CLASS = 'no-display';
this.settings = {
'myID':{'hide':false},
}
}
this.checkVal= function(){
var geoArr = ['id_xx','myID'];
var id;
$.each( geoArr, function(val) {
id = geoArr[val];
console.log(this.store) //-> returns undefined, below line is error
if (!(this.store[id])) {
return false;
}
});
}
};
var FS = new FormStore();
FS.setup();
Works absolutely fine, the code you provided had a missing bracket and you were using some broken es6 syntax

Categories

Resources