Como o prototype chain do Javascript é uma Linked List
Tags: javascript, webdev, beginners, programming
Published: 2022-05-17 05:01:11

É galera, entender o prototype chain do Javascript é um pouquinho mais difícil do que imaginava porque requer um entendimento básico sobre linked lists.

Se você não entende, vou te explicar o básico.

#Linked List

Uma Linked List, ou lista ligada, é uma lista de elementos onde cada elemento aponta para o próximo elemento da lista. Se formos pensar como objetos, teríamos o conteúdo do objeto e uma variável especial apontando para o próximo elemento da lista.

![Linked List](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wt93dh7sb4gurvffos2u.png)

Cada vez que você adicionar um elemento nessa lista, você tem que assegurar que o último elemento da lista vai apontar para este próximo elemento, elegendo este recém-adicionado elemento como o último elemento. Acho que é válido tentar ao menos criar uma implementação básica em qualquer linguagem para entender como ela funciona.

#Sobre Prototypes

Entendendo sobre linked lists, conseguimos entender de forma mais fácil a prototype chain. Acontece que toda função e objeto criados em Javascript, possuem uma propriedade .prototype associada, seja ela uma propriedade explicita ou uma propriedade interna.

```javascript
let someString = {abc:"abc"}
console.log(someString)
/*
> Object { abc: "abc" }
abc: "abc"
<prototype>: Object { … }
*/
```

Se formos investigar este prototype, ele possui uma propriedade __proto__, que possui mais uma propriedade __proto__ que aponta para null. Essas propriedades __proto__ são as propriedades .prototype internas do objeto. Cada objeto terá seu prototype onde eles vão apontar para uma outra prototype até o último prototype apontar para null, encerrando a cadeia de prototypes:

```javascript
Object { abc: "abc" }​
abc: "abc"​
<prototype>: Object { … }​​
  __defineGetter__: function __defineGetter__()
  __defineSetter__: function __defineSetter__()
  ​​__lookupGetter__: function __lookupGetter__()
​​  __lookupSetter__: function __lookupSetter__()
​​  > __proto__: Object { … }
​​​      __defineGetter__: function __defineGetter__()​​
      __defineSetter__: function __defineSetter__()
​​​      __lookupGetter__: function __lookupGetter__()
​​​      __lookupSetter__: function __lookupSetter__()
​​​      __proto__: null
```
Só isso já é evidência suficiente de que a prototype chain é uma linked list e que, para trabalhar com objetos, é necessário um conhecimento básico de referências. 

Em C, por exemplo, linked lists são implementadas utilizando ponteiros, e a lógica utilizada por baixo dos panos na prototype chain é a mesma aqui: um ponteiro na propriedade .prototype (ou __proto__) apontando para outros prototypes até chegar em um null.

#Por quê saber disso?

O estilo de objetos e herança utilizados em Javascript é baseado em prototypes. Então pense que o objeto não será apenas ele e seu construtor, mas uma cadeia de prototypes que dá uma certa capacidade para realizar vários chainings. Pense que se um método ou propriedade não está disponível imediatamente no objeto onde ele está sendo chamado, o runtime irá seguir toda a cadeia de prototypes até achar uma combinação para execução ou retorno de um valor. Isso é mais útil quando utilizamos Object.create e ocultamos as propriedades retornadas pelo Prototype por outras.


#Redefinindo prototypes

Por último, vou utilizar um pequeno exemplo do MDN onde, através de um construtor, é redefinido o prototype de uma função:

```javascript
const personPrototype = {
  greet() {
    console.log(`hello, my name is ${this.name}!`);
  }
}

function Person(name) {
  this.name = name;
}
```
Definimos aqui um object literal e uma função. Vamos investigar seus prototypes através do console.log:
```javascript
>> console.log(personPrototype)
  > Object { greet: greet() }
​      greet: function greet()
​      <prototype>: Object { … }

>> console.log(Person)
    > function Person(name)
        arguments: null
        caller: null
        length: 1
        name: "Person"
        prototype: Object { … }
        <prototype>: function ()
```
Preste muita atenção. A função Person tem um prototype ativo (não é apenas um prototype interno) que já foi reapontado pelo próprio runtime. Se abrir ela, vai ver que este prototype aponta para ela mesma.

