I am working on trying to make an JS object and access to private methods. The problem I am running into when trying to return a function is the private methods cannot be accessed. Code Below.
var Item = (function() {
var price = 0;
var name = '';
var description = '';
var quantity = '';
var attributes = {};
var Item = function(data) {
}
function setPrice(variable) {
this.price = variable;
};
function getPrice() {
return this.price;
};
function setName(variable) {
this.name = variable;
};
function getName() {
return this.name;
};
function setDescription(variable) {
this.description = variable;
};
function setQuantity(variable) {
this.quanity = variable;
};
return function(data){
setPrice : setPrice;
getPrice : getPrice;
setName : setName;
setDescription : setDescription;
setQuantity : setQuantity;
return new Item(data);
}
})();
item2 = Item();
item2.setPrice('3');
alert(item2.getPrice());
With this setup, how can I access the private methods?
I don't think that pattern will work for what you're trying to do. I think using a pattern like this one will keep your code smaller and more reusable. This way you also get rid of the set functions.
var Item = function(options) {
var opts = $.extend({
price: 0,
name: '',
description: '',
quantity: '',
attributes: {}
}, options);
// ...
this.getPrice = function() {
return opts.price;
};
// ...
};
var item = new Item({
price: 100,
name: 'onehundred',
// ...
});
alert(item.getPrice());
Fixed your code here: http://jsfiddle.net/pratik136/JryAk/
Items changed:
Check your return statement
Item is a var, you were trying to instantiate a class object item2
Related
I'm trying to implement the Builder Pattern to generate a JSON string of options that are passed into a library to generate widgets. I can't understand why at console.log below that this.options is undefined.
let Options = function(options) {
this.options = options;
}
let OptionsObjectBuilder = function () {
let options;
return {
addConstantLineToValueAxis: function (lineValue) {
console.log(this.options); // EQUALS UNDEFINED SO CAN'T ADD TO THIS OBJECT
this.options.valueAxis.constantLine.value = lineValue;
return this;
},
build: function () {
return new Options(this.options);
}
};
};
let option = new OptionsObjectBuilder().addConstantLineToValueAxis(1000000000).build();
There are two different ways for the builder to store the temporary states:
In the builder object itself (by setting this.options =)
In a closure (by setting options =)
The closure example has the benefit that the temporary builder state is not accessible to the outside.
You can use either way, as long as the builder uses them from the correct place. I will fix the broken example from the post you mentioned. I think they started using closures, and it didn't work because the param name was shadowing the closure variable and they ended up getting confused switching to using this instead. They forgot to update their build() function to read from the correct place.
Using builder object state - Exposes internal state
let Task = function(name, description, finished, dueDate) {
this.name = name;
this.description = description;
this.finished = finished;
this.dueDate = dueDate;
}
let TaskBuilder = function () {
return {
setName: function (name) {
this.name = name;
return this;
},
setDescription: function (description) {
this.description = description;
return this;
},
setFinished: function (finished) {
this.finished = finished;
return this;
},
setDueDate: function (dueDate) {
this.dueDate = dueDate;
return this;
},
build: function () {
return new Task(this.name, this.description, this.isFinished, this.dueDate);
}
};
};
let builder = new TaskBuilder().setName('Task A').setDescription('finish book')
.setDueDate(new Date(2019, 5, 12));
let task = builder.build();
// Notice the builder does expose the name/description... properties
console.log({builder, task});
Using closure variables - Hides internal state
let Task = function(name, description, finished, dueDate) {
this.name = name;
this.description = description;
this.finished = finished;
this.dueDate = dueDate;
}
let TaskBuilder = function () {
let name;
let description;
let isFinished = false;
let dueDate;
return {
setName: function (pName) {
name = pName;
return this;
},
setDescription: function (pDescription) {
description = pDescription;
return this;
},
setFinished: function (pFinished) {
finished = pFinished;
return this;
},
setDueDate: function (pDueDate) {
dueDate = pDueDate;
return this;
},
build: function () {
return new Task(name, description, isFinished, dueDate);
}
};
};
let builder = new TaskBuilder().setName('Task A').setDescription('finish book')
.setDueDate(new Date(2019, 5, 12));
let task = builder.build();
// Can't see the name/description... properties on the builder, just the methods
console.log({builder, task});
I believe I should only ever use this when returning at the end of the add functions in the builder pattern. I'm still not sure why in the example (zetcode.com/javascript/builderpattern) I was basing my code off of.. they set values with this.name, but passed name in their build function.
// #Steven de Salas: expando function: https://stackoverflow.com/a/44014709/1432612
function buildObjectDepth(obj, base) {
return Object.keys(obj)
.reduce((clone, key) => {
key.split('.').reduce((innerObj, innerKey, i, arr) =>
innerObj[innerKey] = (i+1 === arr.length) ? obj[key] : innerObj[innerKey] || {}, clone)
return clone;
}, Object.assign({}, base));
}
let Options = function(options) {
this.options = options;
}
let OptionsObjectBuilder = function () {
let options = {};
return {
addConstantLineToValueAxis: function (lineValue) {
options = buildObjectDepth({"valueAxis.constantLine.value": lineValue}, options);
return this;
},
build: function () {
return new Options(options);
}
};
};
let obj = new OptionsObjectBuilder().addConstantLineToValueAxis(1000000000).build();
str = JSON.stringify(obj);
str = JSON.stringify(obj, null, 4); // indented output.
alert(str);
This is my code :
export class CustomToast implements OnInit {
toastTypeGroup: string;
message: string;
title: string;
showDuration= "300";
constructor(_toastTypeGroup: string, _message:string, _title: string ){
this.toastTypeGroup = _toastTypeGroup;
this.message = _message;
this.title = _title;
}
ngOnInit() {
**//this is the solution**
**var _this = this;**
console.log(this.showDuration);
var ToastrDemo = function () {
var toastr: any = (<any>window).toastr;
var k, m = -1, f = 0;
var t = function () {
var t, o,
//toastTypeGroup
e = this.toastTypeGroup,
//message
n = this.message,
//title
a = this.title,
//showDuration
i = this.showDuration,
};
return { init: function () { t() } }
}();jQuery(document).ready(function () { ToastrDemo.init() });
}
}
My problem is that I want to read members values from my class called CustomToast inside my JavaScript function called var t = function () {...}.
I can't get typescript class member value inside a JavaScript Function.
e.g "toastTypeGroup" member is not accessible iside my fucntion
This is the big problem.
Thanks in advance.
Move
var _this = this;
to the beginning of ngOnInit and use _this.showDuration or use bind(this)
Not sure I understood the question but wouldn't you just add .bind(this) to your definition of t?
var t = function () {
var t, o,
//toastTypeGroup
e = this.toastTypeGroup,
//message
n = this.message,
//title
a = this.title,
//showDuration
i = this.showDuration,
}.bind(this);
try
/** #type {your type} */
var _this = this;
https://www.typescriptlang.org/docs/handbook/type-checking-javascript-files.html
My function gets model name as string, I need to create new instance of object based on its name.
ex.:
modelName = 'MockA';
model = new modelName();
this is ofcourse not working. in php i would use
model = new $$modelName
thanks in advance
If MockA is in global scope you can use:
var model = new window[modelName]();
if not then you should reconsider the way you store your models, eg. with an object of models:
var my_models = {
MockA: function() {},
MockB: function() {}
}
and to access
var MockA = my_models.MockA;
// or
var model_name = 'MockA';
var MockA = my_models[model_name];
You can use an object factory or bracket notation.
Sample of code:
// First example: Use a Factory
var MockA = function() {
this.sayHello = function() {
console.log('Hi from MockA ');
};
},
MockB = function() {
this.sayHello = function() {
console.log('Hi from MockB ');
}
},
factory = function(type) {
var obj;
switch (type) {
case 'MockA':
obj = new MockA();
break;
case 'MockB':
obj = new MockB();
break;
}
return obj;
}
var objA = factory('MockA');
objA.sayHello();
var objB = factory('MockB');
objB.sayHello();
// Second example: Using bracket notation
var models = {
BaseMockA: {
sayHello: function() {
console.log('Hi from BaseMockA ');
}
},
BaseMockB: {
sayHello: function() {
console.log('Hi from BaseMockB ');
}
}
};
var baseObjA = Object.create(models['BaseMockA']);
baseObjA.sayHello();
var baseObjB = Object.create(models['BaseMockB']);
baseObjB.sayHello();
Im struggling with something quite simply in javascript. I want to have an array which is modifiable only through methods of an object. Consider the following example:
var Cart = function() {
this.items = [];
}
Cart.prototype.getItems = function() {
return this.items;
}
Cart.prototype.addItem = function(item) {
this.items.push(item);
}
module.exports = Cart;
I want to be able to add new items through the addItem method and to retrieve the items through the getItems method. I dont want to be able to just do Cart.items.push(item) for example.
How can i achieve this?
With ES6 WeakMap you can do it like this:
var items = new WeakMap();
var Cart = function() {
items[this] = [];
};
Cart.prototype.getItems = function() {
return items[this];
};
Cart.prototype.addItem = function(item) {
items[this].push(item);
};
module.exports = Cart;
In javascript you don't have private properties. Here are some options:
1 - call property _items. That's a well known convention for naming private properties;
2 - use closure:
var Cart = function() {
var items = [];
this.getItems = function() {
return items;
};
this.addItem = function(item) {
items.push(item);
};
}
3 - use symbols. They are not truly private, but hard to discover:
var items = Symbol();
var Cart = function() {
this[items] = [];
}
Cart.prototype.getItems = function() {
return this[items];
}
Cart.prototype.addItem = function(item) {
this[items].push(item);
}
4 - use private-symbol module instead. This will work only in node.js/io.js (uses v8 C++ API), but gives you truly private symbols
5 - use WeakMap
var items = new WeakMap();
var Cart = function() {
items.set(this, []);
}
Cart.prototype.getItems = function() {
return items.get(this);
}
Cart.prototype.addItem = function(item) {
items.get(this).push(item);
}
Will this meet your requirements?
var Cart = function() {
var items = [];
this.getItems = function() {
return items;
};
this.addItem = function(item) {
items.push(item);
};
}
You can achieve that functionality by leveraging the scope of your constructor.
Instead of defining the array as a public property, you define it as a variable:
var Cart = function() {
var items = [];
this.getItems = function() {
return items;
}
this.addItem = function(item) {
items.push(item);
}
this.addAndRetrieve = function(item) {
items.push(item);
return items;
}
}
module.exports = Cart;
Then, you can access it through the publicly exposed methods getItems and addItem
var x = new Cart(),
y = new Cart();
x.addItem(1);
y.addItem(2);
x.getItems(); // [1]
y.getItems(); // [2]
EDIT1: If these are the only 2 methods that you want to chain on your instance, you can simply combine them in a new function and achieve the combined functionality.
I have a 'model' class/prototype defined as below, it has subclass named 'given' which tries to access method 'getNodes()' of model class.
But it gives exception for 'this.getNodes' saying undefined.
var model = {
constructor: function(/*string*/ mode, /*string*/ name, /*object*/ properties) {
this._mode = mode;
this.beginX = 100;
this.beginY = 100;
this.nodeWidth = 200;
this.nodeHeight = 200;
this.x = this.beginX;
this.y = this.beginY;
this.lastNodeVisible = null;
this.ID = 1;
this.taskName = name;
this.properties = properties;
this.checkedNodes = new Array();
// this.model = #call_build_method;
/*
add subclasses with model accessors
*/
this.given = {
getNodes: this.getNodes,
setNodeName: this.setNodeName
};
},
getNodes: function() {
// Summary: returns an array containing the nodes in the given model
return #someobject;
},
}
I assume that you want to call a method in the parent class with the correct scope.
Here are two ways to do this, one using dojo hitch, and one without:
require([
"dojo/_base/lang"
],function(lang){
model = function(){
var obj = {
data: "ok",
getData4: function(){
return this.data;
}
};
obj.sub = {
getData5: lang.hitch(obj, obj.getData4),
getData6: function(){return obj.getData4.apply(obj,arguments);}
};
return obj;
};
m = new model();
console.log("call getData4: ", m.getData4()); // returns "ok"
console.log("call getData5: ", m.sub.getData5()); // returns "ok"
console.log("call getData6: ", m.sub.getData6()); // returns "ok"
});
You need to store this in variable in outter scope:
this.model = <SOMETHING>;
var self = this;
this.given = {
getNodes: function(){self.getNodes(self.model);}
// inside a function this is this.given
};