Implementing instance members/methods in JavaScript - javascript

This question stems from a problem I was trying to solve regarding the ability to have "private" instance variables in JavaScript. You may want to read this, prior to my question.
For the sake of completeness, I have illustrated my entire problem, before asking the question. My hope is that this will provide a complete example of how to implement instance members and methods correctly in JavaScript, and for any developer who lands here, to understand the pitfalls of the various implementations.
Consider the following JavaScript object:
var MessageBox = (function() {
function MessageBox(message) {
this.message = message;
}
MessageBox.prototype.Show = function() {
alert(this.message);
}
})();
This object was modelled using TypeScript, and can be used as follows:
var msg1 = new MessageBox("Hello World");
msg1.Show(); // alerts "Hello World"
var msg2 = new MessageBox("Bye World");
msg2.Show(); // alerts "Bye World"
But I can still call:
msg1.message; // "Hello World"
msg2.message; // "Bye World"
So clearly this.message is NOT private.
Now consider the following JavaScript object:
var MessageBox = (function() {
return function MessageBox(message) {
var message = message;
MessageBox.prototype.Show = function() {
alert(message);
}
}
})();
This is just a modified version of the TypeScript based MessageBox object.
var msg1 = new MessageBox("Hello World");
msg1.Show(); // alerts "Hello World"
var msg2 = new MessageBox("Bye World");
msg2.Show(); // alerts "Bye World"
but wait...I'm about to throw a spanner in the works!
var msg1 = new MessageBox("Hello World");
var msg2 = new MessageBox("Bye World");
msg2.Show(); // alerts "Bye World"
msg1.Show(); // alerts "Bye World" ... wait, what!?
msg1.message // undefined
msg2.message // undefined
So I can no longer gain access to the message variable, but now, every new instance overwrites the last instances message.
Bear with me, this is the last JavaScript object to consider:
var MessageBox = (function() {
return function MessageBox(message) {
var message = message;
this.Show = function() {
alert(message);
}
}
}();
The above object no longer implements Show() on the prototype, so now I can:
var msg1 = new MessageBox("Hello World");
var msg2 = new MessageBox("Bye World");
msg2.Show(); // alerts "Bye World"
msg1.Show(); // alerts "Hello World"
msg1.message // undefined
msg2.message // undefined
Great! Now I have private variables, and they don't overwrite each other!
So, finally the question: What is the difference between:
MessageBox.prototype.Show = function() {
}
and
this.Show = function() {
}

The question that you eventually get to has a simple answer: setting the function on the prototype means it can get called from any instance, while setting the function on the instance means it can be called only from that instance. Either way can access properties on the instance, but the thing that you're finding complicated is that either way the function only has access to local variables in the scope where it was declared or in containing scopes.
The following is the first way that came to mind to provide both private instance and private prototype variables.:
var MessageBox = (function() {
var privateProtoVar = "Hello";
function MessageBox(message) {
var privateInstanceVar = message;
this.instanceMethod = function() {
alert(privateInstanceVar); // Can access private instance var
alert(privateProtoVar); // Can access private prototype var
}
}
MessageBox.prototype.Show = function() {
alert(privateProtoVar); // Can access private proto var
// but can't access privateInstanceVar
}
return MessageBox;
})();
var msg1 = new MessageBox("1"),
msg2 = new MessageBox("2");
msg1.instanceMethod(); // "1", "Hello"
msg2.instanceMethod(); // "2", "Hello"
msg1.Show(); // "Hello"
msg2.Show(); // "Hello"
The variable privateInstanceVar is accessible by any function declared inside the inner MessageBox() function, but not from functions on the prototype unless they're declared within that same scope (which I don't do in the above example).
If you add additional methods to instances later, i.e., outside the above structure then those methods do not have access to the private var because they're declared in a different scope:
msg1.newMethod = function() {
alert(privateInstanceVar);
}
msg1.newMethod(); // error
Demo: http://jsfiddle.net/SSEga/

With MessageBox.prototype.Show you are overriding the Show function for any instance every time you call new MessageBox(). Therefore every instance always shares the same Show function with the same scope.

Related

How can I access the prototype functions?

