#JS - Manipulando objetos com Proxy
Tags: javascript, object, proxy, ecmascript
Published: 2021-06-14 14:46:27

## Apresentação
Olá devs, antes de adentrarmos no conteúdo do artigo, gostaria de me apresentar: para quem não me conhece, me chamo Erandir Junior, sou desenvolvedor web, atuo tanto no front como back, apesar de ser mais backend. Essa plataforma será nosso ponto de enconto, realmente espero que gostem.

## Objetos
Sabemos que é muito simples definir um objeto em JavaScript, podemos criar um objeto utilizando a forma literal, função construtora ou mesmo por meio da Object API. Outro ponto, é que é muito fácil manipular propriedades, ou seja, acessar, modificar e adicionar.

E para deixar isso de forma clara, vamos criar um pequeno exemplo, onde definiremos um objeto com algumas propriedades, e depois, vamos manipular esse objeto:

```javascript
const user = {
  name: 'Erandir Junior',
  email: 'erandir@email.com',
  password: '123456',
  uuid: '1234-1234-1234-1234'
};

// Exibindo valor da propriedade name
console.log(user.name); // Erandir Junior

// Atribuindo um novo valor para a propriedade uuid
user.uuid = '2222-2222-2222-2222';

// Adicionando uma nova propriedade
user.createdAt = '2021-01-01';
```

## Problema
Como temos algo bastante dinâmico, podemos modificar propriedades que não deveriam ser modificadas, como por exemplo a propriedade ***uuid***. Nós não queremos modificar esse valor, o que poderíamos ter feito para que isso não ocorresse? Bem, poderíamos aplicar algumas convenções:

```javascript
const user = {
  _name: 'Erandir Junior',
  _email: 'erandir@email.com',
  _password: '123456',
  _uuid: '1234-1234-1234-1234',
  get name() {
    return this._name;
  },
  set name(name) {
    this._name = name;
  },
  get email() {
    return this._email;
  },
  set email(email) {
    this._email = email;
  },
  get password() {
    return this._password;
  },
  set password(password) {
    this._password = password;
  },
  get uuid() {
    return this._uuid;
  }
};

// Exibindo valor da propriedade name
console.log(user.name); // Erandir Junior

// Atribuindo um novo valor para a propriedade name
user.name = 'Erandir';
```

Adicionamos os dos métodos ***get*** e ***set***, para acessar e modificar os valores do nosso objeto, além disso, adicionamos um _ antes dos nomes das propriedades, para informar que aquelas propriedades são "privadas". Falando um pouco mais do método ***set***, podemos injetar alguma lógica. Um exemplo que posso pensar, seria o de verificar se um email é válido ou se um nome passado tem pelo menos uma quantidade aceitável de caracteres:

```javascript
const user = {
  _name: 'Erandir Junior',
  _email: 'erandir@email.com',
  _password: '123456',
  _uuid: '1234-1234-1234-1234',
  get name() {
    return this._name;
  },
  set name(name) {
  	if(name.length < 3) {
    	throw Error('Minimum 3 characters!');
    }
  
    this._name = name;
  },
  get email() {
   return this._email;
  },
  set email(email) {
    this._email = email;
  },
  get password() {
    return this._password;
  },
  set password(password) {
    this._password = password;
  },
  get uuid() {
    return this._uuid;
  }
};
```

## Problema resolvido?
Não. Poderíamos criar um método ***set*** para a propriedade ***_uuid***, onde uma exceção fosse levantada caso alguém tentasse modificar o valor, mas isso não resolveria nosso problema. Nós simplesmente aplicamos uma convenção, ainda podemos acessar diretamente uma propriedade ou mesmo adicionar novas propriedades, como no exemplo abaixo:

```javascript
// Modificando diretamente o valor da propriedade
user._uuid = 1;

// Adicionando uma nova propriedade
user.createdAt = '2021-01-01';
```

Então, qual a solução?

