heartyporridge
heartyporridge

Reputation: 1201

Is there a way to give all Objects a method without modifying Object.prototype?

I'd like to augment all Objects in my Node.js application with a logging function, such that each object can call this.log or have its log function referenced by thatObject.log. I see a number of advantages in doing this:

For example:

Object.prototype.log = function(message) {
    console.log(message);
};

function Person(name) {
    this.name = name;
};

Person.prototype.log = function(message) {
    console.log(this.name + " says " + message);
};

function Ghost() {
};

var joe = new Person("joe");
joe.log("Hi!"); //"Joe says Hi!"
var boo = new Ghost();
boo.log("Boo!"); //"Boo!"

//I can call this in any object context, and get a context-specific message
//This includes the global context, like so
this.log("Log!"); //"Log!"

Unfortunately, due to limitations in external dependencies, I can't modify Object.prototype (not simply a namespacing issue). Is there a way of doing this without modifying Object.prototype?

Most importantly, I would like to preserve the behaviour of calling this.log from anywhere.

UPDATE: I've found a workaround for this specific problem. See my answer below.

Upvotes: 2

Views: 104

Answers (3)

heartyporridge
heartyporridge

Reputation: 1201

While I've accepted Oriol's answer as the best answer for the original question, I have found a usable workaround for my specific problem, enabling log from any context:

  • Define a log function in the global scope, enabling this.log for when this refers to the global object, and
  • Create a Logger class with a log function defined on its prototype.
  • Make any classes with log functionality have Logger in the prototype chain somewhere.

This isn't ideal, because now classes have to have Logger in their prototype chain somewhere, either added manually or inherited, but it does ensure the log function will exist for that class. Implemented in the original example,

function log(message) {
    console.log(message);
}

function Logger() {};

Logger.prototype.log = function(message) {
    console.log(message);
};

function Person(name) {
    this.name = name;
};

Person.prototype = Object.create(Logger.prototype);

Person.prototype.log = function(message) {
    console.log(this.name + " says " + message);
};

function Ghost() {
};

Ghost.prototype = Object.create(Logger.prototype);

var joe = new Person("joe");
joe.log("Hi!"); //"Joe says Hi!"
var boo = new Ghost();
boo.log("Boo!"); //"Boo!"
this.log("Log!"); //"Log!"

Upvotes: 0

Jared Smith
Jared Smith

Reputation: 21965

As Oriol already pointed out, no its not possible. Here are a couple of alternatives:

var log = function(message) {
  var msg = message || '';
  console.log(JSON.stringify(this) + msg);
}

//later in the code
log.call(this, 'Log!'); //prints the stringified Object plus 'Log!'

another alternative if you use constructor functions would be to have all of them inherit from a constructor function with the desired method (although admittedly that doesn't satisfy your 'even global context' requirement).

Upvotes: 1

Oriol
Oriol

Reputation: 288470

If you want to add some property to an object, you can add it as an own property (if you have a reference to the object), or somewhere in its prototypical chain.

In this case, you want to add it to all objects. I think it's reasonable to assume that you don't have references to all of them. So the only way is adding the property in the prototypical chain.

For example, you could add the property to Array.prototype, Function.prototype, ...

However, that won't affect objects that don't inherit from those. Specially, an usual way to create objects is using object initializers: {}. Those objects only inherit from Object.prototype.

Therefore, for those, Object.prototype is your only option.

However, that isn't enough. It's possible to create objects that don't inherit from Object.prototype. For example Object.create(null).

So no, it's not possible. And IMO that's a good thing, because you shouldn't pollute objects you don't own.

Upvotes: 4

Related Questions