I have this code here:
var Person = (function() {
var name;
var PersonConstructor = function(n) {
name = n;
};
PersonConstructor.prototype.getName = function() {
return name;
};
return PersonConstructor;
})();
var people = [];
var person1 = new Person("Foo");
var person2 = new Person("Bar");
alert(person1.getName()); // I want it to be Foo
people.push(person1);
people.push(person2);
I got the idea of emulating classes from here.. But of course, I neglected the fact that the private variable var name; is also a static variable. Since this is tripping my current efforts I would like to know if there is a way to keep the private behaviour in this example but avoid the static one?
Use this.
var Person = (function() {
var PersonConstructor = function(n) {
this.name = n;
};
PersonConstructor.prototype.getName = function() {
return this.name;
};
return PersonConstructor;
})();
Unfortunately, this won't preserve the private state.
It's just a scope issue.
var Person = (function(){
var PersonConstructor = function(n){
// ***************************************************************
// PRIVATE VARIABLES AND FUNCTIONS
// ONLY PRIVELEGED METHODS MAY VIEW/EDIT/INVOKE
// ***************************************************************
var myName=n?n:"John Doe";
// ***************************************************************
// PRIVILEGED METHODS
// MAY BE INVOKED PUBLICLY AND MAY ACCESS PRIVATE ITEMS
// MAY NOT BE CHANGED; MAY BE REPLACED WITH PUBLIC FLAVORS
// ***************************************************************
this.toString=this.getName=function(){ return myName }
}
return PersonConstructor;
})();
var person1 = new Person("foo");
var person2 = new Person("bar");
alert(person1.getName());
alert(person1.toString());
alert(person1.myName);
// alerts "foo", "foo", undefined
EDIT - Here is my original solution.
var Person = function(n){
// ***************************************************************
// PRIVATE VARIABLES AND FUNCTIONS
// ONLY PRIVELEGED METHODS MAY VIEW/EDIT/INVOKE
// ***************************************************************
var myName=n?n:"John Doe";
// ***************************************************************
// PRIVILEGED METHODS
// MAY BE INVOKED PUBLICLY AND MAY ACCESS PRIVATE ITEMS
// MAY NOT BE CHANGED; MAY BE REPLACED WITH PUBLIC FLAVORS
// ***************************************************************
this.toString=this.getName=function(){ return myName }
}
var person1 = new Person("foo");
var person2 = new Person("bar");
alert(person1.getName());
alert(person1.toString());
alert(person1.myName);
// alerts "foo", "foo", undefined
There is no "private" when working with prototypes.
It should be noted there is no value in private state, avoid it like the plague. Closures are ugly and expensive.
var o = {
name: value
}
However if you insist on being delusional and want private state badly then
You can store state in a closure
function nameHolder(name) {
return {
get name() {
return name
},
set name(n) {
name = n
}
}
}
Note this is highly inefficient and has little benefit.
Alternatively you can store state in a weakmap
function privates() {
var map = new WeakMap()
return function (key) {
var v = map.get(key)
if (!v) {
v = {}
map.set(key, v)
}
return v
}
}
var NameHolder = (function () {
var state = privates()
return {
constructor: function (name) {
state(this).name = name
},
getName: function () {
return state(this).name
}
}
}())
WeakMap browser support is non-existant so emulate it using pd.Name
Related
I have this:
function Book (){
this.width = 7;
this.height = 10;
var pages = 100;
this.tear_page = function(){
return --pages;
}
}
function TechBook () {
var pages = 50;
this.open_book = function(){ return "Opened in page "+(pages/2); };
return this;
}
var bBook = Object.create(new Book(), new TechBook());
console.log(bBook);
console.log(bBook.tear_page());
console.log(bBook.open_book());
I can't get this to work. I got as far as getting TechBook to inherit the access to local/private variables from Book but only from the Book functions. If I add new methods or overwrite them, they can't get those variables any more. I wonder if there is a way to still have access to those variables from methods of the subclass and to create new private variables inside the subclass.
If this is not posible in any way, that would mean that you can't have private variables if you want inheritage, or viceversa. Oh, and btw, I know that chrome can now (thanks to ES6) implement classes naturally with: class TechBook extends Book (){} as many other languages, but as support is limited to last versions of chrome at this time... I wonder if there is any other way to solve this problem.
You can't inherit private in any language, only protected or public can be inherited.
That concept does not exist in javascript but u can emulate when creating an object (properties = public, scope things = private);
A work around could be add a property that execute a function that return the private variable/function of the object scope.
If expose a method that return a private object it can be modified because u have the returned reference.
I like to do it like this:
var SomeObject = function() {
//private var one, can't access it outside of this scope
var one = 1;
/*private object anotherObject, can't access it directly
but if you return it with a public function you can modify it.*/
var anotherObject = {
a : 'somevalue'
};
//public prop two, can access it outside of this scope.
this.two = 2;
//public method getOne, you can access it.
this.getOne = function() {
return one;
};
/* return private var anotherObject */
this.getAnotherObject = function() {
return anotherObject;
};
};
var someObject = new SomeObject();
console.log(someObject.two); // print in console 2
console.log(someObject.getOne()); // print in console 1
var referencedObject = someObject.getAnotherObject();
console.log(referencedObject);
referencedObject.a = 'anotherValue';
console.log(someObject.getAnotherObject());
Fiddle
Here is an example of how you might pass data by knowing a secret
function Book(secret) {
secret = secret || {};
var env = {}; // `env` to hold data requiring `secret`
this.width = 7;
this.height = 10;
env.pages = 100;
this.getEnv = function (s) { // give access to `env` if you know the secret
if (s === secret) return env;
};
this.tear_page = function () {
return --env.pages;
};
}
function TechBook(secret) {
secret = secret || {};
Book.call(this, secret); // construct from Book
var env = this.getEnv(secret); // get references via secret
this.open_book = function () {
return "Opened in page " + (env.pages/2);
};
}
TechBook.prototype = Object.create(Book.prototype); // set up inheritance
Using an Object reference as the secret will be more secure than using a primitive as you'll need the original reference for access.
Now you have
var bBook = new TechBook();
console.log(bBook); // instance of TechBook
console.log(bBook.tear_page()); // 99
console.log(bBook.open_book()); // Opened in page 49.5
Reference to fundamentals of prototype inheritance and Object.create property arguments.
Implemented based on your example
function Book (){
this.width = 7;
this.height = 10;
this.pages = 100;
this.tear_page = function(){
return --this.pages;
}
this.init = function() {
return this
}
}
Book.prototype = {
open_book: function(){ return "Opened in page "+(this.pages/2) }
}
var bBook = Object.create(new Book(), {pages: { value: 50 } }).init();
console.log( new Book()) // { width: 7, height: 10, pages: 100, tear_page: [Function], init: [Function] }
console.log( bBook ) //{}
console.log( bBook.width ) //->7
console.log( bBook.height ) //-> 10
console.log( bBook.pages ) // -> 50
console.log( bBook.tear_page()) //-> 49
console.log(bBook.open_book()) //-> Opened in page 25
I am new to JavaScript's (prototypal) inheritance and I'm trying to learn more about it.
I am using a simple observer pattern as example, in which I want observable objects to be derived from the 'subject' object. This is what I WANT to do:
function subject()
{
var callbacks = {}
this.register = function(name, callback)
{
callbacks[name] = callback;
}
this.unregister = function(name)
{
delete callbacks[name];
}
var trigger = function()
{
var a = arguments;
var t = this;
$.each(callbacks, function(name, callback)
{
callback.apply(t, a);
});
}
}
list.prototype = new subject()
function list()
{
var items = {}
this.add = function(name, item)
{
items[name] = item;
trigger('add', name);
}
this.remove = function(name)
{
delete items[name];
trigger('remove', name);
}
}
Now when using the code above like below, I run into my first problem:
var l = new list()
l.register('observer1', function() { console.log(this, arguments) });
l.add('item1', 'value1'); // <-- ReferenceError: trigger is not defined, trigger('add', name);
To continue testing I made the trigger function 'public' using this.trigger instead. Running my example again I run into the next problem:
var l = new list()
l.register('observer1', function() { console.log(this, arguments) });
l.add('item1', 'value1'); // <-- output: subject, ["add", "item1"]
The this object is subject, I want it to be list. My third problem occurs when creating another list:
var l2 = new list();
//Don;t register any observers
l2.add('item1', 'value1'); // <-- output: subject, ["add", "item1"]
The callbacks list is shared between the 2 lists.
I've tried similar things with Object.create(new subject()) as well and run into similar problems.
My 3 questions in this are:
Can I have private methods that can be used in derived objects (and
should I even care about having them private or public)?
How can I have the this object I want (without needing to use function.call in the derived object, if possible)?
How can I keep the callbacks list in the base object without it being shared?
An interesting question. As for #1 and #2: let's say you have a function foo:
function foo() {
var _private = 'private var!';
this.access = function () {
return _private;
}
}
access is a so-called privileged method, it's a closure that can access the private variable private.
you can inherit the whole thing by making use of call, like so:
function bar() {
foo.call(this);
}
var b = new bar();
console.log(b.output()); // prints 'private var!'
With the methods apply, call and bind you can establish the context of a function, effectively tamper with the this object. (your #2 question, read here )
Naturally you cannot make use of a totally private method in a derived object. You'd need an accessor method which would defeat the purpose of the original method being private. Having said that, that's the way it works in strongly typed languages too (in java if you mark a method as private not even subclases will be able to access it, it would have to be protected).
As for #3, I cannot think of how to keep callbacks shared and private.
But you can make it a static property for all instances of a function (much like a static property in a lanaguage like java) by simply declaring a function like:
function foo() {
}
add your prototypes which will be assigned to each instance
foo.prototype.bar = // ...
and a static property
foo.callbacks = [];
All instances of foo will share the callbacks property.
You can’t have private methods, and that’s that. It will never work both properly and nicely at the same time, so don’t bother trying to emulate them in JavaScript.
Then all you have to do is call the parent’s constructor in the derived constructor.
function subject()
{
var callbacks = {};
this.register = function(name, callback)
{
callbacks[name] = callback;
};
this.unregister = function(name)
{
delete callbacks[name];
};
this.trigger = function()
{
var a = arguments;
var t = this;
$.each(callbacks, function(name, callback)
{
callback.apply(t, a);
});
};
}
list.prototype = Object.create(subject);
list.prototype.constructor = list;
function list()
{
subject.call(this);
var items = {};
this.add = function(name, item)
{
items[name] = item;
this.trigger('add', name);
};
this.remove = function(name)
{
delete items[name];
this.trigger('remove', name);
};
}
Incorporating Joe's suggestion, this is what I eventually ended up with:
function subject()
{
var callbacks = {}
this.register = function(name, callback)
{
callbacks[name] = callback;
}
this.unregister = function(name)
{
delete callbacks[name];
}
trigger = function()
{
var a = arguments;
var t = this;
$.each(callbacks, function(name, callback)
{
callback.apply(t, a);
});
}
}
//without the following line, 'this' in firefox is 'subject' instead of 'list' (in chrome it is)
list.prototype = new subject()
//without these, 'list' is not an instanceof 'subject'
list.constructor = subject;
list.prototype.constructor = list;
function list(n)
{
this.name = n;
subject.call(this); //as suggested by Joe
var items = {}
this.add = function(name, item)
{
items[name] = item;
trigger.call(this, 'add', name); //no way to do this without using call/apply
}
this.remove = function(name)
{
delete items[name];
trigger.call(this, 'remove', name); //no way to do this without using call/apply
}
this.getitems = function() { return items }
}
//without the following line, 'this' in firefox is 'subject' instead of 'queue'
queue.prototype = new subject()
//without these, 'queue' is not an instanceof 'subject'
queue.constructor = subject;
queue.prototype.constructor = queue;
function queue(n)
{
this.name = n;
subject.call(this); //as suggested by Joe
var items = [];
this.enqueue = function(item)
{
items.push(item);
trigger.call(this, 'enqueue', item); //no way to do this without using call/apply
}
this.dequeue = function()
{
var d = items.shift();
trigger.call(this, 'dequeue', d); //no way to do this without using call/apply
return d;
}
this.getitems = function() { return items }
}
var l1 = new list('l1')
l1.register('observer1', function() { console.log('l1', this, arguments) });
l1.add('item1', 'value1');
// ^ 'l1', list { name = 'l1' ... }, ['add', 'item1']
var l2 = new list('l2')
l2.register('observer2', function() { console.log('l2', this, arguments) });
l2.add('item2', 'value2');
// ^ 'l2', list { name = 'l2' ... }, ['add', 'item2']
var q1 = new queue('q1')
q1.register('observer3', function() { console.log('q1', this, arguments) });
q1.enqueue('item3');
// ^ 'q1', queue { name = 'q1' ... }, ['enqueue', 'item3']
console.log(l1 instanceof list, l1 instanceof subject, l1 instanceof queue);
// ^ true, true, false
console.log(q1 instanceof list, q1 instanceof subject, q1 instanceof queue);
// ^ false, true, true
This ticks all of my boxes (except for the use of call, but I can live with that).
Thanks for all the help,
Mattie
EDIT: appearantly this does not work as expected. creating a new object overwrites the other objects callbacks
$(document).ready(function () {
var patient = (function (options) {
var age = options.age;
var name = options.name;
function getName() {
return this.name;
}
function setName(val) {
name = val;
}
function getAge() {
return this.age;
}
function setAge(val) {
age = val;
}
return {
getAge: getAge,
setAge: setAge,
getName: getName,
setName: setName
}
})();
});
I realize that I'm never passing any options in my example here.
If I try to do something like patient.setAge('100') and then console.log(patient.getAge()) I get an error saying cannot read property Age of undefined. The overarching theme that I'm trying to get at is within a module, how can I emulate consturctors to instantiate a new patient object while keeping all the OOP goodness of private variables and all that jazz.
I've seen some examples of constructors in a module pattern on here and I haven't understood them very well. Is it a good idea in general to have a constructor in a module? Is its main purpose similarity with class-based languages?
This is a constructor:
function Patient(options) {
options = options || {};
this.age = options.age;
this.name = options.name;
}
$(document).ready(function () {
var patient = new Patient();
});
You can put it inside a module if you want. What you shouldn’t do is provide getters and setters, especially ones that don’t do anything. If you’re exposing a variable through two properties to get and set it, it should just be one property.
Try this
function Patient (options) {
options = options || {};
var age = options.age;
var name = options.name;
function getName() {
return name;
}
function setName(val) {
name = val;
}
function getAge() {
return age;
}
function setAge(val) {
age = val;
}
return {
getAge: getAge,
setAge: setAge,
getName: getName,
setName: setName
}
}); // pass empty object
$(document).ready(function () {
var p1 = new Patient({});
var p2 = new Patient();
var p3 = new Patient({age:20});
var p4 = new Patient({name:"abcd"});
var p5 = new Patient({age:21, name:"abcd"});
});
How can the the top-most scope can be cached in order to be used deeper in the prototype later, like so:
var Game = function(id){
this.id = id;
};
Game.prototype = {
board : {
init: function(){
// obviously "this" isn't the instance itself, but will be "board"
console.log(this.id);
}
}
}
var game = new Game('123');
game.board.init(); // should output "123"
update:
Well now that I think about it, I can use apply/call and pass the context...
game.board.init.apply(game);
As you only have one instance of the board object, there is no way for it to know what you used to access it. Using game.board or Game.prototype.board to access the object gives exactly the same result.
If you don't want to create one board object for each Game instance, you have to tell the board object which Game object it should consider itself to belong to for each call:
game.board.doSomething(game);
or:
Game.prototype.board.doSomething(game);
Edit:
To create one board for each Game instance, make a constructor for Board, and make the board object aware of the Game instance that it belongs to:
function Game(id) {
this.id = id;
this.board = new Board(this);
}
Game.prototype = {
};
function Board(game) {
this.game = game;
}
Board.prototype = {
init: function(){
console.log(this.game.id);
}
};
var game = new Game('123');
game.board.init(); // outputs "123"
There's no such thing as 'deeper in the prototype'. "this" will always be the object that you're calling it on, unless that's changed through being a callback or various ways to rebind. You'll have less sanity loss if you split up your concepts and link them together:
Board = function (game) {
this.game = game;
}
Board.prototype.init = function () {
console.log(this.game.id);
}
Game = function () {
this.id = 123;
this.board = new Board(game);
}
Game.prototype = {};
Alternatively, if you're hellbent on making it all use the same base, you can do some crazy hack like..
Game = function () {
this.id = 123;
var self = this;
for(var p in this.board) {
var property = this.board[p];
if(typeof property == 'function') {
this.board[p] = function (method) {
return function () {
method.apply(self, arguments);
}
}(property)
}
}
}
This is a total hack, and it'll make your coworkers hate you. (If you're using the underscore library, there's a bindAll function that helps with this stuff)
UPDATE
You must make a new instance of for each game if you don't want to provide scope for each function. If you want, you can make board private to Game by making the board constructor a variable in the Game constructor function
var Game = function(id){
//Keep board private to Game
var boardClass = function (scope) {
this.scope = scope;
this.init = function () {
if ( this.scope instanceof Game ) {
console.log(this.scope.id);
}
};
};
this.id = id;
//Instantiate a new board for each game
this.board = new boardClass(this);
};
var game = new Game(123);
//Logs 123
game.board.init();
var game2 = new Game(456);
//Logs 456
game2.board.init()
//Still Logs 123
game.board.init();
Don't use the code below because, as Guffy points out, one board object is shared between all instances of Game. So the solutions below do not work for multiple Games.
You could force game.board.init to make this refer to an instance of game.
var Game = function(id){
this.id = id;
this.board.game = this;
};
Game.prototype = {
board : {
game: {},
init: function() {
//check if this is an instance of board by seeing if it has a game property
if(this.game) {
//make this refer to the game if it is referring to the board
this.init.apply(this.game);
}
console.log(this.id);
}
}
}
var game = new Game(123);
//Logs 123
game.board.init();
var game2 = new Game(456);
//Logs 456
game2.board.init();
//Logs 456...oops!
game.board.init();
Or you could simply make the instance of game a property on the board.
var Game = function(id){
this.id = id;
this.board.scope = this;
};
Game.prototype = {
board : {
init: function(){
// can check if the scope is what we want it to be
if( this.scope instanceof Game )
console.log(this.scope.id);
}
}
}
var game = new Game(123);
//Logs 123
game.board.init();
var game2 = new Game(456);
//Logs 456
game2.board.init();
//Logs 456...oops!
game.board.init();
Try this way
var Model = function() {this.b = 2;};
Model.prototype.a = function() {
console.log(this);
var that = this;
this.a.on = function(){
console.log(that); console.log(m.b);
};
}
var m = new Model();
m.a();
m.a.on();
You need to do two things
Create set the method inside the prototype method, means on should be defined inside a so that it has access to this.
Create a copy of the parent reference in that and use it inside on
It's a very bad idea to do so, as it leads to very strange behaviour in some cases, but it's possible:
var Model = function(x) { this.x = x };
Object.defineProperty(Model.prototype, 'a', (function() {
var lastSelf;
function get() { return lastSelf.x }
get.on = function () { return lastSelf.x * 2 };
return { get() { lastSelf=this; return get } };
})());
var m = new Model(17);
console.log(m.a(), m.a.on());
Why? I see your answer below, trying to realize what are bad cases.
You can't pass a through the variable.
You must grant access to on immediately after getting property a of the same object:
var m1 = new Model(1), m2 = new Model(3);
console.log(m1.a(), m2.a(), m1.a.on(), m2.a.on()); // 1 3 2 6 - ok
var a1 = m1.a, a2 = m2.a;
console.log(m1.a(), m2.a(), a1.on(), a2.on()); // 1 3 6 6 - ooops!
console.log(m1.a(), m2.a(), m1.a(), a1.on(), a2.on()); // 1 3 1 2 2 - ooops!
I currently know two ways to construct singletons in JavaScript. First:
var singleton = {
publicVariable: "I'm public",
publicMethod: function() {}
};
It is perfect except that it does not have a constructor where I could run initialization code.
Second:
(function() {
var privateVariable = "I'm private";
var privateFunction = function() {}
return {
publicVariable: "I'm public",
publicMethod: function () {}
}
})();
The first version does not have private properties nor does it have a constructor, but it is faster and simpler. The second version is more complex, ugly, but has a constructor and private properties.
I'm not in a need for private properties, I just want to have a constructor. Is there something I am missing or are the two approaches above the only ones I've got?
function Singleton() {
if ( Singleton.instance )
return Singleton.instance;
Singleton.instance = this;
this.prop1 = 5;
this.method = function() {};
}
Here is my solution with closures:
function Singleton() {
Singleton.getInstance = (function(_this) {
return function() { return _this; };
})(this);
}
Test:
var foo = new Singleton();
var bar = Singleton.getInstance();
foo === bar; // true
If you are just looking for a place to initialise your singleton, how about this?
var singleton = {
'pubvar': null,
'init': function() {
this.pubvar = 'I am public!';
return this;
}
}.init();
console.assert(singleton.pubvar === 'I am public!');
Simple and elegant.
var singleton = new function() { // <<----Notice the new here
//constructorcode....
this.publicproperty ="blabla";
}
This is basically the same as creating a function, then instantly assiging a new instace of it to the variable singleton. Like var singleton = new SingletonObject();
I highly advice against using singletons this way in javscript though because of the execution order is based on where in the file you place the object and not on your own logic.
What about this?
var Singleton = (function() {
var instance;
// this is actual constructor with params
return function(cfg) {
if (typeof instance == 'undefined') {
instance = this;
this.cfg = cfg;
}
return instance;
};
})();
var a = new Singleton('a');
var b = new Singleton('b');
//a === b; <-- true
//a.cfg <-- 'a'
//b.cfg <-- 'a'
I make it an actual Singleton with static functions and no this like so:
class S {
//"constructor"
static init() {
//Note: Since it's a singleton, there's no "this" instance.
//Instead, you store variables directly on the class.
S.myVar = 7;
}
static myOtherFunc() {
alert(S.myVar);
}
}
//Immediately call init() to make it the "constructor".
//Alternatively, you can call init() elsewhere if you'd
//like to initialize it at a particular time.
S.init();
//Later:
S.myOtherFunc();
S.myVar = 10;