How to get the instance of the top/parent object in a javascript Function?

Asked

Viewed 509 times

9

I am implementing a method extension library of prototype, and if I do it simply everything works perfectly as you can see in the simple example below:

String.prototype.append = function(value) {
  // aqui o this é a instancia da String. Ok!
  return this.toString() + value;
};

document.getElementById('result').textContent = "Concatena com ".append("isto!");
<p id="result">
</p>

But to avoid overwriting methods of prototype, created an object within the prototype to register these methods, but with this the scope of the method is modified and the this is no longer a reference to the String, as can be seen in the following example:

String.prototype.extencion = {
  append: function(value) {
    // aqui o this não é mas uma instancia da String. Fail =(!
    // Como pegar a instancia do objecto pai?
    return this.toString() + value;
  }
};

document.getElementById('result').textContent = "Concatena com ".extencion.append("isto!");
<p id="result"></p>

Question

It is possible to recover the instance of the parent object in a function in the child object?

2 answers

10


Unfortunately what you want is not possible. The object you created is a member of the prototype of String, then the most you could (although I believe even that is not possible) would be to get a reference to this prototype - but not to the string that originated the access to it. For once you’ve climbed the chain of prototypes into the field:

"Concatena com ".extencion

Reference to original string has been lost forever...

In the case of methods the reference still exists in the form of this, but not on access to fields, so I usually use closures when I need to do something like this (although I don’t know if it’s a good idea even to do it or not). Example:

String.prototype.extencion = function() {
  return {
    append: (function(value) {
      return this.toString() + value;
    }).bind(this) // aqui o this é amarrado com o objeto original
  };
};

document.getElementById('result').textContent = "Concatena com ".extencion().append("isto!");
<p id="result"></p>

A disadvantage of this method is that it creates a new object with each function invocation... You could save it for each instance, but then there is a waste of memory that can become significant (depending on how it is used). That’s why I said I don’t know if it’s a good idea. A middle ground - not so convenient, but without the problems mentioned above - is to do it in the "jQuery style":

var extensoesString = {
  append: function(_, value) {
    return this.toString() + value;
  }
};

String.prototype.extencion = function(funcao) {
  return extensoesString[funcao].apply(this, arguments);
};

document.getElementById('result').textContent = "Concatena com ".extencion("append", "isto!");
<p id="result"></p>

  • bind will function for all major browser ? ie, Chrome, edge, opera, safari, Godzilla ? In some cases you would have to create at the beginning of the script not? see here

  • 2

    @Eprogrammernotfound Chrome, FF, Opera, Safari and IE9+ at least (there is a polyfill those who can’t stand). What is "edge" and "Godzilla"?

  • 2

    Edge is the new microsoft browser (Apparently you haven’t installed Windows 10 yet). Godzila is the mozzila firefox

  • @Fernando I just wanted to draw attention to the name of the function: The correct would be Extension if it is in English

  • 3

    That last issue killed my answer :P now I’ll have to invent another approach :) +1

  • @mgibsonbr, are good ways to do +1! But the first way with closures as you mentioned will create a new instance every call, which is not good, the second the call becomes unintuitive, even more for an extension library! I’ll review my problem!

  • 2

    @mgibsonbr, about the innerText for textContent, I changed in the question also, so it is consistent with the answer and it works in the FF, had not repaired this!

  • 2

    @Fernando hehe you can try to use creativity, like create a cache (especially now that the main ones browsers started to support the WeakMap) and only create an instance if it doesn’t exist yet, etc. Whether it compensates or not, I don’t know, I’ve gotten used to the "jQuery way" rsrs.

  • @Eprogrammernotfound, it was a typo, I will correct in my project! But in the question I will leave even so as not to cause inconsistency with the answer! Thank you!

  • @Fernando Recreating the object is irrelevant in most cases. Don’t worry about optimizations unless you have a bottleneck in that part. I would make the most simple and readable first. As far as I know, concatenating the string also creates a new urge, no ?

  • @Eprogrammernotfound In theory I agree with you (in particular if the compiler is smart, and the garbage collector is generational, the overhead of the creation of the object will be minimal), in practice, only testing... :)

  • 1

    I think in ES6 I could use one Proxy, and intercept the method search on the object at the time of the call.

  • @bfavaretto Indeed, but from what I understand this solution would suffer from the same problems as my first example (besides the obvious inconvenience of having to create the Proxy). Unless I’ve misunderstood, of course, you don’t want to post an answer with your suggestion?

  • 1

    I need to research to see how it does, if it works out I post later. I’m also not sure I would suffer from the same problems you mentioned.

  • @bfavaretto, interesting your suggestion, despite being "futuristic" =D, since so far only Firefox and Edge have implemented this. I couldn’t quite understand how this was going to be implemented with Proxy, but it seems something at least interesting. If you can implement a simple example of your idea it would be aggregator. (I was curious =D)

  • @mgibsonbr, I ended up implementing its solution number 1 (despite its problems, hehe), so I will accept this as an answer, at least until I have a better solution, maybe the Proxy that the bfavaretto quoted, but possibly will take to be compatible and usual, since there is not even a Polyfill for him. hehe

Show 11 more comments

4

You can create a Type parallel (superString in the example below) and put your methods there. To do this you copy the String prototype and pass your strings through the new Type you created. The idea is like this:

var superString = (function () {
    // constructor
    function MyString(str) {
        this._str = str;
    }

    // criar uma cópia para não escrever métodos na String nativa
    MyString.prototype = Object.create(String.prototype);

    // funcionalidade "append"
    MyString.prototype.append = function (value) {
        return this.toString() + value;
    };

    // é preciso sobreescrever estes métodos... 
    MyString.prototype.toString = function () {
        return this._str;
    };

    MyString.prototype.valueOf = function () {
        return this._str;
    };

    Object.defineProperty(MyString.prototype, 'comprimento', {
        get: function () {
            return this._str.length;
        }
    });
    return MyString;
})();


var str = new superString('Concatena com ');

document.getElementById('result').textContent = str.append('isto!');
console.log(str.comprimento); // 14

jsFiddle: http://jsfiddle.net/f2gzm6dx/

Some notes:

  • I had to rewrite the native methods .valueOf () and .toString () to work otherwise would give error.
  • to re-create a type property .length is theoretically possible, but Chrome enters an infinite loop, so I created a property comprimento with a getter to give what is expected.

Another solution using the same idea, regardless of the String prototype, would already allow the property .length and it would be simpler:

var superString = (function () {
    // constructor
    function MyString(str) {
        this._str = str;
    }

    // funcionalidade "append"
    MyString.prototype.append = function (value) {
        return this._str.toString() + value;
    };

    Object.defineProperty(MyString.prototype, 'length', {
        get: function () {
            return this._str.length;
        }
    });
    return MyString;
})();


var str = new superString('Concatena com ');

document.getElementById('result').textContent = str.append('isto!');
console.log(str.length); // 14

jsFiddle: http://jsfiddle.net/93xc49Lm/

  • 1

    Good and smart alternative @Sergio. Especially since it does not overwrite anything directly on String native. + 1

  • I liked the +1 approach, but frankly I wouldn’t use it.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.