I found code similar to this in a legacy app:
var Example = function (var1, var2, var3) {
this.prop1 = var1;
this.prop2 = var2;
this.prop3 = var3
};
Example.prototype = function () {
var showAlert = function (message) {
alert(message);
}
};
var example = new Example(null, null, null);
So I tried to access the prototype method like this:
example.showAlert("hello");
Here is the jsfiddle: http://jsfiddle.net/64dkjke1/
It doesn't work . . . the showAlert method is undefined. The prototype is equal to a function that defines other functions inside . . . hopefully I'm just overlooking something simple here.
How can I access this prototype function?
Thank you.
If you have no problem changing the original code, you can do the following to have the prototype receive an object using IIFE:
Example.prototype = (function () {
return {
showAlert : function (message) {
alert(message);
}
}
})();
var example = new Example(null, null, null);
example.showAlert("hello");
Fiddle
Was it like this instead, attaching an object to the prototype? It's difficult to determine what the issue is because you say that the code you've posted is only similar to the code in the legacy app, so we can't really know for sure.
Example.prototype = {
showAlert: function (message) {
alert(message);
}
}
You can't, cause
var showAlert = function (message) {
alert(message);
}
is private.
you can do this:
function abc(){
var myPrivateVar = "Hello";
this.alert = function(){
alert(myPrivateVar);
}
}
new abc().alert();
but not this
var a = new abc();
alert(a.myPrivateVar);
You cannot access the function showAlert() (as you have already figured out) The reason for this is simple.
Example.prototype = function ()
Does not extend your prototype by a function but overrides the whole prototype (which you usually don't want to do unless you are very aware of what you're doing). In JavaScript the prototype of a class is a function itself. You have access to your prototype by calling myClass.prototype which will return your prototype object.
The prototype can be extended (like every other object in JavaScript aswell)
quick example:
Example.prototype.showAlert = function(message){ alert(message); };
now you could call:
Example.prototype.showAlert("Hi");
// or calling it from an instance of Example:
var myExample = new Example();
myExample.showAlert("Hi")
So what your fiddle does is it replaces the whole prototype with this:
function () {
var showAlert = function (message) {
alert(message);
}
};
One could argue that this new function does not serve well as a prototype, but still IS callable (to come back to your original question). You can call it like this:
Example.prototype()
But nothing will happen since the only thing happening in this function is that it will declare a 'private' function inside this prototype-scope, which never gets called.
// start of prototype function
var showAlert = function (message) {
alert(message);
}
// end of prototype function
I cannot imagine this is what your intention was, so my advice is to stick to extending prototypes (but even that only if you have to).

Call member function from constructor

I'm new to Javascript's Object Oriented programming (from C++ area).
I want to know the best practice of calling member function from constructor.
Following is a working piece of code:
Obviously the "initialize" is declared ahead of the invocation "this.initialize();"
function Foo() {
this.initialize = function() {
alert("initialize");
};
this.hello = function() {
alert("helloWorld");
this.initialize();
};
this.initialize();
};
var f = new Foo();
f.hello();
If I change the code as following, it will fail at "this.initialize();".
Question 1 Why is that? Won't the Javascript Engine firstly read in all member function declarations of an object?
function Foo() {
this.initialize(); //failed here
this.initialize = function() {
alert("initialize");
};
this.hello = function() {
alert("helloWorld");
this.initialize();
};
};
var f = new Foo();
f.hello();
Then I made the change like this.
The function "initialize" is executed on construction, however, the calling for "this.initialize()" in function "hello" failed.
function Foo() {
this.initialize = function() {
alert("initialize");
}();
this.hello = function() {
alert("helloWorld");
this.initialize(); //failed here
};
};
var f = new Foo();
f.hello();
Question 2: is the first piece of code the only way of calling member function from the constructor?
Update:
if I have to define a function before using it, Question 3: why following code works?
function Foo() {
this.hello = function() {
alert("helloWorld");
this.initialize();
};
this.initialize();
};
Foo.prototype.initialize = function() {
alert("initialize");
};
var f = new Foo();
f.hello();
Question 4:
Why following code succeeded? (considering the "future" function is defined after calling)
alert("The future says: " + future());
function future() {
return "We STILL have no flying cars.";
}
In the first case, you are calling initialize before it is defined (on the next line).
In the second case, you are assigning the return value of the function (undefined in this case) to this.initialize, so when you try to invoke it as a function later, you get your error.
You may want to look further into the prototype pattern for making class-like structures - Douglas Crockford wrote a lot of useful stuff on this and it's useful for introductory learning: http://javascript.crockford.com/prototypal.html is a good start.
My answers inline (kind of)
Question 1 Why is that? Won't the Javascript Engine firstly read in all member function declarations of an object?
No, if they are defined as in your example, they will be executed in order, as the method doesn't exist yet, it will throw.
This way would be a different case (not OO, but to ilustrate):
function Foo(){
initialize(); //This would work
function initialize(){ ... } //Parser defines its function first
}
In this case, the parser does define the function declarations first, it's a different case.
The function "initialize" is executed on construction, however, the calling for this.initialize() in function "hello" failed.
this.initialize = function() {
alert("initialize");
}(); //This executes the function!
The problem with the above code is that you're not assigning the function to this.initialize, you're assigning the result of its execution, in this case undefined (because there is no return inside the function)
for example, if the code were:
this.initialize = function() {
return 2;
}();
then this.initialize would be... 2 !! (not a function).
Hope this helps. Cheers
Calling a method from constructor :
var f = new Foo();
function Foo() {
this.initialize(); //failed here
};
Foo.prototype.initialize = function() {
alert("initialize");
};
Execution Process:
1) All functions are created (that are defined at the root)
2) Code is executed in order
3) When Foo is created/constructed it executes the code.
It try's to run Initialize() but it doesn't find it,
it throws an internal exception that is caught and
then creates the Prototype method and executes it.
4) If the Foo.Prototype.initialize line came BEFORE the,
"var f = new Foo()" then the initialize function would have existed.
This process occurs for every line of execution.

