Question

Is it generally acceptable and reasonable to define class (static) and prototype methods with the same name in JS? In the following node.js example the method update is defined in such a way and the example works like a charm, but are there any common cases when this could cause issues, for example, such as calling update on an instance would dispatch to the class method?

function Base(id, value) {
    this.id = id;
    this.value = value;
    Base.objects[id] = this;
}

Base.objects = {};

Base.find = function(id, done) {
    var err = null;
    var obj = this.objects[id];
    if (!obj) {
        err = new Error("no obj found for id " + id);
    }
    done(err, obj);
};

Base.update = function(id, value, done) {
    this.find(id, function(err, obj) {
        if (err) {
            done(err);
        } else {
            obj.update(value, done);
        }
    });
};

Base.prototype.update = function(value, done) {
    this.value = value;
    done();
};

Trying it out:

new Base('aa', 1);
new Base('bb', 2);

Base.update('aa', 3, function(err) {
    if (err) {
        throw err;
    }
});

Base.update('bb', 5, function(err) {
    if (err) {
        throw err;
    }
});

console.log(Base.objects);

Output:

{ aa: { id: 'aa', value: 3 }, bb: { id: 'bb', value: 5 } }
Was it helpful?

Solution

Not entirely sure what you're asking here, but I take it you're wondering if you were to call:

(new Base('aa', 1)).update(3, function (err){ if (err) throw err;});

That this could invoke the Base.update function.

The answer to that question is, of course: No.

The Base.update function has nothing to do with Base.prototype.update. A function, like almost everything in JS, is an object. Just like the Base instances you create with new Base. The Base function (that just happens to double as a constructor) has its own inheritance chain:

Base //the object itself
  || Object.getPrototypeOf(Base)
  ====> Function (or function Empty(){} in chromium)
         || Object.getPrototypeOf(Object.getPrototypeOf(Base));
         ====> Object {}

When you create a new Base object, that chain looks like this:

x // Base instance itself
 ||
  --> Base.prototype //or Base {} <-- an object!
       ||
        --> Object{}

That means that any property of any Base instance has to be located along that inheritance chain: either defined on the object itself, on the Base.prototype object, or on the mother of all object: Object.

At no point will the function object Base be considered.

So, what does this all mean, exactly:

var x = new Base('x', 1);
console.log(x.update === Base.prototype.update);//true
console.log(x.update === Base.update);//false, of course

But if you were to write:

x.update = Base.update;

Then you've overridden the prototype, and calling update on x (this one instance of Base), then Base.update will be invoked.
However, it'll not work as expected, because its call context has changed. this references x now and this statement:

 this.find(...)

will no longer work, unless you also add x.find = Base.find. However, now we have a similar problem in the x.find method:

var obj = this.objects[id];

x does not have an objects property... to fix that, you'll have to add x.objects = Base.objects to make it all work. We end up with:

x = new Base('x', 1);
x.update = Base.update;
x.find = Base.find;
x.objects = Base.objects;

But now we've assigned all properties of the Base function object to x, a Base instance. That's just silly, so let's delete them:

delete(x.update);
delete(x.find);
delete(x.objects);

Now, x.update will, once again work as expected: the instance itself doesn't mask the prototype methods, so that's what will be invoked.

This is just a quick canter through how JS evaluates expressions and how prototypes (and scopes) are scanned. I've explained it all before here, at length. This linked answer also contains more links, each to answers that are in some way related to the same thing (scopes and prototype chains).

OTHER TIPS

JS is prototype-based language. In this language calling on method from some object is exactly calling that function that is stored in object itself with that name or in it's __proto__ of some level.

The mismatch you've interested in is only possible when SomeObj.prototype==SomeObj. If you wouldn't do such a thing this won't appear at any time.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top