Procurando uma maneira melhor de anexar métodos de reflexão com objetos em Javascript

Eu estou procurando uma maneira rápida e limpa para anexar um par de métodos de reflexão em objetos localizados dentro de uma matriz, mas eu não quero macular os objetos. Os objetos em si podem ser de tamanhos variados, pois são retornados de um nó de extremidade JSON dinâmico, portanto, não indo pela rota de ObjWrapper (obj) {this.foo = obj.foo; } .

Eu criei o seguinte código, mas eu sinto que haveria uma maneira mais elegante de alcançar o mesmo resultado. Eu ficaria feliz em explorar o uso das funções do Lodash :

var objs = [{foo:'bar'}, {foo:'baz'}];

function ObjWrapper(obj){
  var self = this;
 //Inherit all the shallow object properties
  Object.keys(obj).forEach(function(key){
    self[key] = obj[key];
  });
}

ObjWrapper.prototype.isBar = function() {
  return this.foo === 'bar';
}

ObjWrapper.prototype.isBaz = function() {
  return this.foo === 'baz';
}

objsWrapped = objs.map(function(obj){
  return new ObjWrapper(obj);
});

objsWrapped.forEach(function(objWrapped){
  console.log(objWrapped.isBar());
  console.log(objWrapped.isBaz()); 
});

I'm not seeing a performance difference in this approach over using a static function to evaluate the object logic: http://jsperf.com/object-wrapping

Para maior clareza, a questão é; isso pode ser escrito melhor?

0
estranho .. eu corro jspref e os resultados afirmam que as funções auxiliares são melhores do que a instância obj. No entanto, eu recomendo este link que cobre o assunto em profundidade: blog.soulserv.net/…
adicionado o autor ymz, fonte
Nunca use console.log em seus testes de benchmark. E sempre tente restringir os testes o máximo possível, por exemplo, forEach nesse caso também poderia ser substituído por um simples loop. E sim, as funções auxiliares têm muito mais desempenho jsperf.com/object-wrapping/2
adicionado o autor tenbits, fonte
Nunca use console.log em seus testes de benchmark. E sempre tente restringir os testes o máximo possível, por exemplo, forEach nesse caso também poderia ser substituído por um simples loop. E sim, as funções auxiliares têm muito mais desempenho jsperf.com/object-wrapping/2
adicionado o autor tenbits, fonte

6 Respostas

Primeiro de tudo para sua abordagem, embora não seja uma solução ruim, mas em um construtor ObjWrapper você modifica a classe oculta ObjWrapper , e você se tornará a penalidade de desempenho . Saiba mais sobre as classes ocultas .

Algumas opções você tem.

  1. As you have already mentioned, monky-patch original objects.
  2. Create a proxy object

    function ObjProxy(obj) {
        this.data = obj;
    }
    ObjProxy.prototype = {
        isFoo() {
            return this.data.foo === 'foo';
        }
    }
    
  3. Merge two objects. Similar to your solution, but now we work with raw objects

    var Methods = {
        isFoo() {
            return this.foo === 'foo'
        }
    };
    var obj = {foo: 'foo'};
    var objWrapper = Object.assign({}, obj, Methods);
    
  4. Functions: Utility methods are also a good solution, as you can use them as filters.

    function isFoo(obj) {
        return obj.foo === 'foo';
    }
    // Or as an utility object
    var Methods = {
        isBar (obj) {
            return obj.foo === 'bar';
        }
    }
    
    objs.filter(isFoo).filter(Methods.isBar);
    

Quarta opção tem o melhor desempenho, e acho que será a melhor opção aqui.

2
adicionado
Note que Object.assign é novo , e você deve incluir alguns polyfills. E o que você quer dizer com isso destrói cadeia de protótipos , de qual objeto?
adicionado o autor tenbits, fonte
obrigado por estas sugestões, eu já sabia sobre essas alternativas, mas todos eles têm algumas desvantagens. O método 2. significa que você precisa trabalhar com o contêiner de proxy ou com um getter para o objeto original. Vamos imaginar que eu tinha o carro: {color: "red"}, para ter acesso depois que ele é empacotado: car.data.color, um pouco feio. Método 3. Isto está perto do que eu estava procurando e não estava ciente de Object.assign :) Problema é que ele destrói cadeia proto anterior. Método 4. Atualmente estou inclinado para isso. Eu incluí isso no meu JS perf como é a "melhor prática".
adicionado o autor James Broad, fonte

Primeiro de tudo para sua abordagem, embora não seja uma solução ruim, mas em um construtor ObjWrapper você modifica a classe oculta ObjWrapper , e você se tornará a penalidade de desempenho . Saiba mais sobre as classes ocultas .

Algumas opções você tem.

  1. As you have already mentioned, monky-patch original objects.
  2. Create a proxy object

    function ObjProxy(obj) {
        this.data = obj;
    }
    ObjProxy.prototype = {
        isFoo() {
            return this.data.foo === 'foo';
        }
    }
    
  3. Merge two objects. Similar to your solution, but now we work with raw objects

    var Methods = {
        isFoo() {
            return this.foo === 'foo'
        }
    };
    var obj = {foo: 'foo'};
    var objWrapper = Object.assign({}, obj, Methods);
    
  4. Functions: Utility methods are also a good solution, as you can use them as filters.

    function isFoo(obj) {
        return obj.foo === 'foo';
    }
    // Or as an utility object
    var Methods = {
        isBar (obj) {
            return obj.foo === 'bar';
        }
    }
    
    objs.filter(isFoo).filter(Methods.isBar);
    

Quarta opção tem o melhor desempenho, e acho que será a melhor opção aqui.