### Proxy
Sim, temos uma solução. Desde o lançamento do ES6, contamos com o objeto [**Proxy**](https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Proxy). Com ele, podemos sobrescrever algumas ações que são padrões em objetos. O objeto **Proxy** é bem simples de entender, ele recebe 2 parâmetros: o objeto a ser manipulado e um objeto contendo as "armadilhas" (traps), que eu particularmente gosto de chamar de configurações:

```javascript
const target = {};
const settings = {};
const proxy = new Proxy(target, settings);
``` 
### Resolvendo o problema
Agora que conhecemos o objeto **Proxy**, iremos criar uma configuração para checar qual propriedade está sendo alterada o valor, e caso seja a propriedade ***_uuid***, lançamos uma exceção, vejam:

```javascript
const user = {
  _name: 'Erandir Junior',
  _email: 'erandir@email.com',
  _password: '123456',
  _uuid: '1234-1234-1234-1234',
  get name() {
    return this._name;
  },
  set name(name) {
  	if(name.length < 3) {
    	throw Error('Minimum 3 characters!');
    }
  
    this._name = name;
  },
  get email() {
    return this._email;
  },
  set email(email) {
    this._email = email;
  },
  get password() {
    return this._password;
  },
  set password(password) {
     this._password = password;
  },
  get uuid() {
    return this._uuid;
  }
};

const userProxy = new Proxy(user, {
  set (target, key, value) {
  	  if (key === '_uuid') {
    	  throw Error('This property cannot be modified!');
      }
    
      target[key] = value;
   }
});
```
O método ***set*** do segundo objeto passado para ***Proxy***, recebe 3 parâmetros: o objeto alvo, a chave desse objeto e o valor. Com isso, podemos fazer inúmeras checagens e verificações, podemos até mesmo bloquear qualquer modificação no nosso objeto. No exemplo acima, só bloqueamos a modificação do valor de ***_uuid***, caso tentassemos modificar o valor dessa propriedade, receberíamos uma mensagem de erro, o restante do fluxo continua funcionando normalmente:

```javascript
userProxy._uuid = []; // Uncaught Error: This property cannot be modified!
```

## Mais configurações
Podemos definir inúmeras configurações, vamos definir uma configuração para bloquear o acesso direto as propriedades do nosso objeto, além disso, também iremos bloquear o acesso a propriedades não definidas e não permitiremos a atribuição de novas propriedades:

```javascript
const user = {
  _name: 'Erandir Junior',
  _email: 'erandir@email.com',
  _password: '123456',
  _uuid: '1234-1234-1234-1234',
  get name() {
    return this._name;
  },
  set name(name) {
  	if(name.length < 3) {
    	throw Error('Minimum 3 caracters!');
    }
  
    this._name = name;
  },
  get email() {
    return this._email;
  },
  set email(email) {
    this._email = email;
  },
  get password() {
    return this._password;
  },
  set password(password) {
    this._password = password;
  },
  get uuid() {
    return this._uuid;
  }
};


const userProxy = new Proxy(user, {
  set (target, key, value) {
  	if (key === '_uuid') {
    	throw Error('This property cannot be modified!');
    }
    
    if (!(key in target)) {
    	throw Error('Property not found!');    	
    }

    target[key] = value;
  },
  get (target, key) {
  	if (key.startsWith('_')) {
    	throw Error('Property cannot be access!');
    }
    
    if (!(key in target)) {
    	throw Error('Property not found!');    	
    }
    
    return target[key];
  }
});
```

Então pessoal, caso queiram acessar uma determinada propriedade diretamente, obterão um erro, caso tentem acessar uma propriedade não definida, obterão um erro, caso tentem adicionar uma nova propriedade, isso mesmo, obterão um erro.

## Resumo
Com o objeto Proxy, podemos sobrescrever muitas configurações. No exemplo desse artigo, basicamente restringimos o acesso as propriedades de um objeto, mas podemos fazer muito mais. Como esse recurso foi lançado na versão ES6, podemos considerar que os navegadores atuais já dão suporte a esse recurso, porém, em caso de dúvida, é sempre bom checar no [caniuse.com](https://caniuse.com/?search=Proxy). 

Espero que tenham aproveitado esse artigo, até o próximo.