Search code examples
javascriptprototype

Javascript, get "this" from prototype function own field


Suppose I have following code:

var Model = function() {};
Model.prototype.a = function() {//do smth
Model.prototype.a.on = function() {//do smth

var m = new Model();
m.a();
m.a.on();

Now I need reference to specific object m from m.a() and m.a.on() calls. When calling m.a(), i have this referring to m.

Is it possible to get reference to m from m.a.on() call somehow?


Solution

  • It's a very bad idea to do so, as it leads to very strange behaviour in some cases, but it's possible:

    var Model = function(x) { this.x = x };
    
    Object.defineProperty(Model.prototype, 'a', (function() {
      var lastSelf;
      function get() { return lastSelf.x }
      get.on = function () { return lastSelf.x * 2 };
      return { get() { lastSelf=this; return get } };
    })());
    
    var m = new Model(17);
    console.log(m.a(), m.a.on());

    Why? I see your answer below, trying to realize what are bad cases.

    You can't pass a through the variable.
    You must grant access to on immediately after getting property a of the same object:

    var Model = function(x) { this.x = x };
    
    Object.defineProperty(Model.prototype, 'a', (function() {
      var lastSelf;
      function get() { return lastSelf.x }
      get.on = function () { return lastSelf.x * 2 };
      return { get() { lastSelf=this; return get } };
    })());
    
    var m1 = new Model(1), m2 = new Model(3);
    console.log(m1.a(), m2.a(), m1.a.on(), m2.a.on()); // 1 3 2 6 - ok
    var a1 = m1.a, a2 = m2.a;
    console.log(m1.a(), m2.a(), a1.on(), a2.on()); // 1 3 6 6 - ooops!
    console.log(m1.a(), m2.a(), m1.a(), a1.on(), a2.on()); // 1 3 1 2 2 - ooops!


    And the other solution, but with using __proto__. According to ES6 this solution is valid for browser enviroments and for server enviroments __proto__ have to be replaced by Object.setPrototypeOf. Be sure to check browser support and other warnings.

    This solution adds 1 function and 1 object per each instance.

    function Model(x) { 
      this.x = x;
    
      this.a = function () { return Model.prototype.a.call(this, arguments) };
      this.a.__proto__ = Object.create(Model.prototype.a);
      this.a.this = this;
    }
    
    Model.prototype.a = function () { return this.x };
    Model.prototype.a.on = function () { return this.this.x * 2 };
    
    var m1 = new Model(1), m2 = new Model(3);
    console.log([m1.a(), m2.a(), m1.a.on(), m2.a.on()] == "1,3,2,6");
    var a1 = m1.a, a2 = m2.a;
    console.log([m1.a(), m2.a(), a1.on(), a2.on()] == "1,3,2,6");
    console.log([m1.a(), m2.a(), m1.a(), a1.on(), a2.on()] == "1,3,1,2,6");