2
adicionado
Note que Object.assign é novo , e você deve incluir alguns polyfills. E o que você quer dizer com isso destrói cadeia de protótipos , de qual objeto?
adicionado o autor tenbits, fonte
obrigado por estas sugestões, eu já sabia sobre essas alternativas, mas todos eles têm algumas desvantagens. O método 2. significa que você precisa trabalhar com o contêiner de proxy ou com um getter para o objeto original. Vamos imaginar que eu tinha o carro: {color: "red"}, para ter acesso depois que ele é empacotado: car.data.color, um pouco feio. Método 3. Isto está perto do que eu estava procurando e não estava ciente de Object.assign :) Problema é que ele destrói cadeia proto anterior. Método 4. Atualmente estou inclinado para isso. Eu incluí isso no meu JS perf como é a "melhor prática".
adicionado o autor James Broad, fonte

Primeiro de tudo para sua abordagem, embora não seja uma solução ruim, mas em um construtor ObjWrapper você modifica a classe oculta ObjWrapper , e você se tornará a penalidade de desempenho . Saiba mais sobre as classes ocultas .

Algumas opções você tem.

  1. As you have already mentioned, monky-patch original objects.
  2. Create a proxy object

    function ObjProxy(obj) {
        this.data = obj;
    }
    ObjProxy.prototype = {
        isFoo() {
            return this.data.foo === 'foo';
        }
    }
    
  3. Merge two objects. Similar to your solution, but now we work with raw objects

    var Methods = {
        isFoo() {
            return this.foo === 'foo'
        }
    };
    var obj = {foo: 'foo'};
    var objWrapper = Object.assign({}, obj, Methods);
    
  4. Functions: Utility methods are also a good solution, as you can use them as filters.

    function isFoo(obj) {
        return obj.foo === 'foo';
    }
    // Or as an utility object
    var Methods = {
        isBar (obj) {
            return obj.foo === 'bar';
        }
    }
    
    objs.filter(isFoo).filter(Methods.isBar);
    

Quarta opção tem o melhor desempenho, e acho que será a melhor opção aqui.

2
adicionado
Note que Object.assign é novo , e você deve incluir alguns polyfills. E o que você quer dizer com isso destrói cadeia de protótipos , de qual objeto?
adicionado o autor tenbits, fonte
obrigado por estas sugestões, eu já sabia sobre essas alternativas, mas todos eles têm algumas desvantagens. O método 2. significa que você precisa trabalhar com o contêiner de proxy ou com um getter para o objeto original. Vamos imaginar que eu tinha o carro: {color: "red"}, para ter acesso depois que ele é empacotado: car.data.color, um pouco feio. Método 3. Isto está perto do que eu estava procurando e não estava ciente de Object.assign :) Problema é que ele destrói cadeia proto anterior. Método 4. Atualmente estou inclinado para isso. Eu incluí isso no meu JS perf como é a "melhor prática".
adicionado o autor James Broad, fonte

talvez você precise substituir esta implementação:

ObjWrapper.prototype.isBar = function() 
{
     return this.foo === 'bar';
}

com este:

ObjWrapper.prototype.isType = function(type) 
{
     return this.foo === type;
}

então é só ligar:

console.log(objWrapped.isType('bar'));

o próprio parâmetro ('bar') pode ser substituído por qualquer variável

0
adicionado
você pode descrever o uso final que você pretende obter na pergunta original?
adicionado o autor ymz, fonte
obrigado por suas sugestões, acrescentei um pouco mais de detalhes. Eu tenho a sensação de que estou fazendo algo errado ou perdendo um atalho do JS.
adicionado o autor James Broad, fonte
estes eram apenas exemplos de múltiplos métodos sendo anexados, então essa otimização estaria perdendo a questão central.
adicionado o autor James Broad, fonte

talvez você precise substituir esta implementação:

ObjWrapper.prototype.isBar = function() 
{
     return this.foo === 'bar';
}

com este:

ObjWrapper.prototype.isType = function(type) 
{
     return this.foo === type;
}

então é só ligar:

console.log(objWrapped.isType('bar'));

o próprio parâmetro ('bar') pode ser substituído por qualquer variável

0
adicionado
você pode descrever o uso final que você pretende obter na pergunta original?
adicionado o autor ymz, fonte
obrigado por suas sugestões, acrescentei um pouco mais de detalhes. Eu tenho a sensação de que estou fazendo algo errado ou perdendo um atalho do JS.
adicionado o autor James Broad, fonte
estes eram apenas exemplos de múltiplos métodos sendo anexados, então essa otimização estaria perdendo a questão central.
adicionado o autor James Broad, fonte

talvez você precise substituir esta implementação:

ObjWrapper.prototype.isBar = function() 
{
     return this.foo === 'bar';
}

com este:

ObjWrapper.prototype.isType = function(type) 
{
     return this.foo === type;
}

então é só ligar:

console.log(objWrapped.isType('bar'));

o próprio parâmetro ('bar') pode ser substituído por qualquer variável

0
adicionado
você pode descrever o uso final que você pretende obter na pergunta original?
adicionado o autor ymz, fonte
estes eram apenas exemplos de múltiplos métodos sendo anexados, então essa otimização estaria perdendo a questão central.
adicionado o autor James Broad, fonte
obrigado por suas sugestões, acrescentei um pouco mais de detalhes. Eu tenho a sensação de que estou fazendo algo errado ou perdendo um atalho do JS.
adicionado o autor James Broad, fonte
JavaScript Brasil OFICIAL
JavaScript Brasil OFICIAL
4 138 dos participantes

Grupo sobre JavaScript do Brasil