0

I am reading about prototipcal inheritance. There, and from elsewhere, I am learning this style of avoiding classical inheritance which I am still digesting.

One aspect that still puzzles me, though, is the this pointer, which is said to cause confusion for many like myself who come from classic OO languages. (If I were to avoid classic inheritance, shouldn't I be avoiding this as well? )

After some reading, I realize that the this pointer is defined not at the time of object/function definition (as in C++) but rather at the site of function call. The this pointer seems (to me) like a dangling pointer whose target depends on where/how you use it.

Using an example in the linked blog article about extending objects, can someone help explain the following:

Can we avoid using this or replacing it with explicit object reference?

A related question is, is the this pointer necessary if I only use prototypical inheritance?

Example from the blog:

It would be nice to combine these two operations into one, ... and extend it with new properties. This operation, called extend, can be implemented as a function:

 1 Object.prototype.extend = function (extension) {
 2     var hasOwnProperty = Object.hasOwnProperty;
 3     var object = Object.create(this);
 4 
 5     for (var property in extension)
 6         if (hasOwnProperty.call(extension, property) ||
 7             typeof object[property] === "undefined")
 8                 object[property] = extension[property];
 9 
10     return object;
11 };

Using the above extend function we can rewrite the code for square as follows:

1 var square = rectangle.extend({
2     create: function (side) {
3         return rectangle.create.call(this, side, side);
4     }
5 });
6 
7 var sq = square.create(5);
8 
9 alert(sq.area());

Note, this is used in line 3 of both code segments.

tinlyx
  • 18,900
  • 26
  • 81
  • 148

1 Answers1

0

If you want to avoid this completely, then you should step away from creating methods that act on the object they are applied on, meaning that you should not have method calls like obj.method(), where method needs to use the state of obj in some way.

So the effect of the following should be the same as obj.method():

var method = obj.method;
method();

In places where the above would fail, you'll need to refactor the code, so that you can in principle use it like this without problems:

var method = obj1.method;
method(obj2); // apply method on obj2

So, in general you'll need to create utility functions that take one more argument: the object to apply the logic on.

In your case this would mean that you don't define extend on Object.prototype (which is considered bad practice anyway), but on Object itself, and give it the extra source object parameter. This is also how many native methods are defined, like Object.assign, Object.keys, et al. Also the definition of rectangle will need some changes to make it work without ever using this:

Object.extend = function (source, extension) {
    var hasOwnProperty = Object.hasOwnProperty;
    var object = Object.create(source);
 
    for (var property in extension)
        if (hasOwnProperty.call(extension, property) ||
            typeof object[property] === "undefined")
                object[property] = extension[property];

    return object;
};

var rectangle = {
    create: function (width, height) {
        var self = {
            width: width,
            height: height,
            area: function () {
                return self.width * self.height;
            }
        };
        return self;
    }
};

var square = Object.extend(rectangle, {
    create: function (side) {
        return rectangle.create(side, side);
    }
});

var sq = square.create(5);

console.log(sq.area());

As you have realised, there are lots of ways to work with objects and implement some form of inheritance in JavaScript, and each has its pros and cons.

trincot
  • 211,288
  • 25
  • 175
  • 211