Differences in javascript module pattern

Very simple question, not sure if there are any differences in these ways of creating a javascript "module". I'm hoping somebody can clarify it for me.
A)
var foo = function() {
var bar = function() {
console.log('test');
};
return {
bar: bar
};
};
B)
var foo = function() {
function bar() {
console.log('test');
};
return {
bar: bar
};
};
C)
var foo = function() {
this.bar = function() {
console.log('test');
};
return {
bar: this.bar
};
};
A and B are essentially the same, though there is a very minor difference between A and B due to function/variable hoisting, theoretically you could write code which would work in B but break in A, but practically speaking you'd have to really write weird code to do so.
C will work, but is conceptually wrong. The point of using this.funcName in a function is as a constructor (creating lots of objects using new Thing(). If you aren't using the function as a constructor you shouldn't be using that style as someone scanning the code may mistake the function as a constructor instead of its actual purpose which is a module.
At first, you forgot to execute the function expression: the module pattern is an IEFE. You just create a function.
Your last example is nonsense, it looks like a constructor function when assigning properties to this - and when executed as a IEFE it breaks (and using it with new has undesired effects; an when returning an object it's useless).
For the difference between the first and the second snippet see var functionName = function() {} vs function functionName() {}. In context of the module pattern, the function declaration is recommended.
//Javascript Module Pattern
var person = (function() {
var cname = 'CheapFlight';
return {
name: "Santosh Thakur",
getAge: function() {
return cname;
},
growOlder: function() {
return cname + " Updated";
}
};
}());
person.cname = "New Company"
console.log(person.cname);
console.log(person.name);
console.log(person.getAge());
console.log(person.growOlder());
prefix var before a function makes it a "class"-ish, this means you can make many versions of it. This goes for A
For example:
var hi = function()
{
var bye = function()
{
alert("bye");
}
bye(); // this will call bye
var something = new bye(); // this will create a new instance of bye();
}
var something = new hi();
something();
B means you can only call bar, not make a new instance of it inside the function.
C is the same as bar because of its scope
Class-ish:
var Dog = function( hair, type )
{
this.hair = hair;
this.type = type;
}
var fred = new Dog( "long", "Dalmation" );
alert( fred.hair );
var dave = new Dog( "short", "Poodle" );
alert( dave.type);
This is a class ^

How do I create a static member for an object in a namespace in JavaScript?

I've simulated a static class variable in Javascript.
MyObject.staticVariable = "hello world";
function MyObject() {
// do something
}
MyObject.prototype.someFunction = function() ...
I do not understand why this syntax works because I do not create MyObject explicitly (and how could I and still have a MyObject function?) before I assign the staticVariable property. But it does work, and I've seen it in many answers to the question along the lines of: how do I simulate a static class variable in Javascript?
How can I achieve this functionality when I declare the MyObject function in a namespace?
var Namespace = {};
// Not allowed, and for good reason in my eyes, as NameSpace.MyObject does not exist
Namespace.MyObject.staticVariable = "hello world";
Namespace.MyObject = function() {
// do something
}
Namespace.MyObject.prototype.someFunction = function() ...
Let me know if I can clarify and/or if my thinking is off. Thanks.
Function declarations are "hoisted" to the top of the current lexical environment.
This means that the function exists before any other code in that environment runs.
// This works
MyObject.staticVariable = "hello world";
function MyObject() {
// do something
}
// This does not
MyObject.staticVariable = "hello world";
var MyObject = function() {
// do something
}
In your Namespace example, it's effectively the same as the second example. This means that you'll needs to make sure the function assignment happens first.
Function declarations are hoisted, that is, you can use them above their instantiation. However, function expressions are not. In your example, you do this:
Namespace.MyObject.staticVariable = "hello world";
Namespace.MyObject = function() {
// do something
}
This won't work because Namespace.MyObject was not (and cannot be) created as a function declaration. As for simulating the functionality, use a function:
function add( prop, val ) {
Namespace.MyObject[ prop ] = val;
}
This will allow you to add properties to the object before you explicitly create them:
add( "staticVariable", "Hello World" );
Namespace.MyObject = function() {
// do something
};
Could you clarify what you mean by static class variable? JavaScript is not the same as Java.
If you want a constant that no client can modify, you can use closures.
For example:
var Namespace = {};
Namespace.MyObject = (function(){
var staticVariable = "hello world";
//this will be assigned to "MyObject"
return {
someFunction: function() {
//refer to staticVariable here
//no client can modify staticVariable directly;
//only through the methods you provide
}
};
})();

Refer to javascript function from within itself

Consider this piece of code
var crazy = function() {
console.log(this);
console.log(this.isCrazy); // wrong.
}
crazy.isCrazy = 'totally';
crazy();
// ouput =>
// DOMWindow
// undefined
From inside crazy() 'this' refers to the window, which I guess makes sense because normally you'd want this to refer to the object the function is attached to, but how can I get the function to refer to itself, and access a property set on itself?
Answer:
Don't use arguments.callee, just use a named function.
"Note: You should avoid using arguments.callee() and just give every function (expression) a name." via MDN article on arguments.callee
I think you are asking for arguments.callee, but it's deprecated now.
https://developer.mozilla.org/en/JavaScript/Reference/Functions_and_function_scope/arguments/callee
var crazy = function() {
console.log(this);
console.log(arguments.callee.isCrazy); // right.
}
crazy.isCrazy = 'totally';
crazy();
// ouput =>
// DOMWindow
// totally
As rfw said, this is the most straight forward way to go if the function has one single name:
var crazy = function() {
console.log(crazy);
console.log(crazy.isCrazy);
};
crazy.isCrazy = 'totally';
crazy();
In case it may have different names, or you wanted to pass it around, it must be wrapped in a closure:
var crazy = (function(){
var that = function() {
console.log(that);
console.log(that.isCrazy);
};
return that;
})();
crazy.isCrazy = 'totally';
crazy();
Bind the function to itself (taking a hint from answers by #ArunPJohny and #BudgieInWA):
crazy = crazy.bind(crazy);
This will give you access from the function to its properties via this.
> crazy()
function () {
console.log(this);
console.log(this.isCrazy); // works now
}
This seems like a better solution than the accepted answer, which uses the callee feature which is deprecated and doesn't work in strict mode.
You could also now have the function call itself recursively with this() were you so inclined.
We will call this self-thisifying. Write a little utility function:
function selfthisify(fn) { return fn.bind(fn); }
crazy = selfthisify(crazy);
crazy();
Or, if you prefer more "semantic" names, you could call it accessOwnProps.
If you're a syntactic sugar type of person, you could add a selfthisify property to the Function prototype:
Object.defineProperty(Function.prototype, 'selfthisify', {
get: function() { return this.bind(this); }
});
Now you can say
crazy.selfthisify();
You have to give it its own name, so:
var crazy = function() {
console.log(crazy);
console.log(crazy.isCrazy);
}
crazy.isCrazy = 'totally';
crazy();
The variable this is only applicable in the scope of an object, for instance, if you invoked your version of the crazy function with crazy.call(crazy), it will call the function in the context of the function crazy and all would be well.
This has to deal with the scope of the function crazy. If can pass any scope to a function using the function call().
Instead of
crazy();
Use
crazy.call(crazy);
For details refer
http://odetocode.com/blogs/scott/archive/2007/07/05/function-apply-and-function-call-in-javascript.aspxhttps://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/Callhttp://devlicio.us/blogs/sergio_pereira/archive/2009/02/09/javascript-5-ways-to-call-a-function.aspx
You can use the call method
var crazy = function() {
console.log(this);
console.log(this.isCrazy);
}
crazy.isCrazy = 'totally';
crazy.call(crazy);
// calls crazy using crazy as the target, instead of window:
// functionToCall.call(objectToUseForThis);
Though if your function only ever has one name, you can do this:
var crazy = function() {
console.log(crazy);
console.log(crazy.isCrazy);
}
crazy.isCrazy = 'totally';
crazy();
Easiest way to make the function itself available in its body is to do
var crazy = function crazy2() { crazy2(); }, it's okay for crazy and crazy2 to have the same name since the first occurrence is the name in the outer scope and the second is the name in the function body.
Or simply do function crazy() { crazy(); } which will define crazy in both scopes.
how can I get the function to refer to
itself?
The idea of 'itself' does not exist with functions. What you need is an object and not just a function. An object has knowledge of itself available through the keyword 'this'. Within a function, 'this' points to the global object - in this case the window object. But if you use your function as a constructor function to create an object (using the new operator) then the object's 'this' pointer will point to the object itself.
i.e this points to the object if you write:
var anObject = new crazy();
So you can re-write your code as follows:
var crazy = function() {
this.printMe = function(){
console.log(this);
console.log(this.isCrazy);
}
}
var anObject = new crazy(); //create an object
anObject.isCrazy = 'totally'; //add a new property to the object
anObject.printMe(); //now print
In case you wish to add the property before the object is created, then you have to add the property to the function's prototype as follows:
var crazy = function() {
console.log(this);
console.log(this.isCrazy);
}
crazy.prototype.isCrazy = 'totally'; //add the property to the function's prototype
var anObject = new crazy(); //invoke the constructor
See more on my blog for a detailed explanation of these concepts with code-samples.
Are you actually trying to create an object 'class'?
function crazy(crazyState) {
this.isCrazy = crazyState;
console.log(this);
console.log(this.isCrazy);
}
crazy.prototype.alertMe = function() { alert('I am '+ this.isCrazy +' crazy.'); }
var crazyObj = new crazy('totally');
crazyObj.alertMe();
crazyObj.isCrazy = 'not';
crazyObj.alertMe();
Funny that you should ask, mate. I just went through this same issue for a different purpose. The quick version of the final code is:
$a = function() {};
$ = function() {
if (!(this instanceof $)) {
return new $();
}
this.name = "levi";
return this;
};
//helper function
var log = function(message) {
document.write((message ? message : '') + "<br/>");
};
log("$().name == window.name: " + ($().name == window.name)); //false
log("$().name: " + $().name); //levi
log("window.name: " + window.name); //result
log();
log("$a instanceof $: " + ($a instanceof $)); //false
log("typeof $a: " + (typeof $a)); //function
log("typeof $: " + (typeof $)); //function
The critical piece:
if (!(this instanceof $)) {
return new $();
}
If this isn't pointing to an object of the right type, then it makes a new one, which will properly scope this. The rest of the code is just there for verification that it does indeed work as intended.
In order to make you code to work follow below
function crazy_object (crazy) {
this.isCrazy = crazy
}
var create_crazy = new crazy_object('hello') //creating object
console.log(create_crazy); //=> { isCrazy = 'hello' }
var crazy = function() {
console.log(this); //=> { isCrazy = 'totally' }
console.log(this.isCrazy); //=> 'totally'
}
create_crazy.isCrazy = 'totally'; //=> isCrazy = 'totally'
//below we pass the created object in function crazy.
//And doing that we can use the keywork `this` and refer to the object
crazy.call(create_crazy, null);
Using the call and apply method we can pass to a function a
property,and in that function we can use the property with the keyword this
For example:
function speak (message) {
console.log(`A person with name ${this.name} say ${message}`);
}
speak.call({ name: 'Roland' }, 'Javascript is awesome');
To use it with property:
function speak (message) {
console.log(`A person with name ${this.name} say ${message}`);
}
var name = 'Roland'
speak.call({ name }, 'Javascript is awesome');

Categories