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);
Related
I've got two object prototypes like this:
function Tag(name, description) {
this.name = name;
this.description = description || null;
}
function Category(name, description) {
this.name = name;
this.description = description || null;
}
Both of them are exactly the same, which seems awkward. Is it possible to merge them both into an object named 'Entity', and refer to them both by different names (the original 'Tag' and 'Category')?
This may be further complicated by the fact I need to refer to the current prototype name inside the prototype.
Tag.prototype.toJSON = function() {
return {
__type: 'Tag',
name: this.name,
description: this.description
};
};
How can I apply the same 'toJSON' extension to the 'Entity' object, but make sure it returns 'Tag' or 'Category' in the '__type' field, dependent on which object is being used?
I would do something like this:
Dummy = function () {};
Entity = function (name) {
this.name = name;
};
Entity.prototype.toString = function () {
return "My name is " + this.name + ".";
};
A = function () {
Entity.call(this, 'A');
};
Dummy.prototype = Entity.prototype;
Dummy.prototype.constructor = A;
A.prototype = new Dummy();
B = function () {
Entity.call(this, 'B');
};
Dummy.prototype = Entity.prototype;
Dummy.prototype.constructor = B;
B.prototype = new Dummy();
document.body.innerHTML = ""
+ (new A()) + "<br />"
+ (new B());
Here is a small function to make things cleaner (hopefully):
function Nothing () {};
function extend (Sup, proto) {
function Class () {
if (this.init) {
this.init.apply(this, arguments);
}
}
Nothing.prototype = Sup.prototype;
Nothing.prototype.constructor = Sup;
Class.prototype = new Nothing();
delete Nothing.prototype;
for (var k in proto) {
Class.prototype[k] = proto[k];
}
return Class;
}
Here is how to use it:
Entity = extend(Nothing, {
init: function (name) {
this.name = name;
},
toString: function () {
return "My name is " + this.name + ".";
}
});
A = extend(Entity, {
init: function () {
var sup = Entity.prototype;
sup.init.call(this, 'A');
}
});
B = extend(Entity, {
init: function () {
var sup = Entity.prototype;
sup.init.call(this, 'B');
}
});
I'm constructing a Builder in JavaScript, and I'm not sure how builders normally handle undefined values for optionals. I would like to think that the Builder doesn't append the optional field to the object if the field is undefined. Is that acceptable for a Builder? If not, what would be alternatives?
Here is a sample of the first implementation where the builder doesn't append an undefined optional:
Builder:
function Thing(required1, required2, required3) {
//check required params
var fields = {
required1: required1,
required2: required2,
required3: required3
};
var _withOptionalParam = function(param) {
if(!param) { return this; } //exit function early if param is undefined
fields.param = param;
return this;
};
var _build = function() {
var result = fields;
return result;
};
var builder = {
withOptionalParam: _withOptionalParam,
build: _build
};
return builder;
}
In action:
var thing = new Thing("foo","bar","baz").withOptionalParam(undefined).build();
//'thing' should be
// {
// required1:"foo",
// required2:"bar",
// required3:"baz"
// };
//
Thanks in advance!
I think you are losing the context of this in your _withOptinalParam function. You could bind you fields object to it as the this context.
function Thing(required1, required2, required3) {
//check required params
var fields = {
required1: required1,
required2: required2,
required3: required3
};
var _withOptionalParam = function(param) {
if(!param) { return this; } //exit function early if param is undefined
fields.param = param;
return this;
};
var _build = function() {
var result = fields;
return result;
};
var builder = {
withOptionalParam: _withOptionalParam.bind(fields),
build: _build
};
return builder;
}
var thing = new Thing("foo","bar","baz").withOptionalParam(undefined);
console.log( thing );
//'thing' should be
// {
// required1:"foo",
// required2:"bar",
// required3:"baz"
// };
//
<script src="http://codepen.io/synthet1c/pen/WrQapG.js"></script>
I have a sealed object with an array member on which I want to prevent direct pushes.
var myModule = (function () {
"use strict";
var a = (function () {
var _b = {},
_c = _c = "",
_d = [];
Object.defineProperty(_b, "c", {
get: function () { return _c; }
});
Object.defineProperty(_b, "d", {
get { return _d; }
});
_b.addD = function (newD) {
_d.push(newD);
};
Object.seal(_b);
return _b;
}());
var _something = { B: _b };
return {
Something: _something,
AddD: _b.addD
};
}());
myModule.Something.c = "blah"; // doesn't update = WIN!!
myModule.AddD({}); // pushed = WIN!
myModule.Something.d.push({}); // pushed = sadness
How can I prevent the push?
UPDATE:
Thanks for all the thoughts. I eventually need the JSON to send to the server. It looks like I might need to use an object for the array then figure out a way to generate and return the JSON needed, or change _something to use .slice(). Will play and report.
you could override the push method:
var _d = [];
_d.__proto__.push = function() { return this.length; }
and when you need to use it in your module, call Array.prototype.push:
_b.addD = function (newD) {
Array.prototype.push.call(_d, newD);
};
I haven't done any performance tests on this, but this certainly helps to protect your array.
(function(undefined) {
var protectedArrays = [];
protectArray = function protectArray(arr) {
protectedArrays.push(arr);
return getPrivateUpdater(arr);
}
var isProtected = function(arr) {
return protectedArrays.indexOf(arr)>-1;
}
var getPrivateUpdater = function(arr) {
var ret = {};
Object.keys(funcBackups).forEach(function(funcName) {
ret[funcName] = funcBackups[funcName].bind(arr);
});
return ret;
}
var returnsNewArray = ['Array.prototype.splice'];
var returnsOriginalArray = ['Array.prototype.fill','Array.prototype.reverse','Array.prototype.copyWithin','Array.prototype.sort'];
var returnsLength = ['Array.prototype.push','Array.prototype.unshift'];
var returnsValue = ['Array.prototype.shift','Array.prototype.pop'];
var funcBackups = {};
overwriteFuncs(returnsNewArray, function() { return []; });
overwriteFuncs(returnsOriginalArray, function() { return this; });
overwriteFuncs(returnsLength, function() { return this.length; });
overwriteFuncs(returnsValue, function() { return undefined; });
function overwriteFuncs(funcs, ret) {
for(var i=0,c=funcs.length;i<c;i++)
{
var func = funcs[i];
var funcParts = func.split('.');
var obj = window;
for(var j=0,l=funcParts.length;j<l;j++)
{
(function() {
var part = funcParts[j];
if(j!=l-1) obj = obj[part];
else if(typeof obj[part] === "function")
{
var funcBk = obj[part];
funcBackups[funcBk.name] = funcBk;
obj[part] = renameFunction(funcBk.name, function() {
if(isProtected(this)) return ret.apply(this, arguments);
else return funcBk.apply(this,arguments);
});
}
})();
}
}
}
function renameFunction(name, fn) {
return (new Function("return function (call) { return function " + name +
" () { return call(this, arguments) }; };")())(Function.apply.bind(fn));
};
})();
You would use it like so:
var myArr = [];
var myArrInterface = protectArray(myArr);
myArr.push(5); //Doesn't work, but returns length as expected
myArrInterface.push(5); //Works as normal
This way, you can internally keep a copy of the interface that isn't made global to allow your helper funcs to modify the array as normal, but any attempt to use .push .splice etc will fail, either directly, or using the .bind(myArr,arg) method.
It's still not completely watertight, but a pretty good protector. You could potentially use the Object.defineProperty method to generate protected properties for the first 900 indexes, but I'm not sure of the implications of this. There is also the method Object.preventExtensions() but I'm unaware of a way to undo this effect when you need to change it yourself
Thank you, dandavis!
I used the slice method:
var myModule = (function () {
"use strict";
var a = (function () {
var _b = {},
_c = _c = "",
_d = [];
Object.defineProperty(_b, "c", {
get: function () { return _c; }
});
Object.defineProperty(_b, "d", {
get { return _d.slice(); } // UPDATED
});
_b.updateC = function (newValue) {
_c = newValue;
};
_b.addD = function (newD) {
_d.push(newD);
};
Object.seal(_b);
return _b;
}());
var _something = { B: _b };
return {
Something: _something,
AddD: _b.addD
};
}());
myModule.Something.c = "blah"; // doesn't update = WIN!!
myModule.AddD({}); // pushed = WIN!
myModule.Something.d.push({}); // no more update = happiness
This allows me to protect from direct push calls enforcing some logic.
i'm practicing with Javascript Inheritance, my first try is following code:
var base_class = function()
{
var _data = null;
function _get() {
return _data;
}
this.get = function() {
return _get();
}
this.init = function(data) {
_data = data;
}
}
var new_class = function() {
base_class.call(this);
var _data = 'test';
function _getData() {
return this.get();
}
this.getDataOther = function() {
return _getData();
}
this.getData = function() {
return this.get();
}
this.init(_data);
}
new_class.prototype = base_class.prototype;
var instance = new new_class();
alert(instance.getData());
alert(instance.getDataOther());
to that point i am really happy with my solution, but there is one problem
that i dont get resolved.
the "getDataOther" method don`t return the stored data from the base class,
because i cannot access the public "get" class from the protected "_getData" method in the new_class.
How can i get that running ?
Thanks in advance.
Ps.: Please excuse my poor English
If you comment out the this.init function (which overwrites the base_class _data field) and make the new_class's getData function just return _data, you should be able to get distinct variables.
var base_class = function()
{
var _data = null;
function _get() {
return _data;
}
this.get = function() {
return _get();
}
this.init = function(data) {
_data = data;
}
}
var new_class = function() {
var self = this; //Some browsers require a separate this reference for
//internal functions.
//http://book.mixu.net/ch4.html
base_class.call(this);
var _data = 'test';
function _getData() {
return self.get();
}
this.getDataOther = function() {
return _getData();
}
this.getData = function() {
return _data; //Changed this line to just return data
//Before, it did the same thing as _getData()
}
//this.init(_data); //Commented out this function (it was changing the base_class' data)
}
new_class.prototype = base_class.prototype;
var instance = new new_class();
alert(instance.getData());
alert(instance.getDataOther());
Your english is fine by the way :)
In Douglas Crockford's JavaScript: The Good Parts he recommends that we use functional inheritance. Here's an example:
var mammal = function(spec, my) {
var that = {};
my = my || {};
// Protected
my.clearThroat = function() {
return "Ahem";
};
that.getName = function() {
return spec.name;
};
that.says = function() {
return my.clearThroat() + ' ' + spec.saying || '';
};
return that;
};
var cat = function(spec, my) {
var that = {};
my = my || {};
spec.saying = spec.saying || 'meow';
that = mammal(spec, my);
that.purr = function() {
return my.clearThroat() + " purr";
};
that.getName = function() {
return that.says() + ' ' + spec.name + ' ' + that.says();
};
return that;
};
var kitty = cat({name: "Fluffy"});
The main issue I have with this is that every time I make a mammal or cat the JavaScript interpreter has to re-compile all the functions in it. That is, you don't get to share the code between instances.
My question is: how do I make this code more efficient? For example, if I was making thousands of cat objects, what is the best way to modify this pattern to take advantage of the prototype object?
Well, you just can't do it that way if you plan on making lots of mammal or cat. Instead do it the old fashioned way (prototype) and inherit by property. You can still do the constructors the way you have above but instead of that and my you use the implicit this and some variable representing the base class (in this example, this.mammal).
cat.prototype.purr = function() { return this.mammal.clearThroat() + "purr"; }
I'd use another name than my for base access and store it in this in the cat constructor. In this example I used mammal but this might not be the best if you want to have static access to the global mammal object. Another option is to name the variable base.
Let me introduce you to Classical Inheritance that never uses prototype. This is a bad coding exercise but will teach you the real Classical Inheritance which always compared to prototypal inheritance:
Make a custructor:
function Person(name, age){
this.name = name;
this.age = age;
this.sayHello = function(){return "Hello! this is " + this.name;}
}
Make another cunstructor that inherits from it:
function Student(name, age, grade){
Person.apply(this, [name, age]);
this.grade = grade
}
Very simple! Student calls(applies) Person on itself with name and age arguments takes care of grade arguments by itself.
Now lets make an instance of Student.
var pete = new Student('Pete', 7, 1);
Out pete object will now contain name, age, grade and sayHello properties. It owns all those properties. They are not uplinked to Person through prototype. If we change Person to this:
function Person(name, age){
this.name = name;
this.age = age;
this.sayHello = function(){
return "Hello! this is " + this.name + ". I am " this.age + " years old";
}
}
pete will no recieve the update. If we call pete.sayHello, ti will return Hello! this is pete. It will not get the new update.
if you want privacy and you dont like protyping you may or may-not like this approach:
(note.: it uses jQuery.extend)
var namespace = namespace || {};
// virtual base class
namespace.base = function (sub, undefined) {
var base = { instance: this };
base.hierarchy = [];
base.fn = {
// check to see if base is of a certain class (must be delegated)
is: function (constr) {
return (this.hierarchy[this.hierarchy.length - 1] === constr);
},
// check to see if base extends a certain class (must be delegated)
inherits: function (constr) {
for (var i = 0; i < this.hierarchy.length; i++) {
if (this.hierarchy[i] == constr) return true;
}
return false;
},
// extend a base (must be delegated)
extend: function (sub) {
this.hierarchy.push(sub.instance.constructor);
return $.extend(true, this, sub);
},
// delegate a function to a certain context
delegate: function (context, fn) {
return function () { return fn.apply(context, arguments); }
},
// delegate a collection of functions to a certain context
delegates: function (context, obj) {
var delegates = {};
for (var fn in obj) {
delegates[fn] = base.fn.delegate(context, obj[fn]);
}
return delegates;
}
};
base.public = {
is: base.fn.is,
inherits: base.fn.inherits
};
// extend a sub-base
base.extend = base.fn.delegate(base, base.fn.extend);
return base.extend(sub);
};
namespace.MyClass = function (params) {
var base = { instance: this };
base.vars = {
myVar: "sometext"
}
base.fn = {
init: function () {
base.vars.myVar = params.myVar;
},
alertMyVar: function() {
alert(base.vars.myVar);
}
};
base.public = {
alertMyVar: base.fn.alertMyVar
};
base = namespace.base(base);
base.fn.init();
return base.fn.delegates(base,base.public);
};
newMyClass = new namespace.MyClass({myVar: 'some text to alert'});
newMyClass.alertMyVar();
the only downside is that because of the privacy scope you can only extend the virtual classes and not the instanceable classes.
here is an example of how i extend the namespace.base, to bind/unbind/fire custom events.
// virtual base class for controls
namespace.controls.base = function (sub) {
var base = { instance: this };
base.keys = {
unknown: 0,
backspace: 8,
tab: 9,
enter: 13,
esc: 27,
arrowUp: 38,
arrowDown: 40,
f5: 116
}
base.fn = {
// bind/unbind custom events. (has to be called via delegate)
listeners: {
// bind custom event
bind: function (type, fn) {
if (fn != undefined) {
if (this.listeners[type] == undefined) {
throw (this.type + ': event type \'' + type + '\' is not supported');
}
this.listeners[type].push(fn);
}
return this;
},
// unbind custom event
unbind: function (type) {
if (this.listeners[type] == undefined) {
throw (this.type + ': event type \'' + type + '\' is not supported');
}
this.listeners[type] = [];
return this;
},
// fire a custom event
fire: function (type, e) {
if (this.listeners[type] == undefined) {
throw (this.type + ': event type \'' + type + '\' does not exist');
}
for (var i = 0; i < this.listeners[type].length; i++) {
this.listeners[type][i](e);
}
if(e != undefined) e.stopPropagation();
}
}
};
base.public = {
bind: base.fn.listeners.bind,
unbind: base.fn.listeners.unbind
};
base = new namespace.base(base);
base.fire = base.fn.delegate(base, base.fn.listeners.fire);
return base.extend(sub);
};
To proper use Javascript-prototype based inheritance you could use fastClass https://github.com/dotnetwise/Javascript-FastClass
You have the simpler inheritWith flavor:
var Mammal = function (spec) {
this.spec = spec;
}.define({
clearThroat: function () { return "Ahem" },
getName: function () {
return this.spec.name;
},
says: function () {
return this.clearThroat() + ' ' + spec.saying || '';
}
});
var Cat = Mammal.inheritWith(function (base, baseCtor) {
return {
constructor: function(spec) {
spec = spec || {};
baseCtor.call(this, spec);
},
purr: function() {
return this.clearThroat() + " purr";
},
getName: function() {
return this.says() + ' ' + this.spec.name + this.says();
}
}
});
var kitty = new Cat({ name: "Fluffy" });
kitty.purr(); // Ahem purr
kitty.getName(); // Ahem Fluffy Ahem
And if you are very concerned about performance then you have the fastClass flavor:
var Mammal = function (spec) {
this.spec = spec;
}.define({
clearThroat: function () { return "Ahem" },
getName: function () {
return this.spec.name;
},
says: function () {
return this.clearThroat() + ' ' + spec.saying || '';
}
});
var Cat = Mammal.fastClass(function (base, baseCtor) {
return function() {
this.constructor = function(spec) {
spec = spec || {};
baseCtor.call(this, spec);
};
this.purr = function() {
return this.clearThroat() + " purr";
},
this.getName = function() {
return this.says() + ' ' + this.spec.name + this.says();
}
}
});
var kitty = new Cat({ name: "Fluffy" });
kitty.purr(); // Ahem purr
kitty.getName(); // Ahem Fluffy Ahem
Btw, your initial code doesn't make any sense but I have respected it literally.
fastClass utility:
Function.prototype.fastClass = function (creator) {
var baseClass = this, ctor = (creator || function () { this.constructor = function () { baseClass.apply(this, arguments); } })(this.prototype, this)
var derrivedProrotype = new ctor();
if (!derrivedProrotype.hasOwnProperty("constructor"))
derrivedProrotype.constructor = function () { baseClass.apply(this, arguments); }
derrivedProrotype.constructor.prototype = derrivedProrotype;
return derrivedProrotype.constructor;
};
inheritWith utility:
Function.prototype.inheritWith = function (creator, makeConstructorNotEnumerable) {
var baseCtor = this;
var creatorResult = creator.call(this, this.prototype, this) || {};
var Derrived = creatorResult.constructor ||
function defaultCtor() {
baseCtor.apply(this, arguments);
};
var derrivedPrototype;
function __() { };
__.prototype = this.prototype;
Derrived.prototype = derrivedPrototype = new __;
for (var p in creatorResult)
derrivedPrototype[p] = creatorResult[p];
if (makeConstructorNotEnumerable && canDefineNonEnumerableProperty) //this is not default as it carries over some performance overhead
Object.defineProperty(derrivedPrototype, 'constructor', {
enumerable: false,
value: Derrived
});
return Derrived;
};
define utility:
Function.prototype.define = function (prototype) {
var extendeePrototype = this.prototype;
if (prototype)
for (var p in prototype)
extendeePrototype[p] = prototype[p];
return this;
}
[* Disclaimer, I am the author of the open source package and the names of the methods themselves might be renamed in future` *]