Say I have the following code:
var album = new MyObject('album');
Assume that when the object is constructed, a bunch of properties relative to only albums are loaded via AJAX. Would it be possible to create an Album class so that at a later point, I may just do this:
var anotherAlbum = new Album();
The Album constructor would automatically set the properties that are unique to album objects, based on what was loaded when creating MyObject('album')
JavaScript is prototypal, not classical, so if you think in terms of classes, you're doing it wrong.
You don't have to use the new operator at all. You can create a new object using the object literal:
var myObject = {attr1: 'val1', attr2: 'val2'};
Then you can create a new instance of that object:
var mySecondObject = Object.create(myObject);
Now you can change the attributes of mySecondObject, and if it has methods you can overload them just as easily:
mySecondObject.attr1 = "Hello";
mySecondObject.attr2 = function() {
return "World!";
};
And then mySecondObject will of course have all the properties that you gave myObject at creation.
Be aware that this is a simple version, and that this leaves all attributes 'public'. If you need some privacy, it can be achieved by adding some functions to the mix. It's a bit more complicated though, so let me know if you're interested...
JavaScript "classes", just like any other object, can be dynamically created. So, yes, this can be done.
You would do something like this in the code handling the AJAX response (assuming that the AJAX response was providing the name of the new "class", and it's in a variable called newClassName):
window[newClassName] = function() {
// New class name constructor code
}
window[newClassName].prototype = {
someProperty: "someValue",
someMethod: function(a, b) {
},
someOtherMethod: function(x) {
}
}
This is actually the only for of inheritance that JavaScript has. JavaScript has prototypal inheritance (which can be used to recreate classical inheritance). That means that inheritance is from another object, not a class definition.
To create an object that has all the properties of another object is simple:
function Album() {
// do whatever initialization you need to here, all the properties of album
// are available on 'this'
// e.g.,
doSomething(this.albumName);
}
Album.prototype = album;
var anotherAlbum = new Album();
You can use Douglas Crockford's Functional Inheritance Pattern. Code from Javascript Good Parts book
var mammal = function (spec) {
var that = {};
that.get_name = function ( ) {
return spec.name;
};
that.says = function ( ) {
return spec.saying || '';
};
return that;
};
var myMammal = mammal({name: 'Herb'});
var cat = function (spec) {
spec.saying = spec.saying || 'meow';
var that = mammal(spec);
that.purr = function (n) {
var i, s = '';
for (i = 0; i < n; i += 1) {
if (s) {
s += '-';
}
s += 'r';
}
return s;
};
that.get_name = function ( ) {
return that.says( ) + ' ' + spec.name +
' ' + that.says( );
return that;
};
var myCat = cat({name: 'Henrietta'});
It uses functions to decorate existing javascript objects with new functions and properties. Like this you can add new functions and properties on the fly to your existing object
You can do this
var NewClass=function(){
this.id=null;
this.name=null;
this.show=function(){
alert(this.id+" "+this.name;
}
}
NewClass.prototype.clear=function(){
this.id=null;
this.name=null;
};
...
var ins1=new NewClass();
var ins2=new NewClass();
Related
I am using the following JavaScript, which I believe is Object Oriented. Others disagree that this is Object Oriented.
window.onload = function () {
var myphotos;
var currentImage;
var totalImageCount;
function initSlideImages(){
myphotos = new Array (
"slide_image_1",
"slide_image_2",
"slide_image_3",
"slide_image_4",
"slide_image_5"
);
currentImage = 1;
totalImageCount = myphotos.length;
}
initSlideImages(); // Initialise slideshow
function nextPhoto(){
currentImage++;
if (currentImage > totalImageCount) {
currentImage = 1;
}
var theImage = document.getElementById("slide_image");
if (theImage != null){
theImage.src = "images/portfolio/" + theImage.id + "_" + currentImage + ".jpg";
}
}
function previousPhoto(){
currentImage--;
if (currentImage == 0){
currentImage = totalImageCount;
}
var theImage = document.getElementById("slide_image");
if (theImage != null){
theImage.src = "images/portfolio/" + theImage.id + "_" + currentImage + ".jpg";
}
}
var slide_prev = document.getElementById("slide-prev");
if (slide_prev != null){
slide_prev.onclick = function() {
previousPhoto();
}
}
var slide_next = document.getElementById("slide-next");
if (slide_next != null){
slide_next.onclick = function() {
nextPhoto();
}
}
}
Would you agree this is Object Oriented JavaScript? I believe it is. There is Constructor functions (eg. initSlideImages) and literal notation objects (eg. myphotos, currentImage). "slide_prev" and "slide_next" are methods as far as I am aware from my understanding of OO JS.
Am I right, or do I need to revisit Object Oriented JavaScript 101?
This is a traditional JavaScript, no way related to OOJS. OOJS creates new objects and member functions. An example of OOJS would be:
function Person() { }
var person1 = new Person();
var person2 = new Person();
Or something like this:
var myObject = {
};
And this is how you define the methods:
function myObject(){
this.iAm = 'an object';
this.whatAmI = function(){
alert('I am ' + this.iAm);
};
};
Or, the literal version is like this:
var myObject = {
iAm : 'an object',
whatAmI : function(){
alert('I am ' + this.iAm);
}
}
References
Introduction to Object-Oriented JavaScript - MDN
The Basics of Object-Oriented JavaScript
There is Constructor functions (eg. initSlideImages)
The function initSlideImages is not used as a constructor.
and literal notation objects (eg. myphotos, currentImage).
Those are not literals in the sense I think you mean. They're just plain old global variables. And even if they were, that doesn't have much to do with OOP in JavaScript.
"slide_prev" and "slide_next" are methods as far as I am aware from my understanding of OO JS.
Those are not methods.
Am I right, or do I need to revisit Object Oriented JavaScript 101?
You're pretty far from right. None of the terminology in your post is used accurately. Yes, you should start over at the beginning.
Your code looks more procedural than object oriented. Object oriented often implies encapsulation and inheritance. In object oriented JavaScript your code would typically look something like this:
// Constructor
function Slider(photos) {
this.photos = photos;
this._init();
}
// Methods
Slider.prototype._init = function() {
// grab selectors, add events, initialization logic
};
Slider.prototype.count = function() {
return this.photos.length;
};
Slider.prototype.next = function() {
//...
};
Slider.prototype.prev = function() {
//...
};
var photos = [
"slide_image_1",
"slide_image_2",
"slide_image_3",
"slide_image_4",
"slide_image_5"
];
var slider = new Slider(photos);
slider.next(); // go to next slide
This is not object oriented JS.
There is no object instantiation using 'new' operator. There are no properties defined for a 'class like' constructor function.
Refactored object oriented code is as below:
function SlideImages(){
this.myphotos = new Array (
"slide_image_1",
"slide_image_2",
"slide_image_3",
"slide_image_4",
"slide_image_5"
);
this.currentImage = 1;
this.totalImageCount = this.myphotos.length;
}
//Object creation
var slideImages = new SlideImages();
//accessing object properties
console.log(slideImages.currentImage) //output:1
I find this is most recommended way to do inheritance in javascript.
function extend(Child, Parent) {
var F = function(){};
F.prototype = Parent.prototype;
Child.prototype = new F();
}
what if I already have methods in child's prototype, aren't they will overwrite, shouldn't we preserve them.
function extend(Child, Parent) {
var c = child.prototype;
var oldProto = new C();
var F = function(){};
F.prototype = Parent.prototype;
Child.prototype = new F();
for(var i in oldProto ){
Child.prototype[i] = oldProto[i]
}
}
I'm not sure if this is any good to you, but it's well important to remember: prototypes are not the same things as classes. What you're doing is trying to make JS behave like a traditional OO language, which is trying to teach a dolphin to dance ballet, or forcing a tiger to become vegan: Admirable, but destined to end in tears.
I can't really see why you'd want to use the extend function to do whatever it is you're trying to do. Why not simply use this:
function Parent()
{};
function Child()
{};
//augment parent proto
Parent.prototype.parentMethod1 = function()
{};
//set Child's proto to Parent
Child.prototype = new Parent();
Child.prototype.constructor = Child;
//Then augment the Child's prototype
Child.prototype.childMethod1 = function()
{};
var foo = new Child();
foo.parentMethod1();//works
foo.childMethod1();//works, too
IMO, this solves the problem entirely. Sure, it's a tad more verbose, but OOP always is.
The pattern you're trying to achieve is called multiple inheritance. And it's highly not recommended for the use because of the issue you're experiencing, called diamond problem. Just use mixin pattern instead.
The code below is the one of the best I have seen for doing inheritance in JavaScript.
Object.create(proto [, propertiesObject ]) is discussed on MDN here.
Below, Jon defines a base empty object called ExtendBase then adds a function property called extend which is not enumerable which takes as its argument a single new object.
That object should contain enumerable properties such as methods and data that will be added to the base object.
He gets all the enumerable properties from the passed object, then creates an array of the necessary descriptors to pass into Object.create using those properties' names. He then uses the parent object as the prototype and resultant descriptors as new properties to be added to the child object directly in the Object.create() call.
As you can see, you can use an object argument with properties, including methods, to extend a parent without losing that passed object's properties with the result being a child object with the parent as the prototype and the enumerable objects of the passed object added directly to the child.
However, this maintains a clean prototype chain while intending to extend parent objects using other objects which are created sanely to extend the parent into a new child in a way that makes sense:
Live sample here (Press F12 in Chrome for console output, or use FireBug in FireFox, etc.)
JavaScript:
// Original Author: FireFly - Jonas Höglund - ##javascript channel
// on irc.freenode.net - see THANKS File. Updated to private data
// members and passable initial parameters by Scott Sanbar
///////////////
// Library code
///////////////
var ExtendBase = {};
Object.defineProperty(ExtendBase, 'extend', {
enumerable:false, value:function (obj) {
'use strict';
var descs = {};
Object.getOwnPropertyNames(obj).forEach(function (key) {
descs[key] = Object.getOwnPropertyDescriptor(obj, key)
});
return Object.create(this, descs);
}
});
///////////////
// Sample Usage
///////////////
function PersonObj(nam) {
return {
name:new function () {
var name = nam;
this.set = function (value) {
name = value;
};
this.get = function () {
return name;
}
},
// A person can tell you its name.
talk:function () {
return "Hello, I'm " + this.name.get();
}
}
}
;
function WorkingPersonObj(occ) {
return {
occupation:new function () {
var occupation = occ;
this.set = function (value) {
occupation = value;
};
this.get = function () {
return occupation;
}
},
// A working person also tells you their occupation when they talk.
talk:function () {
return Person.talk.call(this) + " and I am a " + this.occupation.get();
}
}
}
;
var hush = {
hush:function () {
return "I am supposed to be quiet";
}
};
var Person = ExtendBase.extend(new PersonObj('Harry'));
var WorkingPerson = Person.extend(new WorkingPersonObj('wizard'));
var wp1 = WorkingPerson.extend(hush);
console.log(wp1.talk()); // "Hello, I'm Harry and I am a wizard"
console.log(wp1.hush()); // "I am supposed to be quiet"
wp1.name.set("Elijah");
wp1.occupation.set("prophet");
console.log(wp1.talk()); // "Hello, I'm Elijah and I am a prophet"
console.log(wp1.name.get());
console.log(wp1.occupation.get());
Considering object creation patterns with private properties, one way to do is :
function MyStack (){
var list = [],
index = 0;
this.push = function(val){
return list[index++] = val;
};
this.pop = function(){// ...}
}
var stack1 = new MyStack(); stack1.push(5);
var stack2 = new MyStack(); stack2.push(11);
Problem with this: Every instance of Stack has it's own copy of methods 'push' and 'pop'.
Another way for implementing constructor method is:
function MyStack(){
this.list = [];
this.index = 0;
}
MyStack.prototype = {
insert: function(val){
return this.list[this.index++] = val;
},
pop:function(){//...}
}
Problem here: We lose the privacy of list and index.
Is there a way, such that we can have both methods reuse among instances and privacy of properties ?
I understand that we can have this for methods that don't operate on any state of the object, but I am talking more about those methods that do operate on the state.
Yes. I've edited this code so it's actually fully functional as you had intended it to work. It seems a bit redundant to me, but, it does provide you the ability to provide a public interface, but to keep your variables private and control the way the user interacts with them.
function MyStack(){
var list = [];
var index = 0;
this.getIndex = function(){
return index;
}
this.setIndex = function(val){
index = val;
}
this.list = function(val){
if(val){
// setter if a value was provided. Illustrating how you can control
// index, which I assume is the point of having these things private
// to begin with
return list[this.setIndex(this.getIndex() + 1)] = val;
}
// always return list - acts like a getter
return list;
}
}
MyStack.prototype = {
insert: function(val){
return this.list(val);
},
pop:function(){}
}
var stack1 = new MyStack();
stack1.insert(5);
var stack2 = new MyStack();
stack2.insert(11);
You should check out John Resig's Simple Javascript Inheritance. It is a great read, and it has been extended to provide support for privates, aptly called Privates.js;
A constructor function may return any object (not necesserily this). One could create a constructor function, that returns a proxy object, that contains proxy methods to the "real" methods of the "real" instance object. This may sound complicated, but it is not; here is a code snippet:
var MyClass = function() {
var instanceObj = this;
var proxyObj = {
myPublicMethod: function() {
return instanceObj.myPublicMethod.apply(instanceObj, arguments);
}
}
return proxyObj;
};
MyClass.prototype = {
_myPrivateMethod: function() {
...
},
myPublicMethod: function() {
...
}
};
The nice thing is that the proxy creation can be automated, if we define a convention for naming the protected methods. I created a little library that does exactly this: http://idya.github.com/oolib/
I think in both approaches you mentioned, When ever object is created using constructor pattern the properties will get copied to its objects. This you mentioned for the 1st approach as the concern. I feel the same will be applied for the second approach also along with your concern in this approach.
We generally go to the second approach you mentioned when ever we want to extend the properties of "MyStack" to some other class.
Lets say i want to extend your class MyStack to MyTest like below
var dummy = function();
dummy.prototype = MyStack.prototype;
var MyTest = function(){
};
MyTest.prototype = new dummy(); // Assigning MyStack properties to MyTest
var obj = new MyTest();
I need some simple objects that could become more complex later, with many different properties, so i thought to decorator pattern.
I made this looking at Crockford's power constructor and object augmentation:
//add property to object
Object.prototype.addProperty = function(name, func){
for(propertyName in this){
if(propertyName == name){
throw new Error(propertyName + " is already defined");
}
}
this[name] = func;
};
//constructor of base object
var BasicConstructor = function(param){
var _privateVar = param;
return{
getPrivateVar: function(){
return _privateVar;
}
};
};
//a simple decorator, adds one private attribute and one privileged method
var simpleDecorator = function(obj, param){
var _privateVar = param;
var privilegedMethod1 = function(){
return "privateVar of decorator is: " + _privateVar;
};
obj.addProperty("privilegedMethod1", privilegedMethod1);
return obj;
}
//a more complex decorator, adds public and private properties
var complexDecorator = function(obj, param1, param2){
//private properties
var _privateVar = param1;
var _privateMethod = function(x){
for(var i=0; i<x; i++){
_privateVar += x;
}
return _privateVar;
};
//public properties
var publicVar = "I'm public";
obj.addProperty("publicVar", publicVar);
var privilegedMethod2 = function(){
return _privateMethod(param2);
};
obj.addProperty("privilegedMethod2", privilegedMethod2);
var publicMethod = function(){
var temp = this.privilegedMethod2();
return "do something: " + temp + " - publicVar is: " + this.publicVar;
};
obj.addProperty("publicMethod", publicMethod);
return obj;
}
//new basic object
var myObj = new BasicConstructor("obj1");
//the basic object will be decorated
var myObj = simpleDecorator(obj, "aParam");
//the basic object will be decorated with other properties
var myObj = complexDecorator(obj, 2, 3);
Is this a good way to have Decorator Pattern in javascript?
Are there other better ways to do this?
There are various implementations of the Decorator pattern in Javascript on Wikipedia and other sites - (1), (2), (3). The pattern is defined as:
the decorator pattern is a design pattern that allows new/additional behaviour to be added to an existing object dynamically.
Object extension is already build into the language itself. Objects can be easily extended, and properties can be added anytime. So why should you have to jump through hoops to achieve this? Shouldn't something like this suffice:
var person = { name: "Jack Bauer" };
// Decorated the object with ability to say a given phrase
person.say = function(phrase) {
alert(phrase);
}
// Using the decorated functionality
person.say("Damn it!");
If you want a method to apply to all objects that were created using this function, then add that method/properties to the function's prototype.
Update: If you have clearly defined pieces of functionality which can be mixed and matched as needed into certain types of objects, then the MooTools approach of extending and mixing in behavior into objects is nicely done. To give an example, consider a UI component that can be resized, dragged around with a handle, and deleted by clicking a tick mark at the top-right corner. You may not want to create each component with these behaviors but define all these behaviors separately each into their own object. And later mix in these behaviors into each type of component as needed.
var Resizable = {
...
};
var Draggable = {
...
};
var Deletable = {
...
};
var someLabel = new Label("Hello World");
// one way to do it
someLabel.implement([Resizable, Draggable, Deletable]);
// another way to do it
someLabel.implement(Resizable);
someLabel.implement(Draggable);
someLabel.implement(Deletable);
It looks better and more intuitive (to me) than doing something like
var awesomeLabel = new Resizable(new Draggable(new Deletable(someLabel)));
because we are still dealing with a label, and not some resizable, or some draggable, or some deletable object. Another small point, but still worth mentioning is that the parentheses start getting unmanageable after 3 or 4 decorators, especially without good IDE support.
I do not understand quite completely how to apply constructors on this object creation method:
var MyObject = {
...
};
I know that you can do:
var MyObject = new Object();
MyObject.prototype.constructor = function(props)
{
...
}
or...
function MyObject(prop1, prop2)
{
this.prop1 = prop1;
...
}
Can I do something like this?
var MyObject = {
MyObject: function(prop1, prop2)
{
...
}
}
No, you can't, that would simply create a (static) method on MyObject -- MyObject.MyObject. In JavaScript, a constructor is the class. Class methods and properties are created either inside the constructor using this. or by adding to the prototype (outside of the constructor) using MyClass.prototype.. You can think of "objects" in JavaScript as static classes.
Example from here
Creating constructors
To write your own constructors, you use the this keyword within the constructor to refer to the newly-created object. The constructor initializes the object.
In the example below:
The make7Table constructor creates a multiplication table for number 7
The size property is introduced to keep track of the number of elements
The value of each element is initialized
function make7Table(numElements)
{
this.size = numElements;
var cnt;
for(cnt = 0; cnt < numElements; cnt++)
{
this[cnt] = cnt*7;
}
}
// Use the constructor to create and initialize an array.
myArray = new make7Table(10);
document.write(myArray[5]);
document.write("This table has " + myArray.size + " elements");
To run the code, paste it into JavaScript Editor, and click the Execute button. myArray[5] retrieves the element with the value of 5*7 = 35.
var MyObject = new Object();
MyObject.prototype.constructor = function(props)
{
...
}
is the same as
var MyObject = {};
MyObject.prototype.constructor = function(props)
{
...
}