views:

132

answers:

5

I'm always learned to define a function in JavaScript like this:

function myFunction(arg1, arg2) { ... }

However, I was just reading Google's guide to Javascript, it mentioned I should define methods like this:

Foo.prototype.bar = function() { ... };

Question: Is "Foo" in the example an Object, or is it a namespace? Why isn't the Google example the following code (which doesn't work):

prototype.bar = function() { ... };

UPDATE: In case it helps to know, all of my JavaScript will be called by the users browser for my web-application.

+1  A: 

Defining a prototype function is useful when creating constructors or 'classes' in JavaScript. e.g. a func that you will new

var MyClass = function(){};
MyClass.prototype.doFoo = function(arg){ bar(arg); }

but is of no use in plain old library functions e.g.

function doPopup(message){ /* create popup */};

There are several benefits of using a prototype function including but not limited to

  • speed
  • memory usage
  • extensibility

But, again, this is in the context of creating constructors for instantiable 'classes'

HTH

Sky Sanders
"a func that you will `new`" is aka a constructor, right?
Cole
@cole - absolutely, as stated.
Sky Sanders
Oh, oops - late night here - thanks :)
Cole
A: 

It works like so:

(function(){ // create an isolated scope
    // My Object we created directly
    var myObject = {
        a: function(x,y) {
            console.log('a');
        },
        b: function(x,y) {
            console.log('b');
            this.a(x,y);
        }
    };
})();

(function(){ // create an isolated scope

    // Create a Object by using a Class + Constructor
    var myClass = function(x,y) {
        console.log('myClass: constructor');
        this.b(x,y);
    };
    myClass.prototype = {
        a: function(x,y) {
            console.log('myClass: a');
        },
        b: function(x,y) {
            console.log('myClass: b');
            this.a(x,y);
        }
    };

    // Define a function that should never inherit
    myClass.c = function(x,y) {
        console.log('myClass: c');
        this.a(x,y);
    };

    // Create Object from Class
    var myObject = new myClass();
    // Will output:
    // myClass: constructor
    // myClass: b
    // myClass: a

    // Define a function that should never inherit
    myObject.d = function(x,y) {
        console.log('myObject: d');
        this.a(x,y);
    };

    // Test the world is roung
    console.log(typeof myClass.c, 'should be undefined...');
    console.log(typeof myClass.d, 'should be function...');
})();

(function(){ // create an isolated scope
    // If you are using a framework like jQuery, you can obtain inheritance like so

    // Create a Object by using a Class + Constructor
    var myClass = function(x,y) {
        console.log('myClass: constructor');
        this.b(x,y);
    };
    myClass.prototype = {
        a: function(x,y) {
            console.log('myClass: a');
        },
        b: function(x,y) {
            console.log('myClass: b');
            this.a(x,y);
        }
    };

    // Create new Class that inherits
    var myOtherClass = function(x,y) {
        console.log('myOtherClass: constructor');
        this.b(x,y);
    };
    $.extend(myOtherClass.prototype, myClass.prototype, {
        b: function(x,y) {
            console.log('myOtherClass: b');
            this.a(x,y);
        }
    });

    // Create Object from Class
    var myOtherObject = new myOtherClass();
    // Will output:
    // myOtherClass: constructor
    // myOtherClass: b
    // myClass: a
})();

(function(){ // create an isolated scope
    // Prototypes are useful for extending existing classes for the future
    // Such that you can add methods and variables to say the String class
    // To obtain more functionality
    String.prototype.alert = function(){
        alert(this);
    };
    "Hello, this will be alerted.".alert();
    // Will alert:
    // Hello, this will be alerted.
})();

Edit: Fixed code so that it will actually run in your browser if you copy and paste :-)

