How do I call a prototype method referencing this from its constructor?

#1

I want to do some preparatory logic on each instance of a class, so I wrap preparatory logic in a method and invoke it from the constructor. Which should work fine, until that method tries to access the this of its constructor. The this surprisingly points to Window and no other measure convinces it to behave otherwise. Something of this nature:

function Foo () {

  var a = 'initializing object with Id:', b = 'done';

 console.log(this.method1(a, b));
}

var prto = Foo.prototype = {
 method1: function (arg1, arg2) {
  return arg1 + ' ' + this.getPrefx() + ' ' + arg2; // cannot remedy this returning Window
},

 getPrefx: function () {
  return Math.random() * 5;
}
};

I’ve tried:

1. binding every method to this constructor, like `for (var b of Object.getOwnPropertyDescriptors()) prto[b] = prto[b].bind(this); console.log (this.method1...`.

2. `var proxy = this.method1.bind (this); console.log (proxy (a,b))`. This didn't bind at all so I bound twice to get a scoped function. Calling that ignores its new scope anyway

3. `console.log(this.method1.call(this, a,b))`

Although I’m yet to try it, I know jQuery has a method dedicated to this purpose (named proxy). Unfortunately, it’s not included in version 1.9. Is there a way to solve this in vanilla?

#2

Hi @nmeri17, that code works fine for me – when I instantiate a new Foo() it logs something like

initializing object with Id: 0.7783665419170649 done

so this this binding is fine.

1 Like
#3

You are going to have to be more specific as to what exactly you are doing. Of course “this” works fine there, you just have to use the instance variable you created “prto”.

For instance if put this after your prototype code…

console.log(prto.method1('Testing','this'));

You will see in your console that it calls getPrefx and returns the random number. It uses “this” on the current class instance.

Now if you are wanting to be able to call method1 from Foo() you can use this as an example…

console.log(this.method1.apply(this, [a,b]));

Here we are calling method1 and using apply, we apply this and the arguments “a”, and “b”. If this is what you are looking to do, then you can check out the following link for more information about apply and its brother call(). But as pointed out, this is working fine out of the box as is if you just wanted to call the other method. You needed to create an instance of the class.

https://www.w3schools.com/js/js_function_apply.asp

Hope this is what you were looking to do. :slight_smile:

#4

I find that it’s more reliable doing without the this keyword.

I don’t like classes as they tend to break the open/closed principle, and removes the prototypal benefits of JavaScript. Because of that, I find other techniques work just as well instead.

Using a module syntax works more reliably for me.

function makeFoo() {
  const a = 'initializing object with Id:';
  const b = 'done';

  function getPrefx() {
    return Math.random() * 5;
  }
  function method1() {
    return a + ' ' + getPrefx() + ' ' + b;
  }

  return { // publicly known methods
    method1
  };
}

const foo = makeFoo();
console.log(foo.method1());
#5

It is so embarrassing to think of how many hours I spent on this, all for a rookie oversight: arrow functions!! My method1 was defined using one, so no matter the binding, it always returned the Window object (not even the object literal it was defined in). I changed the definition to a normal function and everything worked. Insane

Isn’t this something the interpreter should complain about at runtime? Attempting to bind to definition scope doesn’t seem to offer any traction when that scope is an object literal. Who could ever want that?

1 Like
#6

D’oh! :-D

Binding to arrow functions still has a use case when you want to bind arguments (not this), such as

const add = (a, b) => a + b
const addOne = add.bind(null, 1)

IMHO it is indeed unfortunate though that binding context and arguments has to be done using the same method… I’d rather use an own helper function for this.

#7

By way of experimentation, I renamed the this keyword to Foo.prototype, and things worked well.

function Foo() {
    const a = 'initializing object with Id:';
    const b = 'done';
    console.log(Foo.prototype.method1(a, b));
}
Foo.prototype = {
    method1: function (arg1, arg2) {
        console.log(Foo.prototype);
        return arg1 + ' ' + Foo.prototype.getPrefx() + ' ' + arg2;
    },
    getPrefx: function () {
        return Math.random() * 5;
    }
};

Using Foo.prototype all of the time isn’t a good solution, but how does it handle arrow-notation?

function Foo() {
    const a = 'initializing object with Id:';
    const b = 'done';
    console.log(Foo.prototype.method1(a, b));
}
Foo.prototype = {
    // method1: function (arg1, arg2) {
    method1: (arg1, arg2) => {
        return arg1 + ' ' + Foo.prototype.getPrefx() + ' ' + arg2;
    },
    // getPrefx: function () {
    getPrefx: () => {
        return Math.random() * 5;
    }
};
var f = new Foo();

Foo.prototype works well even when using arrow-notation.

Why do the functions need to be on the prototype? Can they be in the Foo function instead?

