5

This question is more about support and backwards compatibility. I have tested the following code.

function newFunc() {}

newFunc.prototype = {

    literal : {
        init : function() {
            console.log(this);
            this.test();
        },
        test : function() {
            console.log('test');
        }
    }
}

var inst = new newFunc();

inst.literal.init();

This works, though I've not seen object literals as prototypes in any other code. Is there a reason for this? This seems like a logical way of coding to me though I don't want to pursue it if it has serious pitfalls.

David Barker
  • 14,484
  • 3
  • 48
  • 77
  • Well, anyone can swap `literal.init` with whatever they feel like, and the change will reflect on every instance ever created. This would not be possible if there was no intermediate `literal` stage. – Jon May 29 '13 at 13:07
  • @jon I understand that, I guess I really want to know if using literals as prototypes is, for lack of other words, 'ok' :-) – David Barker May 29 '13 at 13:09
  • 1
    I would say that like nearly everything in JavaScript, `prototype` is just an object and can therefore be created or reassigned like any other object. I have seen this pattern quite a few times but I'm also curious if this has some disadvantages. I would say 'no' ...but good question. – basilikum May 29 '13 at 13:09
  • There's only the "standard" issues of an existing `prototype` object - apart from that I can't think of any particular issues with doing so. – Qantas 94 Heavy May 29 '13 at 13:39
  • Biggest problem I see is that `this` in your methods will never be a reference to each unique `new newFunc()` instance, so you'll never be able to work with the unique data in each instance. –  May 29 '13 at 13:41
  • @squint sure it will. The only "oddity" here is that there's only one property that's _really_ on the prototype, the one called `literal`. The other methods are then just properties of that object, but that doesn't break the way that `this` works. – Alnitak May 29 '13 at 13:45
  • @Alnitak: No it won't. `inst.literal.init();` The value of `this` will be the `newFunc.prototype.literal` object, which is shared among all the instances created. –  May 29 '13 at 13:46
  • 2
    @squint hmm, ok, let me check... Yeuch, yes, you're right. That's horrid. – Alnitak May 29 '13 at 13:47
  • 1
    @squint thanks for the comments, this is probably why I won't be going down this route. No pun intended... Thanks – David Barker May 29 '13 at 14:03

4 Answers4

2

It's perfectly normal to use an object literal to create the prototype for a function, but normally only as the actual value of the prototype object.

What's unusual is doing what you've done and include a nested object within the prototype.

In effect you've only added one object to the prototype, the one named literal. All of the methods are then properties of that object. It's technically valid syntax, but I've never seen it used before. As @squint points out in the comments, it also appears to break the way that the this variable works, because it binds this to the "next left" property that was used in the function call:

var inst = new newFunc();
inst.literal.init();
> Object { init: function, test: function }

i.e. this has been set to point at the .literal object, and not at the actual instance that has been created.

Alnitak
  • 334,560
  • 70
  • 407
  • 495
  • actually it really was only an example, I would intend to have either multiple nested objects as prototypes or a mixture of nested objects and function prototypes. Thanks for the answer this was the sort of clarification I was looking for. – David Barker May 29 '13 at 13:52
  • As for edit, yes this is exactly what I would have expected without calling the object with a context. Thanks again. – David Barker May 29 '13 at 13:53
  • @DavidBarker yup - don't nest properties in the prototype. It breaks things in unexpected ways. In the example I _did_ call it with a context, but that context was `inst.literal`, and not `inst`. I'm a pretty experienced JS programmer and that caught me out. – Alnitak May 29 '13 at 13:54
  • Yes, that is a concern and could cause problems down the line with readability and mind bending contextual changes. I think I'll stick to splitting out the nested literal either as a stand-alone literal or class prototype. – David Barker May 29 '13 at 14:01
  • @DavidBarker splitting it out (and storing a reference to it in the prototype) won't help - there's simply no good reason to put _nested_ properties in the prototype. – Alnitak May 29 '13 at 14:04
  • I think you misunderstood me, I woulnd't do that. I would remove it from the prototype altogether ;-) – David Barker May 29 '13 at 14:05
1

Yes, using literals for prototype is correct. For example Mozilla explicitly uses a literal in the prototype's documentation:

var Customer = function(name) {
    this.name = name;
}

