Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaScript prototype limited to functions?

o.prototype = {...} is working only if o is a Function. Suppose I've the following Code

 conf = {
  a: 2,
  b: 4
 };
 conf.prototype = {
  d: 16
 }

conf.a and conf.b is OK and returns proper values. But conf.d doesn't return 16 rather it goes undefined. Is there any solution suck that prototype based generalization can also be applied on these type of Objects.

like image 493
Neel Basu Avatar asked Jan 21 '10 17:01

Neel Basu


2 Answers

You are confusing the prototype property that can be used on Constructor Functions and the internal [[Prototype]] property.

All objects have this internal [[Prototype]] property, and only the new operator when you call it with a constructor function is allowed to set it (through the [[Construct]] internal operation).

If you want to have prototypal inheritance with object instances (without using constructors), the Crockford's Object.create technique is what you want (that method is now part of the recently approved ECMAScript 5th Edition):

// Check if native implementation available
if (typeof Object.create !== 'function') {
  Object.create = function (o) {
    function F() {}  // empty constructor
    F.prototype = o; // set base object as prototype
    return new F();  // return empty object with right [[Prototype]]
  };
}

var confProto = {
  d: 16
};
var conf = Object.create(confProto);
conf.a = 2;
conf.b = 4;

In the above code conf will have its three members, but only a and b will exists physically on it:

conf.hasOwnProperty('a'); // true 
conf.hasOwnProperty('b'); // true
conf.hasOwnProperty('d'); // false

Because d exists on the conf [[Prototype]] (confProto).

The property accessors, . and [] are responsible to resolve the properties looking up if necessary in the prototype chain (through the [[Get]] internal method).

like image 72
Christian C. Salvadó Avatar answered Oct 22 '22 09:10

Christian C. Salvadó


There are actually two different kind of "prototype" in JavaScript:

  1. One is the "hidden" link every object has (let's use [[Prototype]] to represent this hidden link). object literals by default have their hidden links pointing to Object.prototype, function objects have their hidden link pointing to Function.prototype, and arrays have theirs pointing to Array.prototype. These hidden prototype links are not related to properties with name "prototype". You can't change these hidden links by adding or modifying o.prototype.
  2. Another one is that all function objects automatically have a special property named "prototype". This is mainly for the usage of constructor invocation pattern.

[[Prototype]] is used for searching properties (like the parent in classical hierarchy), whenever a property cannot be found in an object, its [[Prototype]] is searched instead. One usage scenario: say you'd like to add a property to all objects, you could simply add it to Object.prototype which would automatically apply to all objects since all objects somehow have Object.prototype as their [[Prototype]] chain root.

Lets get back to function objects' "prototype" property. It is only useful when used with operator new. Take the following code snippet as an example:

function F() {} // function declaration
// F now has a property named "prototype"

var f = new F(); // use "new" operator to create a new function object based on F

What new F() does above is to first create a new function object, set the [[Prototype]] (hidden link) of this newly created function object to be F.prototype, and then return the new function object. This is probably what you already understand that works for function objects.

Remember that I said we can't change objects's [[Prototype]]? Well, at least not directly. Crockford's Object.create function does just that, by utilizing the fact that operator new could help set [[Prototype]] for us. So by using Object.create, you get to deliberately indicate where your new object's hidden link should point to. (somewhat feel like indicating who is your parent class)

In your example, conf is an object literal and conf.prototype isn't really useful. Here's another version utilizing classical style:

function ConfBase() {}
ConfBase.prototype.d = 16;
var conf = new ConfBase();
conf.a = 2;
conf.b = 4;

document.writeln(conf.a);
document.writeln(conf.b);
document.writeln(conf.d);

Compared to the answer of @CMS, I do prefer using Object.create. But essentially the 2 styles are using the same underlying mechanism, just that Object.create helps tidy it up.

like image 25
bryantsai Avatar answered Oct 22 '22 08:10

bryantsai