function Foo() {
    const method1 = (arg1, arg2) => {
        return arg1 + ' ' + getPrefx() + ' ' + arg2;
    };
    const getPrefx = () => {
        return Math.random() * 5;
    };

    const a = 'initializing object with Id:';
    const b = 'done';
    console.log(method1(a, b));
}
var f = new Foo();

yes they can, and they don’t need any reference at all to be called.
And those arrow-notation functions are one-liners, so can be expressed more appropriately:

function Foo() {
    const method1 (arg1, arg2) => arg1 + ' ' + getPrefx() + ' ' + arg2;
    const getPrefx = () => Math.random() * 5;

    const a = 'initializing object with Id:';
    const b = 'done';
    console.log(method1(a, b));
}
var f = new Foo();

Not only is the this-keyword removed, but any need for Foo.prototype is easily removed too.

The question needs to be asked. Why are you using the prototype when the code is less confusing and works just as well without it? Your code doesn’t seem to benefit from using it.

#8

@Paul_Wilkins Thanks for stopping by. It’s always nice to have you around. Your example assumes the module had foreknowledge of a and b, which my situation happens not to.
I could achieve the same result to did if I used an IIFE (that you one-time called a condom :joy: ). What else do I stand to gain beside preventing variable conflict? Presentational legibility, maybe. Because every time someone says something about encapsulation, keeping objects safe etc, I can’t help but wonder who and what they could gain by accessing your classes some other way.

1. Browser js limits modifications to the users machine

2. Compiled languages don't have their source served to an end user, only those working on same project. By extension, they should be able to modify the classes

But isn’t the point of prototyping that it allows an inheritance chain through a this? How do you get that without constructors? In the example you posted, this cannot come into play. Your concept neither allows for instantiation nor supplication of arguments.

It sort of seems like a topic too broad to be covered outside the confines of an expository article

#9
  1. Methods too big to fit in a constructor
  2. Constructors are for instantiating instance properties. Actions go to methods that should be exposed to callers. Dumping every method in a constructor may work well for objects intended for only one purpose

Did you try replacing Foo.prototype with this? Using a reference to the plain prototype works but it’s unreasonable. If I have a prototype object, its methods should be linked in such a way they can be called on an instance basis, access instance properties i.e. single source of truth

#10

That seems to be quite easily achieved by passing what it needs to know or access as function parameters. An init functions serves as a handy constructor:

const makeModule = (function iife() {
    const moduleParams = {};

     function processParam(param) {
        return param || 0;
    }
    function doStuff(foo) {
        const view = moduleParams.view;
        view.render(foo, ...moduleParams);
    }
    function init({bar, baz}, db, view) {
        moduleParams.bar = processParam(bar);
        moduleParams.baz = db.get(baz);
        moduleParams.view = view;
    }
    return {
        doStuff,
        init
    };
}());

const myModule = makeModule({
    bar: "def",
    baz: "ghi"
}, someDB, anotherView);

myModule.doStuff("foo");

Mind you, my philosophical odds with classes under fire because classes help to simplify some things.

2 Likes
#11

I just happened to respond to someone else on this topic elsewhere. Constructors do not break the open/closed principle.

#12

I’ve been basing that on what Eric Elliot says: “Constructors violate the open/closed principle because they couple all callers to the details of how your object gets instantiated.”

#13

By the way, do you mind elucidating on how one could go about modifying a constructor, and how the same method will fail if I were using this your modular architecture

#14

@Paul_Wilkins In the response I linked to, the first line is:

Obligitory beware^1 referencing^2 or learning^3 from Eric Elliott.

He’s a better salesman than programmer. He’s good at projecting confidence, but most of what he says is flat wrong.

The rest of my response goes on to describe what the open/closed principle actually is. In that description, I cite – and often quote verbatim – descriptions and code examples from Robert Martin, the guy who coined SOLID (where the “o” is for the open/closed principle).

1 Like
#15

The example using arrow-notation is a good example, where the this keyword doesn’t work in that context.

The main reason though why I stay away from the this keyword, is that it’s meaning can change rapidly throughout the code.

#16

Yes, I really like what Robert Martin has to say too.

And interestingly, both Martin and Elliot are drawing directly from the same source, the “Gang of Four” Design Patterns book which is built around two foundational principles of: "Program to an interface, not an implementation,” and “favor object composition over class inheritance.”

#17

And interestingly, both Martin and Elliot are drawing directly from the same source, the “Gang of Four” Design Patterns book

The difference is Elliott habitually mis-represents his sources. His M-O is to latch onto popular buzzwords and then invent his own personal definition for them.

As another user put it in the “beware” link:

What [other person] and Eric are doing is like a scam artist talking about “quantum effects” and “toxin cleansing”. Scammers ride on the popularity of some accepted terms, but reject their meaning and make up some B.S.