var Person = { // this is a literal
    canTalk : true,
    greet : function() { /* ... */ }
}

Customer.prototype = Person;

Some explanation: Value of prototype is an object. It doesn't matter how the object was created - using simply {} is fine. It is often initialized using something like MyClass1.prototype = new MyClass2(), but new just creates a new object. It also sets the prototype property and executes the constructor (MyClass2) but on the new object, it doesn't affect MyClass1 in any way (see explanation here).

Using a nested literal doesn't make a difference. In the question, the prototype is set to { literal : { ... } }. What actually happens when you call inst.literal.init() is:

  1. The runtime looks at inst and checks whether the object has a value assigned for property literal.
  2. inst dos not have such property, therefore the runtime continues with its prototype property
  3. inst.prototype references the literal object to which it was initialized. This object has assigned a value for property literal.
  4. inst.literal therefore evaluates to the nested literal inst.prototype.literal
  5. The literal object does have a value for property init
  6. The init() function is called

This is one of the principles of JavaScript (ECMA Script) so there should be no compatibility issues.

Community
  • 1
  • 1
Mifeet
  • 12,949
  • 5
  • 60
  • 108
  • you did notice there's actually a _nested_ `literal` in the OP's code? – Alnitak May 29 '13 at 13:42
  • I'm aware what a prototype is, and how it works. My question was, as Alnitak mentioned more about nesting objects as prototypes. – David Barker May 29 '13 at 13:58
  • 2
    @Mifeet it does make a difference - per comments on the question that nested property breaks `this`. – Alnitak May 29 '13 at 14:05
  • Unfortunately, the entire context is changed when working with the nested object. Unless you explicitly call it with a given context I don't think it will be feasible – David Barker May 29 '13 at 14:09
  • @Alnitak All right, one always needs to be careful about `this`. Obviously, you're answer was the right one for David – Mifeet May 29 '13 at 14:12
0

What you are doing is setting the prototype to be a JavaScript object with several properties. This is perfectly acceptable, as functions act very similarly to objects in JavaScript. All JavaScript does is passes the reference to this prototype property down to inherited objects, so they will not have a function they can access, but an object instead in this case.

You can see that this is actually done in the MDN documentation:

var Person = {
    canTalk : true,
    greet : function() {
        if (this.canTalk) {
            console.log("Hi, I'm "+this.name)
        }
    }
}
Customer.prototype = Person;

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FObject%2Fprototype

Alex W
  • 37,233
  • 13
  • 109
  • 109
  • I'm sorry, how is this a misuse of _literal_? – Evan Davis May 29 '13 at 13:42
  • @Mathletics It is actually a JavaScript *Object Literal* – Alex W May 29 '13 at 13:43
  • Are you splitting hairs between _object literal_ and _JavaScript Object Literal_? And besides, it's still __not__ JSON, so your link is misleading. – Evan Davis May 29 '13 at 13:44
  • 1
    @AlexW: Forget the "JS" in "JSON". It's merely a nod to the literal syntax that was partially used in forming the JSON syntax. JSON is a data transmission format. It doesn't make sense to refer to object literal syntax in a JavaScript program as JSON since you can't directly use the result of the syntax for data transmission. –  May 29 '13 at 13:51
  • 1
    I write them exactly as they are, object literals. @squint beat me to a perfect reply, but you are welcome to read [the spec](http://tools.ietf.org/html/rfc4627). – Evan Davis May 29 '13 at 13:52
  • Thanks for the clarification, semantics aside, a useful answer. Many thanks. – David Barker May 29 '13 at 13:54
  • Glad the whole JSON thing is resolved...Talk about splitting hairs, there's literally two differences: http://stackoverflow.com/questions/2904131/what-is-the-difference-between-json-and-object-literal-notation – Alex W May 29 '13 at 14:18
-1

if i understand it right they say you can do that in javascript (ECMA SCRIPT)

http://en.wikipedia.org/wiki/Literal_(computer_programming)

but what i know is that if you want to instance your Object there is an issue for old browsers..that you cannot instance objects with the Object.create() function

so you should generally do like this...

var foo = function(){};
foo.prototype = {func:function(){}}
var bar = new foo();

like you do so or so :)

Sven Delueg
  • 1,001
  • 11
  • 23