6

In PHP, you can do something like that:

class myClass() {
    function doSomething(someVar) {
         // do something here
    }
    // etc... (other methods and properties)
}

Then, of course, you could call that method after instanciating the class, like that:

$myObj = new myClass();
$myObj->doSomething();

But you would also have the option to call the method as a standalone function, without instantiating the class (but you'd have to pay attention to dependencies in that function), like that:

myClass::doSomething();

I believe it's something borrowed for C++... It's known as a Scope Resolution Operator (Paamayim Nekudotayim in the PHP code...)
http://en.wikipedia.org/wiki/Scope_resolution_operator#PHP
http://www.php.net/manual/en/language.oop5.paamayim-nekudotayim.php

How would you do something like that in JavaScript? It doesn't seem to be possible. Maybe I am approaching this the wrong way, I should disclose what I'm trying to achieve...

I simply have a function, which goes like this:

function submitContactForm(form) {
    // pretty JavaScript...
}

And I'm happy with it being a function. But I'd like to implement a "resetContactForm()" but would like to have it attached somehow to the submitConatctForm function.

I know I could probably do this:

var contactForm = {
   "submit" : function(form) {
       //...
   },
   "reset" : function(form) {
       //...
   }
}

And I'd have answered my own question like that...

But, besides the fact that I don't like this syntax, and would like to avoid it, there is also the fact that the above structure cannot be used as a class definition, it is not the same than in PHP... so going back to the original question: is there a way to have a JavaScript structure that can be used as a class definition and a collection of stand-alone functions at once?

Rolf
  • 5,550
  • 5
  • 41
  • 61
  • 3
    That's not JSON syntax, it's JavaScript's object syntax, you don't need the quotes if there aren't any special characters. Paamayim Nekudotayim has no place in JavaScript as there are no classes, no static methods. – elclanrs Jan 06 '14 at 18:32
  • Well the quotes don't hurt. Thanks anyway. I've removed "JSON" from my post. – Rolf Jan 06 '14 at 18:34
  • 1
    [Javascript doesn't have classes](http://stackoverflow.com/questions/387707/whats-the-best-way-to-define-a-class-in-javascript?rq=1) so there's no use for the scope resolution operator. Follow the advice in that link for attaching methods to objects in Javascript. Pretty much like @Deepsy just said. – bishop Jan 06 '14 at 18:35
  • you can only use `::` for static functions and constants. You don't just have the option of deciding to use `->` or `::`, you use `->` for functions and constants of instantiated objects, and use `::` for static functions and constants of classes. – chiliNUT Jan 06 '14 at 18:43

4 Answers4

3

You are mis-understanding prototypal inheritance - you actually can use your second example as a "class" definition and the methods can be invoked either from the "class" or from the "instance":

// This is a normal JavaScript object
// not JSON as another commenter pointed out.
var ContactForm = {
   submit: function(form) {
       form = form || this.form;
       // general contact form submission implementation
   },
   reset: function(form) {
       form = form || this.form;
       // general contact form reset implementation
   },
   setForm: function(form) {
       this.form = form;
   }
};

// Now we will create an instance of the contactForm "class"
// We are setting the prototype of `firstContactForm`
// to point at the `contactForm` object.
// If we wanted to we could create a function on the
// ContactForm object (e. g. `create`) that would invoke
// Object.create for us. (i. e. `ContactForm.create()`)
var firstContactForm = Object.create(ContactForm);
firstForm.setForm(document.getElementById("someForm"));
firstForm.reset();

 // But, we can also use the function as a "static":
 ContactForm.reset(document.getElementById("someForm"));

In answer to the other part of your question, if you want to make it something that is invokable "stand-alone" you can also allow the data to be passed in directly, as we are doing in the example with our form = form || this.form; checks in submit and reset.

Alternately, you can use call and apply (as @elclanrs points out in his answer) and always use this.form:

 ContactForm.reset.call({form: document.getElementById("someForm")});
Community
  • 1
  • 1
Sean Vieira
  • 155,703
  • 32
  • 311
  • 293
  • @elclanrs's solution is good, and might be a more correct answer from a "strictly technical" but yours is closer to what I had in mind and somehow easier to understand... – Rolf Jan 06 '14 at 18:56
3

In JavaScript's object syntax you don't need quotes if there aren't any special characters:

var obj = {
   key: function() {
     ...
   },
   ...
}

Paamayim Nekudotayim has no place in JavaScript as there are no classes, no static methods. But JavaScript has a dynamic context, what we call this. It is not in any way similar to this in PHP or other classical inheritance languages, other than the name of the keyword.

A typical JavaScript "class" looks like:

// A "Class"
var Person = (function(){
  // Private stuff, shared across instances
  var instances = [];

  // The constructor AKA "__construct"
  function Person(name) {
    this.name = name;
    instances.push(this); // keep track of instances
  }

  // Static methods, attached to the constructor
  // don't need an instance
  Person.instances = function() {
    return instances;
  };

  // Public methods
  Person.prototype = {
    say: function() {
      return this.name +' says hello!';
    }
  };

  return Person;
}());

Now, how you use this:

var mike = new Person('Mike');
mike.say(); //=> Mike says hello!
Person.instances().length; //=> 1

So good so far. As for "scope resolution" in JavaScript, you can pass the context explicitly; knowing that this is dynamic, you can borrow the Person's say method and invoke it in any other context, for example:

Person.prototype.say.call({name:'John'}); //=> John says hello!
elclanrs
  • 92,861
  • 21
  • 134
  • 171
2

You can make it a class like this:

function ContactForm(form) {
    this.form = form;
}

ContactForm.prototype.submit = function() {
    console.log('submiting: ' + this.form);// do something with the form
}

ContactForm.prototype.reset = function() {
    console.log('reseting: ' + this.form);
}



var someForm = ...;

var form = new ContactForm(someForm);

form.submit();
form.reset();

Or if you want to use them statically you can do as following:

var ContactForm = (function() {
    var reset = function(form) {
        console.log('reseting' + form);
    };

    var submit = function(form) {
        console.log('submiting' + form);
    }

    return {
        submit: submit,
        reset: reset
    }
}()); // note that this is self-executing function

and use it like

ContactForm.submit(form);
ContactForm.reset(form);
Deepsy
  • 3,769
  • 7
  • 39
  • 71
  • Well then i'd be instantiating an object, which is what I'm trying to avoid... because I don't want to track that instance, and don't want to be creating superfluous instances. OK maybe i'm over-doing it, your code is perfectly OK (thanks), from a pragmatic perspective and may actually be the best answer. – Rolf Jan 06 '14 at 18:39
1

Reading Sean Vieira and elclanrs' answers gave me better insight. I've come up with this code as a proof of concept, and to make sure I understood what I was reading. This is essentially a simplified version of elclanrs' answer:

function contactForm(form) {
    this.form = form;
}
contactForm.prototype.submit = function() {
    alert("submit "+this.form);
}
contactForm.prototype.reset = function() {
    alert("reset "+this.form);    
}

// Without instanciating:
contactForm.prototype.submit.call({form:'form2'});

// With instance:
myForm = new contactForm('form1');
myForm.reset();

So it seams this "functionality" is already available in JavaScript, albeit in a different, less straightforward form.

Also, Sean Vieira's approach, completed:

var ContactForm = {
   submit: function(form) {
       form = form || this.form;
       alert("submit "+form);
   },
   reset: function(form) {
       form = form || this.form;
       alert("reset "+form);
   },
   createForm: function(form) {
       var myForm = Object.create(this);
       myForm.setForm(form);
       return(myForm);
   },
   setForm: function(form) {
       this.form = form;
   }
};

// instanciated
myContactForm = ContactForm.createForm('Me Form');
myContactForm.submit();

// no instance
ContactForm.submit("Some Form");

Also (my contribution), how about using wrapper functions, like that? Looks like a decent option to me.

function contactForm(form) {
    this.form = form;
    this.submit = function() {
        submitContactForm(this.form)
    }
    this.reset = function() {
        resetContactForm(this.form);
    }
}
function submitContactForm(form) {
    alert("submit "+form);
}
function resetContactForm(form) {
    alert("reset "+form);    
}

// Without instanciating:
submitContactForm('form2');

// With instance:
myForm = new contactForm('form1');
myForm.reset();

There is no perfect solution...

Rolf
  • 5,550
  • 5
  • 41
  • 61