```javascript
> prototype: Object { … }
​​    constructor: function Person(name)
    <prototype>: Object { … }
```
Isso é importante, pois quando for criado um novo objeto passando o Person como função construtora, a propriedade .prototype vai ser "copiada" para este novo objeto, contendo o construtor, ou seja, todo objeto criado a partir deste vai ter sua .prototype apontando para esta função.

```javascript
let eron = new Person("eron");
undefined

console.log(eron)
> Object { name: "eron" }
    name: "eron"
    > <prototype>: Object { … }
        constructor: function Person(name)
​​        <prototype>: Object { … }
```

Agora o objeto não tem este mesmo prototype, utilizando o que a própria linguagem declara para ele.

Feito isto, em Person, vamos então reapontar seu .prototype para o objeto PersonPrototype:

```javascript
Person.prototype = personPrototype;
Object { greet: greet() }

console.log(Person)
> function Person(name)
    arguments: null​
    caller: null​
    length: 1​
    name: "Person"​
  > prototype: Object { greet: greet() }​​
      greet: function greet()​​
      <prototype>: Object { … }​
    <prototype>: function ()
```

Percebam que o construtor sumiu do .prototype. Por quê? Porque o prototype agora está apontando para a função personPrototype, então temos que consertar isso. Se não, se for criado um novo objeto dessa forma e outro programador quiser seu constructor, ele nunca vai saber, nem mesmo se ele mergulhar no prototype chain:

```javascript
let carina = new Person("Carina"); 

carina.constructor
> function Object()​
  assign: function assign()​
  create: function create()​
  defineProperties: function defineProperties()​
  defineProperty: function defineProperty()​
  entries: function entries()​
  freeze: function freeze()​
  fromEntries: function fromEntries()​
  getOwnPropertyDescriptor: function getOwnPropertyDescriptor()​
  getOwnPropertyDescriptors: function getOwnPropertyDescriptors()​
  getOwnPropertyNames: function getOwnPropertyNames()​
  getOwnPropertySymbols: function getOwnPropertySymbols()​
  getPrototypeOf: function getPrototypeOf()​
  hasOwn: function hasOwn()​
  is: function is()​
  isExtensible: function isExtensible()​
  isFrozen: function isFrozen()​
  isSealed: function isSealed()​
  keys: function keys()​
  length: 1​
  name: "Object"​
  preventExtensions: function preventExtensions()​
  prototype: Object { … }​
  seal: function seal()​
  setPrototypeOf: function setPrototypeOf()​
  values: function values()​
  <prototype>: function ()
```

Vamos consertar isso atrelando um construtor à personPrototype. Nesse caso, ou atrelamos esse construtor direto no objeto personProtoype, ou descemos o prototype chain usando os ponteiros.

Opção 1:

```javascript
personPrototype.constructor = Person; 

console.log(Person)
> function Person(name)​
    arguments: null​
    caller: null​
    length: 1​
    name: "Person"​
    prototype: Object { greet: greet(), constructor: Person(name) }
```

Opção 2:

```javascript
Person.prototype.constructor = Person; 

console.log(Person)
> function Person(name)​
    arguments: null​
    caller: null​
    length: 1​
    name: "Person"​
    prototype: Object { greet: greet(), constructor: Person(name) }
```

Feito isso, como o constructor é um ponteiro, chamando-o novamente em cima do objeto criado anteriormente, verificamos que o comportamento agora está ok:

```javascript
carina.constructor
> function Person(name)
​  arguments: null
​  caller: null
​  length: 1
​  name: "Person"
​  prototype: Object { greet: greet(), constructor: Person(name) }
  <prototype>: function ()
```

Basicamente é isso. Prototype chain é difícil, mas depois que você entende, vê o conceito básico que está por trás dele, acredito que fique mais fácil de entender.

#Para saber mais

[Seção #Objetos do ECMAScript](https://262.ecma-international.org/12.0/#sec-objects)
[Herança e a cadeia de Protótipos](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain)