Problem accessing private variables in jQuery like chainable design pattern - javascript

I'm trying to create my custom toolbox which imitates jQuery's design pattern. Basically, the idea is somewhat derived from this post: jQuery plugin design pattern (common practice?) for dealing with private functions (Check the answer given by "David").
So here is my toolbox function:
(function(window){
var mySpace=function(){
return new PrivateSpace();
}
var PrivateSpace=function(){
var testCache={};
};
PrivateSpace.prototype={
init:function(){
console.log('init this:', this);
return this;
},
ajax:function(){
console.log('make ajax calls here');
return this;
},
cache:function(key,selector){
console.log('cache selectors here');
testCache[key]=selector;
console.log('cached selector: ',testCache);
return this;
}
}
window.hmis=window.m$=mySpace();
})(window)
Now, if I execute this function like:
console.log(m$.cache('firstname','#FirstNameTextbox'));
I get an error 'testCache' is not defined. I'm not able to access the variable "testCache" inside my cache function of the prototype. How should I access it? Basically, what I want to do is, I want to cache all my jQuery selectors into an object and use this object in the future.

testCache is hidden in the closure that new PrivateSpace creates.
The correct pattern to use here is
var PrivateSpace=function(){
this.testCache={};
};
PrivateSpace.prototype={
cache:function(key,selector){
this.testCache[key]=selector;
return this;
}
}
The essential part here is this.
But the entire piece of code seems a bit contrived - there is no reason to use a prototype pattern when only a single instance is to be created. You should instead rely on variables accessible through a shared scope (closure).
(function(window)(
var cache = {}, mylib;
window.mylib = mylib = {
cache: function(key, selector) {
cache[key] = selector;
return mylib;
}
}
})(window);
Update
And by the way, do not follow the jQuery pattern without having a real reason to do so ;)
Update
A better approach for PrivateSpace could be
function PrivateSpace(){
var cache = {};
return {
cache: {
get: function(key){
return cache[key];
},
set: function(key, value) {
cache[key] = value;
}
},
init: function() {
...
}
};
}
This is the pattern normally used for providing private members..

The prototype pattern does not allow per-instance private variables.
You can either use a private cache that will be accessed by all instances of PrivateSpace (by declaring the cache inside your outer most closure, or stop using the prototype pattern.

Related

Experiment: extending a native object

I was reading up on some discussions about extending native objects in javascript. Extending native objects in current browsers seem to have much less disadvantages than they used to. Certainly considering we have more control about how objects are extended through methods such as:
Object.defineProperty(<some parameters...>)
A big risk that however remains is that of collision between different code/libraries, resulting in unexpected behavior.
Risks of naming collision (and global clobbering) can be reduced by namespacing your own functions. So I thought why do we not do this if we extend the native object? Of course execution context is a problem but we can fix that with bind(), making native functions of that object available in our extended functionality. So i created the following to extend the Element object:
// Define the extend function
Object.defineProperty(Element.prototype, 'extend', { value: function(namespace, object) {
var cache = {};
// Create the namespace and make sure on invocation the Element is bound
Object.defineProperty(Element.prototype, namespace, { value: function() {
var objectCheck = typeof cache[Element.prototype.constructor.name] !== 'undefined';
if(objectCheck && typeof cache[Element.prototype.constructor.name][namespace] !== 'undefined'){
console.log('cache used');
return cache[Element.prototype.constructor.name][namespace];
} else {
var extended = Element.prototype[namespace].extended;
for (var e in extended) {
extended[e] = extended[e].bind(this);
}
if(!objectCheck)
cache[Element.prototype.constructor.name] = {};
cache[Element.prototype.constructor.name][namespace] = extended;
}
return extended;
}});
this[namespace].extended = object;
return this;
}});
// Extend the Element prototype with an attr function in
// the namespace 'namespace' or ofcourse whatever function
Element.prototype.extend('namespace',{
attr: function(name, value) {
if(arguments.length === 1) {
return this.getAttribute(name);
} else {
this.setAttribute(name, value);
return this;
}
}
});
It all looks good when check it out on an actual element, the namespace is there and inside it we find our 'attr' function. We can invoke it like this:
document.querySelector('.a-css-class').namespace().attr('class')
// returns 'a-css-class'
The code could be refactored further to dynamically extend all sorts of objects. However I'm curious, what could this mean for performance, does this experiment make sense? The main question, is this any better than extending directly?
EDIT (based on Bergi's comments regarding performance):
It would be possible to cache the created function in the outer function. Let's see if I can come up with an implementation.
EDIT 2:
Added a simple caching function to make sure not all namespaced methods are create each en every invocation.
EDIT 3:
The updated code. In an attempt to make extending native object safer, the following code was produced. It might prevent naming collision. Thanks #Bergi:
/**
* Extend a (NATIVE) object with a namespace. See below for an example
*/
Object.defineProperty(Object.prototype, 'extend', { value: function(namespace, object) {
function Wrapper(that) {
this.that = that;
}
Wrapper.prototype = object;
Object.defineProperty(Object.prototype, namespace, { value: function() {
return new Wrapper(this);
}});
}});
// This example uses the Element object, but it could be any object
Element.prototype.extend('namespace',{
attr: function(name, value) {
if(arguments.length === 1) {
return this.that.getAttribute(name);
} else {
this.that.setAttribute(name, value);
return this;
}
}
});
However I'm curious, what could this mean for performance
Your current code means that whenever you call namespace(), you create a new object and lots of bound functions, which will impact performance quite heavily for large namespaces.
I would recommend to let your namespace method return a wrapper object with an element: this property, which inherits all the methods that can be called on it. You'd use this.element.getAttribute then instead of this.getAttribute:
Element.prototype.extend = function(namespace, object) {
function Wrapper(el) {
this.element = el;
}
Wrapper.prototype = object;
Element.prototype[namespace] = function() {
return new Wrapper(this);
};
};
The main question, is this any better than extending directly?
Not much. You still are defining extend and namespace properties on the Element.prototype, and for these two properties all the arguments against extending the DOM are still valid. You might lower the risk of collisions with more common names (attr), but it's not better than just defining a namespace_attr method.

JavaScript: Adding a Namespace to Prototype Methods

I have a commercial application that has an existing JavaScript object structure using prototype chains. I have had success extending this API by adding more methods to the prototypes of objects. However, I realize that it would be best to add a namespace in front of my methods in case the application vendor decides to name a new method the same as one of my methods in a future release.
If I have an existing object called State, I would add a method called getPop like so:
State.prototype.getPop = function(){return this.pop;};
var Washington = new State('Washington',7000000);
Washington.getPop(); //returns 7000000
What I want to do is add a namespace called 'cjl' before my custom method to avoid name collision so that I can call it like so:
Washington.cjl.getPop();
I tried:
State.prototype.cjl = {};
State.prototype.cjl.getPop = function(){return this.pop;};
The problem is this. It doesn't point to the instance but instead points to the 'cjl' object.
I tried various methods, including using .bind() but none of them seemed to work. I finally found an answer here: Is it possible to organise methods on an object's prototype into namespaces? This works using the Object.defineProperty() method. The problem is the commercial application only works in compatibility mode in IE which doesn't support the Object.defineProperty() method for non-DOM elements.
Is there another way to accomplish this? I don't want to have to call multiple functions, which is the result of some techniques, e.g.:
Washington.cjl().getPop();
You could namespace in the following way, reading your comments I see that you can't change the original constructor so you'll have to replace the original with your own and save the original in a closure.
Every state instance will have it's own cjl instance but that only has a reference to current State instance, all the cjl functions are shared as they exist only once:
[UPDATE]
Forgot to get State.prototype in myState's prototype chain.
//the original constructor
function State(name, pop){
this.name=name;this.pop=pop;
}
State.org="original constructor";
//original constructor is available through
// closure and window.State is replaced with
// your constructor having the cjl namespace
(function(State){
//cjl namespace
function cjl(stateInstance){
this.stateInstance=stateInstance;
};
//cjl functions
cjl.prototype.getPopInThousands=function(){
//do not use this, instead use this.stateInstance
return this.stateInstance.pop/1000;
}
function myState(){
//apply State constructor
State.apply(this,arguments);
//create a clj instance, all methods
// are on cjl.prototype so they're shared
this.cjl = new cjl(this);
}
//inherit from State (use polyfil for older browsers)
myState.prototype = Object.create(State.prototype);
//replace window.State with your constructor
window.State=myState;
}(State))
var s = new State("Wasington", 7000000);
console.log(s.cjl.getPopInThousands());
//non standard, name
console.log("constructor name",s.constructor.name);
console.log("constructor tostring",s.constructor.toString());
More on constructor functions and prototype can be found here: https://stackoverflow.com/a/16063711/1641941
I have to agree with friend and cookie that pre fixing the function names may be the better solution but if you want to use the same methods for an object named Country then you may think of using the previous code as you can re use the cjl object.
Instead of defining State.prototype.cjl outside of the function, try to set the cjl "namespace" inside the constructor function.
function State(){
var thisObject = this;
this.cjl = {
getPop: function(){
return thisObject.pop;
}
};
}
Then you can do Washington.cjl.getPop();.
Try:
var State = function(name, pop) {
this.name = name;
this.pop = pop;
};
State.prototype.cjl = function(method) {
return this.cjlDefs[method].apply(this, Array.prototype.slice.call(arguments, 1) );
};
State.prototype.cjlDefs = {
getPop: function() {return this.pop;}
};
var Washington = new State('Washington', 80000);
console.log( Washington.cjl('getPop') );
https://jsfiddle.net/ghbjhxyh/
Or another shape if you prefer:
var State = function(name, pop) {
this.name = name;
this.pop = pop;
};
State.prototype.cjl = function(method) {
this.cjlDefs.obj = this;
return this.cjlDefs;
};
State.prototype.cjlDefs = {
assertObj: function() { /* Make sensible assertion */ },
getPop: function() { this.assertObj(); return this.obj.pop; }
};
var Washington = new State('Washington', 75000);
console.log( Washington.cjl().getPop() ); // 75000
https://jsfiddle.net/7vjrz2mn/

Switching between singleton and prototype scope using RequireJS

Spring has very useful option, that whey I define a bean, I define a scope. If it's singleton, only one instance is created. By prototype, each time a bean is required, a new instance is created.
RequireJS provides by default singletons, so with such simple module:
Singleton.js
define([], function() {
console.log('Instance initialization')
var items = []
var singleton = {
getItems: function() {
return items
},
setItems: function(newItems) {
items = newItems
},
addItem: function(item) {
items.push(item)
}
};
return singleton;
})
and the usage:
require(["showcase/modules/Singleton"], function(Singleton){
Singleton.addItem('item1')
console.log(Singleton.getItems())
})
require(["showcase/modules/Singleton"], function(Singleton){
Singleton.addItem('item2')
console.log(Singleton.getItems())
})
the output will be:
Instance initialization
["item1"]
["item1", "item2"]
Is it possible to define and use the module in such way, that I could switch in the module definition, if I want to use prototype or singleton scope? So in my case, without changing the usage, I'd get:
Instance initialization
["item1"]
Instance initialization
["item2"]
I'm using RequireJS from Dojo, just in case of syntax differences
Well, first of all the problem is that when you import a module using an AMD loader, you will actually get an instance, but the second time you import the same module, the same instance is actually returned (problem 1).
To overcome this problem you should use the factory design pattern to get your instance and also translate your singleton object to a class that can be instantiated (problem 2). Your factory could have a method called getInstance() that accepts a boolean parameter that can toggle between singleton/prototype.
So without changing your usage you won't be able to do this because of the problems I just addressed. The best solution I can come up with (with a factory) is:
Singleton.js
define([], function() {
console.log('Instance initialization');
// Singleton class
var Singleton = function() {
this.items = [];
this.getItems = function() {
return this.items;
};
this.setItems = function(newItems) {
this.items = newItems;
};
this.addItem = function(item) {
this.items.push(item);
}
};
// Factory
var factory = {
singletonInstance: new Singleton(),
getInstance: function(/** Boolean */ isSingleton) {
if (isSingleton === true) {
return this.singletonInstance;
} else {
return new Singleton();
}
}
};
return factory;
});
Usage (singleton)
require(["app/Singleton"], function(singletonFactory){
var Singleton = singletonFactory.getInstance(true);
Singleton.addItem('item1');
console.log(Singleton.getItems());
});
require(["app/Singleton"], function(singletonFactory){
var Singleton = singletonFactory.getInstance(true);
Singleton.addItem('item2');
console.log(Singleton.getItems());
});
Usage (multiple instances)
require(["app/Singleton"], function(singletonFactory){
var Singleton = singletonFactory.getInstance(false);
Singleton.addItem('item3');
console.log(Singleton.getItems());
});
require(["app/Singleton"], function(singletonFactory){
var Singleton = singletonFactory.getInstance(false);
Singleton.addItem('item4');
console.log(Singleton.getItems());
});
In case you're interested in a full example, it's on Plunker.
Eventually you could wrap the factory as a plugin so that you could actually do something like:
require([ "app/Object!singleton", "app/Object!prototype" ], function() {
});
However I don't know if RequireJS also supports this (and if I'm understanding well it should be a generic story for both AMD loaders).

Javascript library development scopes and namespaces

we currently learn some Javascript stuff in a course at the university.
For that we implement a library for common tasks like show(), hide(), write and such things.
Currently im running with an implementation like:
var myLib_maker = function () {
/*
private scope
*/
var debuggingMode=true;
var currentElement=null;
/*
end private scope
*/
return {
getElement: function (id) {
var o;
if (typeof id === 'string') { o = document.getElementById(id); }
if (!!(o && o.nodeType !== 1)) {
throw {
name: 'Type Error',
message: 'Wrong node type at id: '+id
}
}
currentElement=o;
return this;
},
getCurrentElement: function() {
console.log(currentElement)
return currentElement;
},
isVisible: function () {
return this.getCurrentElement().style.display.toLowerCase() === "block";
},
show: function () {
this.debug("show "+this.getCurrentElement())
this.getCurrentElement().style.display = "block";
return this;
},
hide: function () {
this.debug("hide "+this.getCurrentElement())
this.getCurrentElement().style.display = "none";
return this;
},
toggle: function() {
this.debug("toggle "+this.getCurrentElement())
this.isVisible() ? this.hide(): this.show();
return this;
},
write: function (content){
this.debug("write to"+this.getCurrentElement().id);
var tg = this.getCurrentElement().tagName.toLowerCase();
if (tg === 'input' || tg === 'textarea') {
currentElement.value = content;
} else {
currentElement.innerHTML = content;
}
return this
},
debug: function (what) {
if (debuggingMode===true){
console.log("[DEBUG] "+what);
}
return this;
}
};
}
var myLib=myLib_maker();
Than I have an external function (for testing) to switch 2 textareas contents.
function switchEditors(id1, id2){
c1=myLib.getElement(id1).getCurrentElement().value;
c2=myLib.getElement(id2).getCurrentElement().value;
myLib.getElement(id1).write(c2)
myLib.getElement(id2).write(c1)
}
I first tried with the following code, which obviously does not work, cause I overwrite my private currentElement and so I write always to id2
function switchEditors(id1, id2){
tmp=myLib.getElement(id1).getCurrentElement().value
myLib.getElement(id1).write(myLib.getElement(id2).getCurrentElement().value)
myLib.getElement(id2).write(tmp)
}
But what I really wanted initially was not using a private currentElement variable.
The first implementation of the write method extended the Element Object
Element.prototype.write= function (content){
var tg = this.tagName.toLowerCase();
if (tg === 'input' || tg === 'textarea') {
this.value = content;
} else {
this.innerHTML = content;
}
return this;
}
and such the getElement function returned
document.getElementById(id)
I want cascading (I hope this is the right word -> I mean the myLib.getElement("myid").show().hide() concatenation thing) and getting direct access to
all Element attributes but we must not use global scope for our library, so I have to encapsulate my library in any way.
So is there an elegant way to use the cascading thing and be able to get a direct access to all attributes on an element object without implementing each method within the global element scope?
Or is my lib desing completely wrong and has to be done totally different.
If so, just tell me, I appreciate any help.
(I tried to figure out how jQuery actually implement these things, but didn't get a real clue how it is done ... too much code ... :) )
I hope I described my wishes and requirements. If not please ask for more specific details.
As you've figured out, the currentElement is shared between calls to getElement. Instead you could create a new instance of myLib-object with Object.create and bind currentElement to that.
getElement: function (id) {
var o, self = Object.create(this);
/* ... */
self.currentElement = o;
return self;
}
And use this.currentElement throughout so that each call uses its own current element.
While Magnar's solution will work with this (singleton) pattern, it is a better idea to avoid creating a whole new object each time you call getElement. There is a reason for creating "classes" instead of singletons.
You can do it like this:
var MyLib_Maker = (function () { // I capitalized the class as a helpful
// convention recommended by Douglas Crockford
// Private static vars
var debuggingMode = true;
var currentElement = null;
// Private static methods
function _myMethod (x, y) { // call below by _myMethod(3,4);
return x * y;
}
// Private instance methods (but only if called properly:
// invoke below by _instMethod.call(this, 3, 4); )
function _instMethod (x, y) {
return this.anInstanceNumber * x * y;
}
// Private instance properties (quite cumbersome but doable if you
// absolutely must--e.g., for classes whose objects need to be clean when iterated)
// See http://brettz9.blogspot.com/2009/02/true-private-instance-variables-in.html
// and http://brettz9.blogspot.com/2009/02/further-relator-enhancements.html
// (put the Relator inside the closure if you don't want it reusable (and public),
// but then your extending classes must be inside the closure too)
function MyLib_Maker (arg1, arg2) {
// I recommend the following check to allow your class to be
// instantiated without the 'new' keyword (as in jQuery/$):
if (!(this instanceof MyLib_Maker)) {
return new MyLib_Maker(arg1, arg2);
}
// Other constructor code here
// ...
}
// Methods added on the prototype benefit from merely
// providing a low-memory reference across all instances;
// this will avoid adding a whole new object unnecessarily
// into memory
MyLib_Maker.prototype.getElement = function () {
// ....
return this; // Keep the chain going (if not public
// properties, you could add a method which also avoids
// extending the chain, like $(el).get() in jQuery
};
return MyLib_Maker;
}()); // We can invoke immediately to wrap up the closure
// Usage example:
var mlm = MyLib_Maker(2, 3).getElement().doSomething();
By the way, what you describe is called chaining; cascading is used in the likes of CSS to indicate that like different waves out of a waterfall, one may write over the other, as you can do by writing rules which override prior ones in CSS.
And it is good you moved away from overriding the Element object because, whatever the browser incompatibilities, this is the worst kind of global namespace pollution because it affects all elements, increasing the chance that another library which depends on that method (or which is careless in overriding the built-in prototypes itself) may get you unexpected results.

Minimalistic prototype (js framework)

I want to use the prototype javascript framework for its "class" and inheritance capabilities. For everything else I will be using jQuery. Is there a minimalist version of prototype that will give me just this functionality? I don't want the additional overhead of the entire library if I won't be using it all.
To be specific I want the class and inheritence capabilities that allow me to define classes as follows (examples from wikipedia):
var FirstClass = Class.create( {
// The initialize method serves as a constructor
initialize: function () {
this.data = "Hello World";
}
});
and to extend another class:
MyNewClass = Class.create( FirstClass, {
//Override the initialize method
initialize: function() {
//..
},
// ...more methods add ...
});
Plus I don't want conflicts between the frameworks (i.e. $ should only be used by jQuery..I only want prototype (or any other suggestion would be fine) for class creation / inheritance).
Inheritance.js was the model that the guys developing the Prototype library have been inspired from and I think it is what you where asking for.
Note: $super seems to be this.parent as stated in the comments below.
If you're looking for something minimalistic:
function clone(obj) {
if(typeof obj !== 'undefined') {
clone.prototype = obj;
return new clone;
}
}
function copy(dest, src) {
for(var name in src) {
if(src.hasOwnProperty(name))
dest[name] = src[name];
}
}
function classof(constructor) {
return {
extend : function(base) {
constructor.prototype = clone(base.prototype);
return this;
},
mixin : function(members) {
copy(constructor.prototype, members);
return this;
}
};
}
Example usage:
// base class:
function Foo(value) {
this.value = value;
}
classof(Foo).mixin({
inc : function() { ++this.value; }
});
// derived class:
function Bar() {
Foo.apply(this, arguments);
}
classof(Bar).extend(Foo).mixin({
dec : function() { --this.value; }
});
var bar = new Bar(42);
bar.inc();
bar.dec();
Don't mix Prototype and jQuery. My experience says they don't play nice together. My personal preference would be to use Prototype due to the superior syntactical sugar.
There's no way to disable Prototype's $-function. You can disable jQuery's use of $ via jQuery.noConflict() - but it's not perfect.
As #mhtiza said, use Interitance.js for the class-sugar if you decide to stick to jQuery.
For version 1.7.1 I deleted everything below line 1625 in the prototype.js file and I no longer have conflicts with bootstrap and jquery. And the Class.create function still works. The class.create is the only method I wanted as well.

Categories