I'm defining a class in javascript as
Class = (function() {
var privateFunction = function() { return "private"; }
return { publicFunction: function() { return privateFunction("public"); } };
)();
Here user can access Class.publicFunction, but not Class.privateFunction.
Now I want to provide the user an interface to extend this Class. So I added a public function extend.
Class = (function() {
var privateFunction = function() { return "private"; }
return {
publicFunction: function() { return privateFunction("public"); }
extend: function(source) {
dest=this;
for(var prop in source)dest[prop] = source[prop]
}
};
)();
My aim was to use the extend attribute as follows
Class.extend({
someFunc: function() { return privateFunction("hooray"); }
});
and access it as
Class.someFunc()
The problem I face is the call to the privateFunction() in the extended function someFunc is not available for it. I can understand that it is the problem of the scope, but, is there anyway to solve my need.
While it's a horrible violation of encapsulation, you could do what you describe by passing the function you want to add as a string and evaling it in extend:
Class.extend({
someFunc: 'function() { return privateFunction("hooray"); }'
});
and in the extend function, change
for(var prop in source)dest[prop] = source[prop]
to
for(var prop in source)dest[prop] = eval(source[prop])
this.before = function(){return "public"};
this.publicFucntion = function(){privateFunction(this.before());}
Then just override this.before.
Related
I'm not sure on the best approach to have object properties that are individual for each object in a OLOO inheritance chain.
Check this fiddle or consider the following code:
http://jsfiddle.net/HB7LU/19413/
Parent = {
array: [],
add: function(element) {
this.array.push(element + this.array.length.toString());
return this;
},
getAll: function() {
return this.array;
}
};
Child = Object.create(Parent, {
removeAllButOne: { value: function() {
this.array.splice(1);
return this;
}}
});
foo = Object.create(Parent);
foo.add('foo');
bar = Object.create(Child);
bar.add('bar');
In the fiddle a click on the foo or bar text will call the foo.add(...) or bar.add(...) function to add an element to the objects array, resulting in one extra <p> tag in the output.
The result is not what I want. Both foo and bar share the same array. But its easy to understand what happens, if we look up the object inheritance we can see the following:
Ok then, what can I do go get around this? There were two options that came to my mind:
Option 1)
http://jsfiddle.net/HB7LU/19419/
Parent = function() {
return {
array: [],
add: function(element) {
this.array.push(element + this.array.length.toString());
return this;
},
getAll: function() {
return this.array;
}
};
};
Child = Object.create(Parent(), {
removeAllButOne: { value: function() {
this.array.splice(1);
return this;
}}
});
foo = Object.create(Parent());
foo.add('foo');
bar = Object.create(Child);
bar.add('bar');
This would create a new Parent object, creating all the functions of the Parent object each time a Parent object is created or a child "inherits" from a (new) Parent object. While this solves the problem I had, it seems like a bad idea to always recreate the same functions over and over again for each child type object.
Option 2)
http://jsfiddle.net/HB7LU/19420/
Parent = Object.create({
add: function(element) {
this.array.push(element + this.array.length.toString());
return this;
},
getAll: function() {
return this.array;
}
}, {
ctor: { value: function(someArgs) {
this.array = [];
// maybe use someArgs
return this;
}}
});
Child = Object.create(Parent, {
removeAllButOne: { value: function() {
this.array.splice(1);
return this;
}}
});
foo = Object.create(Parent).ctor();
foo.add('foo');
bar = Object.create(Child).ctor();
bar.add('bar');
This seems to also solve the problem but avoids the recreation of the Parent object and its functions. So is this the way to go? What if I had multiple children in the inheritance chain that also have private properties?
Something like this?
Child = Object.create(Parent, {
ctor: { value: function(someArgs) {
this.__proto__.ctor(someArgs);
this.otherPrivate = {};
// maybe use someArgs
return this;
}},
removeAllButOne: { value: function() {
this.array.splice(1);
return this;
}}
});
Children would be shadowing the parent ctor with their own function... but in their ctor function they could call the parents ctor to not break functionality.
Thoughts and advice is highly appreciated, thanks!
Easiest way is to use Constructors so array is always created as an own property on the instance
// define Parent
function Parent() {
this.array = []; // array will be an instance property
}
Parent.prototype = {}; // inherit all the goodies from Object.prototype
Object.assign(Parent.prototype, { // using `Object.assign` for shorthand
add: function (element) {
this.array.push(element + this.array.length.toString());
return this;
},
getAll: function () {
return this.array;
}
});
// define Child
function Child() {
Parent.apply(this); // apply Parent constructor to the instance
}
Child.prototype = Object.create(Parent.prototype); // inherit Parent's prototype chain
Object.assign(Child.prototype, {
removeAllButOne: function () {
this.array.splice(1);
return this;
}
});
Now have
var a = new Child(),
b = new Child();
a.array === b.array; // false
You could also write this using ES 6's classes, but that is just syntactic sugar for what I've written above and will result in the same structures.
OLOO favours composition over inheritance. You could use a factory method pattern with Object.assign to compose objects with simple prototype delegation:
// Composable prototype objects, or "traits"
var base = {
add: function(element) {
this.array.push(element + this.array.length.toString());
return this;
},
getAll: function() {
return this.array;
}
};
var canRemoveAllButOne = {
removeAllButOne: function() {
this.array.splice(1);
return this;
}
}
// Factory functions
// You could think of these like external constructors
function createBase() {
return Object.assign({}, base, {
array: []
})
}
function createComposed() {
var base = createBase();
return Object.assign(base, canRemoveAllButOne)
}
// Test
function log(s) {
document.write(s + "<br>");
}
var b1 = createBase();
var b2 = createBase();
var c1 = createComposed();
var c2 = createComposed();
b1.add(1);
b1.add(2);
b2.add(9);
c1.add('a');
c2.add('b');
log(b1.getAll());
log(b2.getAll());
log(c1.getAll());
log(c2.getAll());
I have been trying to work out why my public methods do not appear to exist for my custom jQuery plugin object.
I have created a simplified version of the jQuery plugin with a private variable and two public methods to access/modify the private variable. I have looked online but the results are not that clear, that or my searching skills are terrible.
It keeps saying 'TypeError: myObject.getMyValue is not a function', anyone got a clue as to what I am doing wrong, or suggestions for a better approach to this?
The basic object class is below, but a proper code example can be found on the jsFiddle link.
(function ($) {
var MyClass = function (element) {
var myValue = 'Hello World';
this.getMyValue = function() {
return myValue;
};
this.setMyValue = function(value) {
myValue = value;
};
return this;
};
$.fn.myPlugin = function() {
return this.each(function(key, value){
if ($(this).data('myclass')) {
return $(this).data('myclass');
}
var instance = new MyClass(this);
$(this).data('myclass', instance);
return instance;
});
};
})(jQuery);
var myObject = $('#test').myPlugin();
alert(myObject.getMyValue());
myObject.setMyValue('Goodbye World');
alert(myObject.getMyValue());
http://jsfiddle.net/hZExb/4/
Because you're returning the result of this.each() which would be this. Create a variable outside of this.each() and return that after your this.each has completed.
jsFiddle
$.fn.myPlugin = function() {
var instance;
this.each(function(key, value){
if ($(this).data('myclass')) {
return $(this).data('myclass');
}
instance = new MyClass(this);
$(this).data('myclass', instance);
});
return instance;
};
If you wanted to return an array of MyClass's if your jQuery object was a collection you could do it like this:
jsFiddle
$.fn.myPlugin = function() {
var instances = [];
this.each(function(key, value){
if ($(this).data('myclass')) {
return $(this).data('myclass');
}
var instance = new MyClass(this);
$(this).data('myclass', instance);
instances.push(instance);
});
if (instances.length == 1)
return instances[0];
return instances;
};
In the course of an ExtJS 3 to 4 migration, I have run into a snag I'm wondering if anyone has tackled: namely, how can I extend a singleton with private scope?
In Ext3 I would do this using Extend:
Ext.namespace("My.New.Obj");
My.New.Obj = (function() {
var privateVar = 3;
function privateFunc() { alert(privateVar); }
var extendedObj = Ext.extend(My.Other.Obj, {
newFunc: function() { alert(this.publicVar+privateVar); },
publicVar: 4
});
return new extendedObj();
})();
As best I understand, I would create a singleton pattern using Ext.define but I don't know how to extend the internal object.
Ext.define('My.New.Obj', function() {
var privateVar = 3;
function privateFunc() { alert(privateVar); }
var extendedObj = Ext.create('My.Old.Obj',{
newFunc: function() { alert(this.publicVar+privateVar); },
publicVar: 4
});
return extendedObj;
});
The only trouble with the above example is that I believe the super methods that were preserved in Ext.extend get overridden.
How can I extend a singleton while keeping private scope?
Ext.define('A', {
someMethod: function(){
return 'a';
}
});
Ext.define('B', (function(){
var fn = function(){
return 'b';
};
return {
extend: 'A',
someMethod: function(){
return this.callParent() + fn();
}
}
})());
console.log(new A().someMethod());
console.log(new B().someMethod());
Is it possible to have private properties in a model? Like the locally declared variables in a (constructor) function, not attached to this, but declared locally and visible only by whatever is defined in the (constructor)function.
Example without BB View:
function MyView(aModel){
var $internalInput = $('<input>');
this.render: function($where){
$internalInput.val(aModel.get('SomeProperty'));
$where.append($('<div class="inputWraper">').append($internalInput));
};
this.toggleReadonly: function() {
toggle $internalInputs readonly attribute
}
...
+ Code to bind input.val to some aModel property(ies) and setup events
...
}
Note that internalInput is not accessible to outside world and aModel is also not accessible (through MyView at least).
So if I want to use Backbone.View to implement the above MyView, how would i do it and keep $internalInput 'private'?
You should be able to achieve private data by passing an IIFE to extend when defining your Backbone objects, rather than just a plain object. For example:
var Thing = Backbone.Model.extend((function () {
var foo = "Private data!";
return {
bar: function () {
console.log(foo);
}
};
})());
You'd better off with
var Thing = Backbone.Model.extend(
{
constructor : function ()
{
var _value = "Private data!";
this.getValue = function ()
{
return _value;
};
this.setValue = function (value)
{
_value = value;
};
}
});
Javascript is fun!
var Thing = (function () {
var number_of_things = 0;
return function (options) {
var value = "Private data!";
return new ( Backbone.Model.extend({
constructor: function constructor () {
number_of_things += 1;
},
getValue: function getValue () {
return value;
}
}) )();
};
}());
I'm a little concerned by the fact that every instance of this "Thing" is also a subclass, in the OOP lingo.
In the context of using Broserify.js with Backbone (and really any above medium project) I found the following way to have private vars and functions:
myView.js
'use strict';
var config = require('../config.js'),
private_var = 'private variable',
my_private_fn = function() {
...
};
module.exports = Backbone.Model.extend({
initialize: function() {
this.my_public = 'public variable');
console.log('This is my' + this.my_public);
console.log('This is my' + my_private);
},
});
The idea to take here is go with Browserify :P
The simplest way is the following:
...
initialize:function(properites){
// Init the logic with private and public methods/variable
this.logic.initFirst(this);
// Use public methods
this.logic.doSomething();
},
logic:{
initFirst:function(modelOrView){
// Do not continue if already initiated
if( this.instance !== undefined ) return;
// Write all logic here
this.instance = (function(logic, modelOrView){
// Private variables
var private = "private";
// Public methods
logic.doSomething = function(){
console.log(private, modelOrView);
};
// Private methods
function hidden(){
}
}(this, modelOrView));
}
},
I have tree of closures: 'A' containing private closures 'pancake' and 'B'. There is a situation when I need to call from inside of 'B' public function, the private closure of 'A' - 'pancake' and retrieve its public properity. How can I do it? Oh, and this is useless, as this is not an object.
My code:
var A = (function() {
var pancake = (function() {
return {
numeric: 142
};
})(A);
var B = (function() {
return {
init: function(name) {
console.log(pancake.numeric);
//How to access the same element using 'name' variable?
}
};
})(A);
return {
init: function() {
B.init('pancake');
}
};
})();
A.init();
JSFiddle might show more details: http://jsfiddle.net/yALkY/3/
Thanks in advance
Though I have to aggree with jfriend00 that the given code is over-complicating things, one solution would be to introduce some map to store references in, like:
var A = (function() {
var pancake = (function() {
return {
numeric: 142
};
})();
var B = (function() {
return {
init: function(name) {
console.log(privateVars[name].numeric);
//How to access the same element using 'name' variable?
}
};
})();
// added:
var privateVars = {
pancake: pancake
};
return {
init: function() {
B.init('pancake');
}
};
})();
A.init();
The drawback, of course, is that you'll have to maintain that list manually.