balupton
The comment on your last function() is a bit misleading -- there's nothing declared there which would require an isolated scope. In particular, String.prototype.alert is going to be visible on *all* String objects, once that function has been executed.
Dan Breslau
You're sure right about String.prototype.alert being inherited by all String objects.I prefer to use isolated scopes for every piece of block to allow for separation, such that in the future variables are never accidentally leaked through. So Plugin 1 and Plugin 2 should have their own scopes besides being in the same file :-)For the example, they are all meant to be separate individual examples to explain an overall point. Good to demo isolated scopes too :-)
balupton
A: 

Foo is both an Object and a namespace. See this question.

Using objects as namespaces prevents name collisions. That's always a good idea, but especially when you're developing and/or using shared libraries.

If you don't expect to be making multiple Foo objects (and so don't need the object-oriented style), you could create your functions as methods on a singleton object:

var Foo = {}
Foo.bar = function() { ... }

or

var Foo = {
    bar: function() {...},
    quux: function() {...}
};

You'd then simply call the function as:

Foo.bar()

(This kind of declaration is roughly equivalent to a static method in C++ or Java.)

Dan Breslau
Is there any side effects to using "Foo.bar = function() { ... };" vs just "function bar()" ?
Timmy
function bar() {} is not in a namespace (not that JavaScript really has namespaces, but using object properties serves the same purpose.) You could still avoid namespace collisions if you declare bar() inside of a closure, as balupton illustrated elsewhere on this page.
Dan Breslau
A: 

Your two examples are not functionally equivalent. The first example simply defines a function (probably a global one, unless you define it inside another function). The second example extends the prototype of a constructor. Think of it as adding a method to the class Foo.

Unless you're building a JavaScript library, my suggestion would be to use neither and use some kind of namespace system. Create a single global object that acts as a namespace through which you can access all your functions.

var MyObject = {
    utils: {
        someUtil: function() {},
        anotherUtil: function() {}
    },
    animation: {
        // A function that animates something?
        animate: function(element) {}
    }
};

Then:

// Assuming jQuery, but insert whatever library here
$('.someClass').click(function() {
    MyObject.animation.animate(this);
});

If you want to emulate classes in JavaScript, you would define the "class" as a function (the function itself being the constructor) and then add methods through the prototype property.

function Foo() {
    // This is the constructor--initialize any properties
    this.a = 5;
}
// Add methods to the newly defined "class"
Foo.prototype = {
    doSomething: function() { /*...*/ },
    doSomethingElse: function() { /*...*/ }
};

Then:

var bar = new Foo();
console.log(bar.a); // 5
bar.doSomething();
// etc...
musicfreak
A: 

I'm always learned to define a function in JavaScript like this: function myFunction(arg1, arg2) { ... }

There are two ways to define a function. Either as a function declaration

function foo(...) {
    ...
}

Or as a function expression

var foo = function() {
    ...
};

Read more here.

However, I was just reading Google's guide to Javascript, it mentioned I should define methods like this: Foo.prototype.bar = function() { ... };

This is specifically related to method creation for objects, not just normal, stand-alone functions. Assuming you have the base object declaration:

var Foo = function() {
    ...
};

Just like any other assignment, to assign a function to an object's property, you must use an assignment expression. You can do this two ways. The succinct and common way (as suggested by Google's reference)

Foo.prototype.bar = function() {};

Or, if you want to continue to use the declarative form of defining functions

function bar() {
    ...
};
Foo.prototype.bar = bar;

This is normally more verbose than necessary, but may be useful in situations where you want to assign the same method to multiple object prototypes.

Question: Is "Foo" in the example an Object, or is it a namespace? Why isn't the Google example the following code (which doesn't work): prototype.bar = function() { ... };

  1. Foo is an object. Although the concept can be expressed through the use of static objects, as I've shown in my answer to your other question, there is no such thing as namespaces in JavaScript. Further, especially in the example code given, Foo is likely intended to be an instantiated object, which precludes it from being behaving like a namespace.

  2. Of course it doesn't work: prototype has not been defined as an object (unless, of course, you define it as such). The prototype property exists on every object (a function is also an object), which is why you can do Foo.prototype.bar = ...;. Read more here.

Justin Johnson