Como obter a instância do objeto superior/pai em uma function javascript? – javascript
Pergunta:
Estou implementando uma biblioteca de extensão de métodos do prototype, e se faço da forma simples tudo funciona perfeitamente como podem verificar no simples exemplo a seguir:
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>
Mas para evitar sobrescrever métodos do prototype, criei um objeto dentro do prototype para registrar esses métodos, mas com isso o escopo do método se modifica e o this não é mais uma referencia para a String, como pode ser verificado no exemplo a seguir:
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>
Pergunta
É possível recuperar a instância do objeto pai em uma function no objeto filho?
Autor da pergunta Fernando Leal
mgibsonbr
Infelizmente isso que você quer não é possível. O objeto que você criou é um membro do protótipo de String, então o máximo que você poderia (embora eu acredite que nem isso seja possível) seria obter uma referência a esse protótipo – mas não à string que originou o acesso a ele. Pois uma vez que se subiu a cadeia de protótipos no acesso ao campo:
"Concatena com ".extencion
Já se perdeu a referência pra string original para sempre…
No caso dos métodos a referência ainda existe na forma do this, mas não no acesso a campos, de modo que eu costumo usar closures quando preciso fazer algo desse tipo (embora não saiba se é uma boa ideia mesmo fazer isso ou não). Exemplo:
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>
Uma desvantagem desse método é que ele cria um objeto novo a cada invocação de função… Você poderia salvá-la para cada instância, mas aí há um desperdício de memória que pode se tornar significativo (dependendo do modo como se usa). Por isso disse não saber se é uma boa ideia. Um meio termo – não tão conveniente, mas sem os problemas citados acima – é fazer no “estilo jQuery”:
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>
Podes criar um Type paralelo (superString no exemplo em baixo) e colocar os teus métodos alí. Para isso copias o prototype de String e passas as tuas strings pelo novo Type que crias-te. A ideia é assim:
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/
Algumas notas:
- Tive de re-escrever os métodos nativos
.valueOf ()e.toString ()para funcionar senão dava erro. - para re-criar uma propriedade tipo
.lengthé teoréticamente possivel, mas o Chrome entra num loop infinito, assim criei uma propriedadecomprimentocom um getter para dar o que é esperado.
Outra solução usando a mesma ideia, sem importar o prototype de String já permitiria a propriedade .length e seria mais simples:
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



