Você está na página 1de 93

Índice

Introdução
Prefácio
Capítulo 1: this Ou Aquilo?
Porque this ?
Confusões
O que é this ?
Capítulo 2: this Agora tudo faz sentido!
Call-site
Nada além de Regras
Tudo em Ordem
Exceções de Binding
this Léxico

Capítulo 3: Objetos
Sintaxe
Tipo
Conteúdos
Iteração
Capítulo 4: Confundindo Objetos com "Classes"
Teoria do Classes
Mecânica do Classes
Heranças de Classe
Mixins
Capítulo 5: Protótipos
[[Prototype]]

"Classes"
"Herança (Prototípica)"
Ligações de Objeto
Capítulo 6: Delegação de Comportamentos
Em Direção ao Design Orientado à Delegação
Classes vs. Objetos
Design Simplificado
Sintaxe Melhorada
Introspecção
Apêndice A: ES6 class
Apêndice B: Agradecimentos
Prefácio
Tenho certeza que você notou, mas o "JS" como título da série não é uma abreviação de palavras usadas para falar mal de JavaScript,
embora xingar sobre as peculiaridades da linguagem é algo com o qual todos nós podemos provavelmente nos identificar.

Desde os primórdios da web, o JavaScript tem sido a fundação de tecnologias que determinam a experiência interativa do conteúdo que
consumimos. Apesar de efeitos nos traçados do mouse e perturbadores pop-ups com prompts terem sido a forma que o JavaScript
começou, quase duas décadas depois, as capacidades do JavaScript cresceram em muitas ordens de magnitude, e poucos duvidam de sua
importância como coração da plataforma de software mais acessível do mundo: a web.

Como linguagem, tem sido alvo permanente de críticas, em parte por sua herança mas principalmente pela filosofia de sua concepção. Até
o seu nome evoca, como Brendan Eich uma vez colocou, um status de "irmão mais novo bobinho" se comparado ao seu irmão mais velho e
maduro "Java". De qualquer forma, o nome é um mero acidente relacionado à políticas e marketing. As duas linguagens são bem diferentes
em muitos pontos importantes. "JavaScript" está tão relacionado a "Java" quanto "Casaco" está relacionado com "Casa".

Pelo fato do JavaScript adotar conceitos e sintaxe idiomática de diversas linguagens, incluindo orgulhosas raízes procedurais ao estilo de C,
assim como sutis e menos óbvias raízes funcionais ao estilo de Scheme/Lisp, é bastante acessível para uma larga audiência de
desenvolvedores(as), até mesmo aqueles com pouca ou nenhuma experiência com programação. O exemplo de "Hello World" do JavaScript
é tão simples que a linguagem é convidativa e fácil de se sentir confortável logo no primeiro contato.

Apesar do JavaScript ser talvez uma das linguagens mais fáceis de se começar, suas excentricidades fazem com que o domínio sólido da
linguagem seja muito menos comum do que em outras linguagens. Enquanto se precisa um conhecimento aprofundado em linguagens
como C ou C++ para escrever um programa em grande escala, uma aplicação similar em JavaScript pode ser desenvolvida, e muitas vezes é,
apenas arranhando parte do que a linguagem pode fazer de verdade.

Conceitos sofisticados e profundamente enraizados na linguagem tendem a surgir de modo aparentemente simples, como a utilização de
funções como callbacks, o que encoraja o(a) desenvolvedor(a) JavaScript a usar a linguagem apenas como forma de alcançar um objetivo,
sem se preocupar muito com o que está acontecendo nos bastidores.

É, simultaneamente, uma linguagem simples, fácil de usar e com forte aceitação, mas também um conjunto de mecanismos de linguagem
complexos e cheios de nuances, cuja falta de um estudo minucioso pode mascarar um verdadeiro entendimento até mesmo para os(as)
desenvolvedores(as) JavaScript mais experientes.

É aí que se encontra o paradoxo do JavaSript, o tendão de Aquiles da linguagem, o desafio que estamos propondo. Pelo fato do JavaScript
poder ser utilizado sem sua compreensão, o verdadeiro conhecimento sobre a linguagem geralmente nunca é alcançado.

Missão
Se a cada ponto que você encontrar uma surpresa ou se sentir frustrado com JavaScript, sua resposta for adicionar mais um item à sua lista
negra, como muitos(as) estão acostumados(as) a fazer, você em breve entrará em uma penumbra cinzenta que irá te afastar de toda a
riqueza da linguagem.

A ideia de aprender JavaScript separando todas as partes frustrantes da linguagem foi famosamente apelidada de "A parte boa". Eu vou
implorar a você, querido(a) leitor(a), para ao invés disso, considerar essa parte como "A Parte Fácil" ou "A Parte Segura", ou até mesmo "A
Parte Incompleta".

A série de livros You Don't Know JavaScript oferece um desafio inverso: aprender e entender profundamente tudo sobre JavaScript, até
mesmo "As Partes Difíceis".

Aqui, nós abordamos profundamente a tendência dos(das) desenvolvedores(as) JS de aprender "apenas o suficiente" para seguir em frente,
sem se forçarem a entender exatamente a forma e o motivo pelos quais a linguagem se comporta da maneira que o faz. Além disso,
evitamos o senso comum de recuar quando a estrada fica irregular ou perigosa.

Eu não fico feliz, nem você deveria ficar, em parar assim que algo funciona, sem realmente entender o porquê. Eu gentilmente desafio você a
viajar para a parte "menos acessada" da estrada e se envolver com tudo que o JavaScript pode fazer. Com esse conhecimento, nenhuma
técnica, nenhum framework, nenhum nome que virou moda na semana estará além da sua compreensão.

Cada um desses livros tem um enfoque específico em partes da linguagem que em geral são compreendidas de maneira incorreta ou mal
compreendidas, e mergulha a fundo nelas. Você deverá finalizar a leitura com confiança em relação ao seu entendimento, não só das partes
teóricas, mas também das partes que você "precisa saber".
O JavaScript que você conhece agora é um provável conjunto de partes ensinadas a você por outros(as) que também tiveram um
conhecimento incompleto. Esse JavaScript não é nada além de uma sombra da verdadeira linguagem. Você não conhece realmente o
JavaScript, ainda, mas se você caminhar por esta série você saberá. Continuem lendo, amigos(as). O JavaScript aguarda vocês.

Sumário
JavaScript é incrível! É fácil de aprendê-la parcialmente, e muito difícil aprendê-la por completo (ou até mesmo suficientemente). Quando
desenvolvedores(as) se sentem confusos(as), geralmente culpam a linguagem ao invés de sua falta de conhecimento. Esses livros têm como
objetivo corrigir isso, inspirando uma apreciação forte da linguagem que agora você pode (e deve) conhecer profundamente.

Nota: Muitos dos exemplos desse livro supõem que você dispõe de ambientes JavaScript modernos (e de longo alcance futuro), como o
ES6. Alguns códigos podem não funcionar como descrito se você estiver utilizando ambientes mais antigos (pre-ES6).
Capítulo 1: this Ou Aquilo?
Um dos mecanismos mais confusos no JavaScript é a palavra-chave this . É uma palavra-chave de identificação especial que é definida
automaticamente no escopo de cada função, mas o que exatamente ela se refere atormenta até mesmo os desenvolvedores JavaScript mais
experientes.

Qualquer tecnologia bastante avançada é indistinguível da magia. -- Arthur C. Clarke

O mecanismo this do JavaScript na verdade não é tão avançado, mas os desenvolvedores muitas vezes utilizam essa citação em suas
mentes inserindo "complexa" ou "confusa", e não há dúvidas que, sem uma clara compreensão, this pode parecer totalmente magia em
sua confunsão.

Nota: A palavra "this" (isto) é um pronome terrivelmente comum em diálogos de forma geral (em inglês). Então, pode ser muito difícil,
especialmente oralmente, determinar se estamos usando "isto" como um pronome, ou usando para realmente referir à palavra-chave de
identificação. Para deixar claro, sempre vou utilizar this para referi à palavra-chave, e "isto" ou isto ou isto caso contrário.

Por que this ?

Se o mecanismo this é tão confuso, até mesmo para desenvolvedores JavaScript experientes, pode-se perguntar: por que ainda é útil? É
um problema ou vale a pena? Antes de entrarmos no como, devemos examinar o porquê.

Vamos tentar ilustrar a motivação e utilidade do this :

function identify() {

return this.name.toUpperCase();

function speak() {

var greeting = "Hello, I'm " + identify.call( this );

console.log( greeting );

var me = {

name: "Kyle"

};

var you = {

name: "Reader"

};

identify.call( me ); // KYLE

identify.call( you ); // READER

speak.call( me ); // Hello, I'm KYLE

speak.call( you ); // Hello, I'm READER

Se o como desse trecho de código confunde você, não se preocupe! Já vamos chegar lá. Apenas coloque isso de lado por um momento
para que possamos olhar mais claramente o porquê.

Esse pedaço de código permite que as funções identify() e speak() sejam reutilizadas em diversos objetos ( me and you ) de contexto,
em vez de precisar de uma versão separada da função para cada objeto.

Ao invés de confiar no this , você poderia passar explicitamente um objeto de contexto, tanto para identify() , quanto para speak() .

function identify(context) {

return context.name.toUpperCase();

function speak(context) {

var greeting = "Hello, I'm " + identify( context );

console.log( greeting );

identify( you ); // READER

speak( me ); // Hello, I'm KYLE


No entanto, o mecanismo this provê um modo mais elegante de "passar" um objeto como referência implicitamente, levando a um
design de API mais limpo e fácil de ser reutilizado.

Quanto mais complexo for o padrão de utilização, você verá mais claramente que passar o contexto como um parâmetro explícito é
frequentemente mais confuso do que passar o contexto com o this . Quando exploramos objetos e protótipos, você verá a utilidade de
uma coleção de funções sendo capaz de referenciar automaticamente o objeto no contexto apropriado.

Confusões
Vamos começar a explicar como this realmente funciona em breve, mas primeiro nós devemos dissipar alguns equívocos sobre como ele
não funciona.

O nome "this" gera confusão quando os desenvolvedores tentam pensar nele de forma literal. Existem dois significados muitas vezes
adotados, mas ambos estão incorretos.

Ele mesmo
A tentação mais comum é assumir que this se refere a própria função. Essa é uma conclusão gramatical aceitável, pelo menos.

Por que você gostaria de referir a uma função de dentro dela mesma? As razões mais comuns poderiam ser coisas como recursão (chamar
uma função de dentro dela mesma) ou tendo um manipulador de evento que pode se desvincular quando for chamado pela primeira vez.

Desenvolvedores novatos nos mecanismos do JS muitas vezes pensam que referenciar uma função como um objeto (todas as funções em
JavaScript são objetos!) permite armazenar estado (valores em propriedades) entre as chamadas da função. Embora isso certamente seja
possível e com algumas limitações de uso, o restante do livro irá expor muitos outros padrões com melhores lugares para se armazenar
estado fora do objeto da função.

Mas por um momento, nós vamos explorar esse padrão, para ilustrar como this não deixa uma função pegar uma referência a ela mesma
como podemos ter assumido.

Considere o seguinte código, onde nós tentaremos contar quantas vezes a função ( foo ) foi chamada:

function foo(num) {
console.log( "foo: " + num );

// keep track of how many times `foo` is called

this.count++;

foo.count = 0;

var i;

for (i=0; i<10; i++) {

if (i > 5) {

foo( i );

// foo: 6

// foo: 7

// foo: 8

// foo: 9

// how many times was `foo` called?

console.log( foo.count ); // 0 -- WTF?

foo.count ainda é 0 , mesmo que as quatro declarações console.log claramente indicam que foo(..) foi, de fato, chamada quatro
vezes. A frustração se origina de uma interpretação muito literal do que o this (em this.count++ ) significa.

Quando o código executa foo.count = 0 , realmente está adicionando a propriedade count ao objeto da função foo . Mas para a
referência this.count de dentro da função, this não está de fato apontando de todo para aquele objeto da função, e então, mesmo que
os nomes das propriedades sejam os mesmos, os objetos-raiz são diferentes, e resultam em confusão.
Nota: Uma desenvolvedora responsável deve perguntar nesse momento, "Se eu estava incrementando uma propriedade count mas não
era a que eu esperava, que count eu estava incrementando?" De fato, ela está se aprofundando, ela descobrirá que acidentalmente criou
uma variável global count (veja o Capítulo 2 para como isso aconteceu!), e atualmente ela tem o valor NaN . Naturalmente, uma vez que ela
descobre esse resultado peculiar, ela então tem um novo conjunto de perguntas: "Como essa variável é global e por que ela acabou como
NaN ao invés de algum valor adequado?" (veja o Capítulo 2).

Ao invés de parar e analisar com profundidade o porquê da referência this parecer não se comportar da maneira esperada, e responder
essas questões difíceis mas importantes, muitos desenvolvedores simplesmentes evitam esse problema completamente, e escrevem alguma
outra solução, como criar um outro objeto para armazenar a propriedade count :

function foo(num) {
console.log( "foo: " + num );

// keep track of how many times `foo` is called

data.count++;

var data = {

count: 0

};

var i;

for (i=0; i<10; i++) {

if (i > 5) {

foo( i );

// foo: 6

// foo: 7

// foo: 8

// foo: 9

// how many times was `foo` called?

console.log( data.count ); // 4

Embora seja verdade que essa abordagem "resolve" o problema, infelizmente ela simplesmente ignora o problema real -- a falta de
entendimento do que o this significa e como ele funciona -- e, ao invés disso, retrocede para a zona de conforto de um mecanismo mais
familiar: o escopo léxico.

Nota: Escopo léxico é um mecanismo muito bom e útil; Eu não estou menosprezando o uso dele, de forma alguma (veja o título "Escopos &
Closures" desta série de livros). Mas constantemente adivinhar como usar o this , e geralmente estar errado, não é uma boa razão para se
voltar para o escopo léxico e nunca aprender porquê o this ilude você.

Para referenciar o objeto da função de dentro dela mesma, this por si só será em geral insuficiente. Normalmente você precisa de uma
referência para o objeto da função por um identificador léxico (variável) que aponta para ela.

Considere essas duas funções:

function foo() {

foo.count = 4; // `foo` refers to itself

setTimeout( function(){

// anonymous function (no name), cannot

// refer to itself

}, 10 );

Na primeira função, chamada de "função nomeada", foo é uma referência que pode ser usada para se referir a função de dentro dela
mesma.

Mas no segundo exemplo, a função de callback passada para o setTimeout(..) não tem identificador (então chamada de "função
anônima"), então não há uma maneira adequada de se referenciar o próprio objeto da função.
Nota: A referência old-school, mas agora depreciada e de torcer o nariz, arguments.callee dentro uma função também aponta para o
objeto da função que está sendo executado. Essa referência é normalmente a única forma de acessar um objeto de função anônima de
dentro dela mesma. A melhor abordagem no entanto, é evitar o uso de funções anônimas completamente, pelo menos para as que
necessitam de auto-referência, e em vez disso utilizar funções nomeadas (expressões). arguments.callee é obsoleta e não deve ser usada.

Outra solução para nosso atual exemplo seria usar o identificador foo como referência ao objeto da função em cada lugar, e não utilizar o
this absolutamente, o que funciona:

function foo(num) {
console.log( "foo: " + num );

// keep track of how many times `foo` is called

foo.count++;

foo.count = 0;

var i;

for (i=0; i<10; i++) {

if (i > 5) {

foo( i );

// foo: 6

// foo: 7

// foo: 8

// foo: 9

// how many times was `foo` called?

console.log( foo.count ); // 4

No entanto, essa abordagem evita o real entendimento do this e depende inteiramente do escopo léxico da variável foo .

Ainda outro modo de abordar esse problema é forçar o this para realmente apontar para o objeto da função foo :

function foo(num) {
console.log( "foo: " + num );

// keep track of how many times `foo` is called

// Note: `this` IS actually `foo` now, based on

// how `foo` is called (see below)

this.count++;

foo.count = 0;

var i;

for (i=0; i<10; i++) {

if (i > 5) {

// using `call(..)`, we ensure the `this`

// points at the function object (`foo`) itself

foo.call( foo, i );

// foo: 6

// foo: 7

// foo: 8

// foo: 9

// how many times was `foo` called?

console.log( foo.count ); // 4

Ao invés de evitar o this , nós abraçamos ele. Vamos explicar daqui a pouco o como essas técnicas funcionam muito mais completamente,
então não se preocupe se você ainda estiver um pouco confuso!

Seu Escopo
O próximo equívoco mais comum sobre o significado do this é que ele, de alguma forma, se refere ao escopo léxico da função. É uma
questão complicada, porque por um lado isso é verdade, mas pelo outro é bastante errado.

Para ser claro, this não é, de forma alguma, uma referência para o escopo léxico da função. É verdade que internamente, o escopo é como
um objeto com propriedades para cada um dos identificadores disponíveis. Mas o "objeto" do escopo não é acessível para o código
JavaScript. É uma parte interna da implementação do Motor.

Considere o código que tenta (e falha!) cruzar o limite e usar o this para, implicitamente, se referir ao escopo léxico da função:

function foo() {

var a = 2;

this.bar();

function bar() {

console.log( this.a );

foo(); //undefined

Existe mais de um erro neste trecho de código. Embora ele pode parecer artificial, o código que você vê é uma essência do verdadeiro
código do mundo real, que foi postado em fóruns públicos de ajuda da comunidade. É uma maravilhosa (se não triste) ilustração de quão
errada pode ser a suposição do this .

Primeiramente, é feita uma tentativa de referenciar a função bar() pelo this.bar() . É certamente quase um acidente que isso funcione,
mas vamos explicar como em breve. A forma mais natural de chamar bar() seria omitindo o sedutor this. e apenas fazer uma referência
léxica ao identificador.

No entanto, o desenvolvedor que escreve esse tipo de código está tentando utilizar this para criar uma ponte entre o escopo léxico de
foo() e bar() , então bar() teria acesso a variável a de dentro do escopo de foo() . Tais pontes não são possíveis. Você não pode usar
uma referência this para procurar algo em um escopo léxico. Isso não é possível.

Toda vez que você sentir que está tentando mixar escopo léxico procurando algo com this , lembre-se: não existem pontes.

O que é this ?

Deixando de lado as várias premissas incorretas, vamos focar nossa atenção para como o mecanismo this realmente funciona.

Mais cedo falamos que o this não é um vínculo do momento de escrita mas um vínculo do momento de execução. Ele é baseado nas
condições do contexto da função que o chama. O vínculo this não tem nada a ver com onde a função foi declarada, mas em vez disso,
tem tudo a ver com com a forma que a função é chamada.

Quando uma função é chamada, um registro de ativação, também conhecido como contexto de execução, é criado. Esse registro contém
informações sobre de onde a função foi chamada (a pilha de chamadas), como a função foi chamada, quais parâmetros foram passados, etc.
Uma das propriedades desse registro é a referência this , que será usada pela duração da execução dessa função.

No próximo capítulo, nós vamos aprender a encontrar o call-site da função para determinar como sua execução será vinculada ao this .

Revisão (TL;DR)
O vínculo this é uma constante fonte de confusão para os desenvolvedores JavaScript que não param para aprender como o mecanismo
realmente funciona. Suposições, tentativa e falha, e copiar-e-colar cegamente das respostas do Stack Overflow não são maneiras eficazes ou
adequadas de alavancar esse importante mecanismo this .

Para entender o this , você primeiro precisa aprender o que o this não é, apesar de quaisquer suposições ou equívocos que podem levá-
lo por esses caminhos. this não é uma referência para a própria função, nem é uma referência para o escopo léxico da função.

this na verdade é um vínculo que é feito quando a função é chamada, e o que é referenciado é determinado inteiramente pelo call-site de
onde a função é chamada.
Capítulo 2: this Agora tudo faz sentido!
No Capítulo 1, nós eliminamos diversos equívocos relacionados à this e aprendemos que this é um binding feito para cada invocação de
função, baseado inteiramente no seu call-site (como a função é chamada).

Call-site
Para entender o binding do this , precisamos entender o call-site: o lugar no código onde a função é chamada (não onde ela é declarada).
Nós devemos inspecionar o call-site para responder a seguinte questão: a quem este this está fazendo referência?

Encontrar o call-site é geralmente: "ir até o local de onde a função é chamada", mas não é sempre tão fácil assim, já que alguns padrões de
código podem obscurecer o verdadeiro call-site.

O que é importante é pensar sobre o call-stack (a pilha de funções que foram chamadas para nos deixar no momento atual na execução). O
call-site que devemos nos importar está dentro da invocação anterior à função em execução atual.

Demonstremos o call-stack e o call-site:

function baz() {

// call-stack é: `baz`

// sendo assim, nosso call-site está no escopo global

console.log( "baz" );

bar(); // <-- call-site para `bar`

function bar() {

// call-stack é: `baz` -> `bar`

// sendo assim, nosso call-site está em `baz`

console.log( "bar" );

foo(); // <-- call-site para `foo`

function foo() {

// call-stack é: `baz` -> `bar` -> `foo`

// sendo assim, nosso call-site está em `bar`

console.log( "foo" );

baz(); // <-- call-site para `baz`

Seja cuidadoso ao analizar o código ao procurar pelo call-site atual (através do call-stack), visto que ele é a única coisa que importa para o
binding do this .

Nota: Você pode visualizar o call-stack mentalmente ao ver a cadeia de funções em ordem, como fizemos nos comentários no trecho de
código anterior. Entretanto, esta é uma forma dolorosa e passível a erros. Uma outra forma de ver o call-stack é usar a ferramenta de debug
do seu navegador. A maioria dos navegadores modernos tem ferramentas do desenvolvedor nativas, as quais incluem um debugger de JS.
No trecho de código anterior, você poderia ter definido um breakpoint na ferramenta para a primeira linha da função foo() , ou
simplesmente inserido a instrução debugger; nessa primeira linha. Quando você rodar a página, o debugger irá pausar neste ponto, e irá
mostrar à você a lista de funções que foram acionadas para poder chegar à esta linha, que virá a ser o seu call stack. Sendo assim, se você
está tentando diagnosticar o binding de this , use as ferramentas do desenvolvedor para acessar o call-stack, e então busque o segundo
item começando do topo, e ela irá mostrar à você o verdadeiro call-site.

Nada além de Regras


Iremos direcionar nossa atenção agora para como o call-site determina onde o this irá apontar durante a execução de uma função.

Você precisa inspecionar o call-site e determinar onde as 4 regras se aplicam. Iremos primeiro explicar cada uma dessas 4 regras de maneira
independente, e depois iremos ilustrar sua ordem de precedência, caso possamos aplicar multiplas regras para o call-site.

Binding padrão
A primeira regra que iremos examinar vem do caso mais comum ao se chamar uma função: invocar uma função separada. Pense nessa regra
de this como a regra padrão para todos os casos quando nenhuma outra regra puder ser aplicada.

Considere esse código:

function foo() {

console.log( this.a );

var a = 2;

foo(); // 2

A primeira coisa a se notar, se você ainda não estiver notado, é que as variáveis declaradas no escopo global, como var a = 2 , são
sinônimos de propriedades de objetos globais com o mesmo nome. Elas não são cópias umas das outras, eles são as outras. Pense nisso
como os dois lados da mesma moeda.

A segunda coisa a se notar, nós vemos que quando foo() é chamado, this.a se refere à nossa variável global a . Por quê? Porque nesse
caso, o binding padrão para this se aplica ao chamado da função, sendo assim ela aponta this para o objeto global.

Como podemos saber se a regra do binding padrão se aplica aqui? Nós examinaremos o call-site para ver como foo() é chamado. No
nosso trecho de código, foo() é chamado como uma referência plana, sem nenhuma decoração. Nenhuma das outras regras que iremos
demonstrar seriam aplicadas aqui, sendo assim o binding padrão é o que seria aplicado.

Se o strict mode estiver ativo, o objeto global não é elegível para o binding padrão, sendo this nesse caso sendo apresentado como
undefined .

function foo() {

"use strict";

console.log( this.a );

var a = 2;

foo(); // TypeError: `this` is `undefined`

Um detalhe sutil, mas importante, é que: mesmo que a regra geral do binding de this seja inteiramente baseada no call-site, o objeto
global é eligível apenas para o binding padrão se o conteúdo de foo() não estiver rodando em strict mode ; o estado strict mode do
call-site de foo() é irrelevante.

function foo() {

console.log( this.a );

var a = 2;

(function(){

"use strict";

foo(); // 2

})();

Nota: Misturar código strict mode e código não- strict mode juntos não é uma boa ideia. Seu programa deveria ser inteiramente ou
Strict ou não-Strict. Entretanto, às vezes você inclui uma biblioteca externa que tem um modo diferente do seu código, então esteja atento
com esse sutil detalhe sobre compatibilidade.

Binding Implícito
Outra regra à se considerar é: o call-site tem um objeto como contexto, também chamado de objeto proprietário ou que contêm, apesar
destes termos alternativos serem levemente enganosos.

Considere:
function foo() {

console.log( this.a );

var obj = {

a: 2,

foo: foo

};

obj.foo(); // 2

Primeiramente, note a maneira como foo() é declarado e depois adicionado como uma propriedade de referência em obj .
Independentemente se foo() é inicialmente declarado no obj , ou se é adicionado como uma referência depois (como o trecho mostra),
em nenhum dos casos essa função realmente é "possuída" ou está "contida" pelo objeto obj .

Entretanto, a call-site usa o contexto do obj para referenciar a função, então você poderia dizer que o objeto obj é "dono" ou "contém" a
função de referência no momento que a função é chamada.

Qualquer que seja a forma que você chama esse padrão, no momento em que foo() é chamado, é precedido por uma referência de objeto
à obj . Quando existe um objeto de contexto para uma função referenciada, a regra do binding implítico diz que deve ser aquele objeto que
deve ser usado para o binding da função this chamada.

Pelo fato de que obj é o this para a chamada de foo() , this.a é sinônimo de obj.a .

Apenas o topo/último nível de uma cadeia de propriedade de um objeto referênciado é o que importa para o call-site. Por exemplo;

function foo() {

console.log( this.a );

var obj2 = {

a: 42,

foo: foo

};

var obj1 = {

a: 2,

obj2: obj2

};

obj1.obj2.foo(); // 42

Perda implícita

Uma das frustrações mais comuns que o binding do this cria é quando uma função bindada implícitamente perde seu binding, o que
normalmente quer dizer que ela retorna ao seu binding padrão, que ou é do objeto global ou undefined , dependendo do strict mode .

Considere:

function foo() {

console.log( this.a );

var obj = {

a: 2,

foo: foo

};

var bar = obj.foo; // referência para a função!

var a = "oops, global"; // `a` também é propriedade do objeto global

bar(); // "oops, global"

Apesar de bar aparentemente ser uma referência para obj.foo , na realidade, é apenas outra referência para o próprio foo . Além do
mais, o call-site é o que importa, e o call-site é bar() , o que é uma chamada simples e não-decorada, e por isso o binding padrão se aplica.
A maneira mais sútil, mais comum, e mais inesperada em que isso acontece é quando passamos uma função de callback:

function foo() {

console.log( this.a );

function doFoo(fn) {

// `fn` é apenas mais uma referência para `foo`

fn(); // <-- call-site!

var obj = {

a: 2,

foo: foo

};

var a = "oops, global"; // `a` também é propriedade do objeto global

doFoo( obj.foo ); // "oops, global"

Passagem de parâmetros é apenas uma atribuição implícita, e já que estamos passando uma função, é uma atribuição por referência
implícita, então o resultado final é o mesmo que no trecho anterior.

Mas e se a função que você estiver passando como callback não for sua, mas nativa da linguagem? Sem diferenças, o resultado é o mesmo.

function foo() {

console.log( this.a );

var obj = {

a: 2,

foo: foo

};

var a = "oops, global"; // `a` também é propriedade do objeto global

setTimeout( obj.foo, 100 ); // "oops, global"

Pense sobre essa pseudo-implementação teórica e crua do setTimeout() , fornecido nativamente pelo ambiente JavaScript:

function setTimeout(fn,delay) {

// espera (de alguma forma) por `delay` milissegundos

fn(); // <-- call-site!

É bem comum que a nossa função de callback perca seu binding ao this , como nós acabamos de ver. Mas outra maneira em que o this
pode nos surpreender é quando a função que passamos nosso callback intencionalmente muda o this para a chamada. Event handlers em
bibliotecas JavaScript populares são bem afeiçoados de forçar seu callback a ter um this que aponta para, por exemplo, o elemento da
DOM que disparou o evento. Enquanto isso pode ser útil algumas vezes, outras vezes podem ser completamente enfurecedor. Infelizmente,
essas ferramentas raramente deixam você escolher.

De qualquer forma o this é modificado inesperadamente, você não está realmente com o controle de como sua função de callback
referenciada vai ser executada, então você não tem nenhuma forma (ainda) de controlar o call-site para dar a ele seu binding desejado.
Veremos em breve, uma maneira de "consertar" essa problema consertando o this .

Binding Explícito
Com o binding implícito como acabamos de ver, nós tivemos que alterar o objeto em questão para incluir a referência dele mesmo para a
função, e usar essa referência da propriedade da função para indiretamente (implicitamente) fazer o bind do this para o objeto.

Mas, e se você quiser forçar uma chamada de função para usar um objeto específico para o binding this , sem colocar uma referência de
propriedade de função no objeto?
"Todas" as funções da linguagem têm algumas utilidades disponíveis nelas (via [[Prototype]] -- mais disso adiante) que podem ser úteis
para essa tarefa. Especificamente, funções têm os métodos call(..) e apply(..) . Tecnicamente, ambientes de host Javascript, por vezes,
fornecem funções que são especiais o suficiente (uma maneira gentil de colocá-lo!) que elas não tem tal funcionalidade. Mas essas são
poucas. A vasta maioria das funções fornecidas, e certamente todas as funções que você irá criar, têm acesso ao call(..) e apply(..) .

Como essas ferramentas funcionam? Ambas tomam, como primeiro parâmetro, um objeto para usar o this , e então invocam a função com
o this específicado. Já que você está claramente indicando o que você quer que seja this , chamamos de binding explícito.

Considere:

function foo() {

console.log( this.a );

var obj = {

a: 2

};

foo.call( obj ); // 2

Invocando foo com binding explícito no foo.call(..) nos permite forçar o this para ser o obj .

Se você simplesmente passar uma valor primitivo (do tipo string , boolean ou number ) como o binding this , o valor primitivo é
encapsulado na sua forma de objeto ( new String(..) , new Boolean(..) , ou new Number(..) , respectivamente). Isso é frequentemente
referido como "boxing".

Nota: Com relação ao binding this , call(..) e apply(..) são idênticos. Eles comportam-se de maneira diferente com seus parâmetros
adicionais, mas isso não é algo com que nos importamos atualmente.

Infelizmente, binding explicito sozinho continua não oferecendo nenhuma solução para o problema mencionado anteriormente, de uma
função "perdendo" seu binding this pretendido, ou apenas ter ele passado por um framework, etc.

Hard Binding

Mas um padrão diferente sobre binding explícito realmente faz o truque. Considere:

function foo() {

console.log( this.a );

var obj = {

a: 2

};

var bar = function() {

foo.call( obj );

};

bar(); // 2

setTimeout( bar, 100 ); // 2

// `bar` aplica hard bind no `this` de `foo` para `obj`

// então isso não pode ser substituído

bar.call( window ); // 2

Vamos agora examinar como essa variação funciona. Nós criamos uma função bar() que, internamente, chama manualmente
foo.call(obj) , invocando de forma forçada foo com o binding obj para this . Não importa quão tarde você invoque a função bar , ela
vai manualmente invocar foo com obj . Esse binding é explícito e forte, então o chamamos de hard binding.

O modo mais comum de encapsular uma função com um hard binding é criar uma via de quaisquer argumentos passados ​e qualquer valor
de retorno recebido:

function foo(something) {

console.log( this.a, something );

return this.a + something;

var obj = {

a: 2

};

var bar = function() {

return foo.apply( obj, arguments );

};

var b = bar( 3 ); // 2 3

console.log( b ); // 5

Outra forma de representar esse padrão é criar um helper reutilizável:

function foo(something) {

console.log( this.a, something );

return this.a + something;

// `bind` helper simples

function bind(fn, obj) {

return function() {

return fn.apply( obj, arguments );

};

var obj = {

a: 2

};

var bar = bind( foo, obj );

var b = bar( 3 ); // 2 3

console.log( b ); // 5

Já que hard binding é um padrão bem comum, é fornecido como uma utilidade nativa do ES5: Function.prototype.bind , e é usada assim:

function foo(something) {

console.log( this.a, something );

return this.a + something;

var obj = {

a: 2

};

var bar = foo.bind( obj );

var b = bar( 3 ); // 2 3

console.log( b ); // 5

bind(..) retorna uma nova função que é escrita para chamar a função original com o contexto do this definido como você especificou.

Nota: No ES6, a função que faz o hard binding produzida por bind(..) tem uma propriedade .name que deriva da função alvo original.
Por exemplo: bar = foo.bind(..) deveria ter um valor bar.name de "bound foo" , que é o nome da chamada da função que deve
aparecer em um rastreamento de pilha.

"Contextos" de chamadas de API

Muitas funções de bibliotecas, e de fato muitas funções nativas na linguagem JavaScript e em seus ambientes, fornecem um parâmetro
opcional, geralmente chamado "contexto", que é projetado como uma solução para você não ter que usar o bind(..) para garantir que
seu callback use um this em particular.

Por exemplo:

function foo(el) {

console.log( el, this.id );

var obj = {

id: "awesome"

};

// use `obj` como `this` para chamadas `foo(..)`

[1, 2, 3].forEach( foo, obj ); // 1 awesome 2 awesome 3 awesome

Internamente, essas funções certamente usam ligação explícita via call (..) ou apply (..) , poupando você do problema.

new Binding
A quarta e última regra para binding do this requer que nós repensemos um equívoco muito comum sobre funções e objetos no
JavaScript.

Em linguagens tradicionais orientadas por classes, "construtores" (constructors) são métodos especiais anexados nas classes, que quando a
classe é instanciada com um operador new , o construtor dessa classe é chamado. Isso geralmente se parece com algo assim:

something = new MyClass(..);

JavaScript tem um operador new e o padrão de código para utilizá-lo é basicamente o mesmo que vemos naquelas linguagens orientadas
à classes; a maioria dos desenvolvedores assumem que o mecanismo JavaScript está fazendo algo similar. Porém, realmente não há
nenhuma relação com funcionalidades orientadas à classes no uso do new em JS.

Primeiro, vamos redefinir o que é um "construtor" em JavaScript. Em JS, construtores são apenas funções que são chamadas quando o
operador new está na frente delas. Elas não são vinculadas à classes, nem estão instanciando-as. Elas não são nem tipos especiais de
funções. Elas são apenas funções normais que são, em essência, sequestradas pelo uso de new quando invocadas.

Por exemplo, a função Number(..) atua como um construtor, citação da especificação ES5.1:

15.7.2 O Construtor Number

Quando Number é chamado como parte de uma expressão ele é um construtor: ele inicializa os novos objetos criados.

Então, praticamente qualquer função, incluindo funções de objetos nativos como Number(..) (Veja o capítulo 3) podem ser chamadas com
new à frente, e isso faz dessa chamada uma chamada de construtor. Essa é uma importante mas sútil diferença: não existem "funções
construtoras" mas sim chamadas construtoras de funções.

Quando uma função é invocada com new à sua frente, também conhecida como chamada de construtor, as seguintes coisas são feitas
automaticamente:

1. um objeto novo em folha é criado (construído)


2. o objeto recém construído é linkado ao [[Prototype]]
3. o objeto recém construído é definido como bind do this para aquela chamada de função
4. a menos que a função retorne seu próprio objeto alternado, a chamada da função invocada new vai retornar o objeto recém
construído automaticamente.

Os passos 1, 3 e 4 aplicam-se à nossa discussão atual. Nós vamos pular o passo 2 por agora e voltar nele no Capítulo 5.

Considere esse código:

function foo(a) {

this.a = a;

var bar = new foo( 2 );

console.log( bar.a ); // 2

Ao chamar foo(..) com new a sua frente, nós construímos um novo objeto e definimos esse novo objeto como this para a chamada de
foo(..) . Então new é a última maneira que uma chamada de função this pode sofrer o bind. Vamos chamar isso de new binding .

Tudo em Ordem
Então, agora descobrimos as 4 regras para ligação do this em chamadas de função. Tudo que você precisa fazer é encontrar o local de
chamada e inspecioná-lo para ver qual regra se aplica. Mas, e se o local de chamada tiver várias regras elegíveis? Deve haver uma ordem de
precedência para essas regras e, assim, demonstraremos em seguida que ordem aplicar à elas.

Deve ficar claro que o default binding (binding padrão) é a regra de prioridade mais baixa das 4. Então, vamos deixar isso de lado.

Qual tem maior precedência, binding implícito ou binding explícito? Vamos testar:

function foo() {

console.log( this.a );

var obj1 = {

a: 2,

foo: foo

};

var obj2 = {

a: 3,

foo: foo

};

obj1.foo(); // 2

obj2.foo(); // 3

obj1.foo.call( obj2 ); // 3

obj2.foo.call( obj1 ); // 2

Portanto, binding explícito tem precedência sobre binding implícito, o que significa que você deve perguntar primeiro se o binding explícito é
aplicado antes de verificar pelo binding implícito.

Agora, só precisamos descobrir em qual precedência o new binding se encaixa.

function foo(something) {

this.a = something;

var obj1 = {

foo: foo

};

var obj2 = {};

obj1.foo( 2 );

console.log( obj1.a ); // 2

obj1.foo.call( obj2, 3 );

console.log( obj2.a ); // 3

var bar = new obj1.foo( 4 );

console.log( obj1.a ); // 2

console.log( bar.a ); // 4

OK, o new binding tem maior precedência que o binding implícito. Mas você acha que o new binding tem maior ou menor precedêcia sobre
o binding explícito?

Nota: new e call / apply não podem ser usados juntos, então new foo.call(obj1) não é permitido, para testar o new binding
diretamente contra o binding explícito. Mas nós ainda podemos usar o hard binding para testar a precedência entre as duas regras.

Antes de explorarmos isso em uma listagem de código, pense em como o hard binding funciona fisicamente, Function.prototype.bind(..)
cria uma nova função de encapsulamento que é codificada para ignorar seu próprio binding this (qualquer que seja), e usar um que
fornecemos manualmente.

Por esse raciocínio, parece óbvio assumir que hard binding (que é uma forma de binding explícito) tem maior precedência que new binding,
e assim não pode ser substituído por new .

Vamos checar:
function foo(something) {

this.a = something;

var obj1 = {};

var bar = foo.bind( obj1 );

bar( 2 );

console.log( obj1.a ); // 2

var baz = new bar( 3 );

console.log( obj1.a ); // 2

console.log( baz.a ); // 3

Epa! bar tem hard binding contra obj1 , mas new bar(3) não mudou o obj1.a para ser 3 como havíamos esperado. Em vez disso, a
chamada hard binding (para obj1 ) é passível de ser substituída com new . Desde que new foi aplicada, nós temos de volta o objeto recém
criado, que nomeamos como baz , e nós vemos de fato que baz.a tem o valor 3 .

Isso pode ser surpreendente se você voltar para nosso bind helper "fake":

function bind(fn, obj) {

return function() {

fn.apply( obj, arguments );

};

Se você pensar sobre como o código do helper funciona, não há uma maneira da chamada do operador new substituir o hard binding para
obj como nós observamos.

Mas a função nativa Function.prototype.bind(..) a partir do ES5 é mais sofisticada, na verdade é mais ou menos. Aqui está o polyfill
(ligeriamente reformulado) fornecido pela página do MDN para bind(..) .

if (!Function.prototype.bind) {

Function.prototype.bind = function(oThis) {

if (typeof this !== "function") {

// closest thing possible to the ECMAScript 5

// internal IsCallable function

throw new TypeError( "Function.prototype.bind - what " +

"is trying to be bound is not callable"

);

var aArgs = Array.prototype.slice.call( arguments, 1 ),

fToBind = this,

fNOP = function(){},

fBound = function(){

return fToBind.apply(

this instanceof fNOP &&

oThis ? this : oThis

),

aArgs.concat( Array.prototype.slice.call( arguments ) )

);

fNOP.prototype = this.prototype;

fBound.prototype = new fNOP();

return fBound;

};

Nota: o polyfill de bind(..) mostrado acima difere do bind(..) nativo no ES5 com respeito à funções com hard binding que serão usadas
com new (veja abaixo porque isso é útil). Porque o polyfill não pode criar uma função sem um prototype como as funcionalidades nativas
fazem, há uma aproximação suavemente indireta para o mesmo comportamento. Tenha cuidado se você planeja usar new com uma função
hard binding e você confia nesse polyfill.
A parte que está permitindo a substituição do new é:

this instanceof fNOP &&

oThis ? this : oThis

// ... e:

fNOP.prototype = this.prototype;

fBound.prototype = new fNOP();

Nós não vamos realmente explicar como esse truque funciona (é complicado e além do nosso escopo aqui), mas essencialmente a utilidade
determina se a função hard binding foi chamada com new (resultando em um objeto recém construído sendo this ), e se assim for, ele usa
esse recém criado this ao invés do hard binding para o this .

Porque a capacidade do new de substituir o hard binding é útil?

A principal razão para este comportamento é criar uma função (que pode ser usada com new para construir objetos) que essencialmente
ignora o this com hard binding, mas que pré configura alguns ou todos os argumentos da função. Uma das capacidades do bind (..) é
que quaisquer argumentos passados ​após o primeiro argumento de binding do this são padronizados como argumentos padrão para a
função subjacente (tecnicamente chamado de "aplicação parcial", que é um subconjunto de "currying" -- Vide WORDREFERENCE).

Por exemplo:

function foo(p1,p2) {

this.val = p1 + p2;

// usando o `null` aqui porque não nos importamos

// com o `this` hard-binding nesse cenário, e isso

// será substituído pela chamada `new` de qualquer forma!

var bar = foo.bind( null, "p1" );

var baz = new bar( "p2" );

baz.val; // p1p2

Determinando o this
Agora, podemos resumir as regras para determinar o this a partir do local de chamada de uma função, na sua ordem de precedência. Faça
estas perguntas nesta ordem e pare quando a primeira regra se aplicar.

1. A função está sendo chamada com new (new binding)? Se sim, this é o objeto recém construído.

var bar = new foo()

2. A função é chamada com call ou apply (explicit binding), mesmo oculta dentro de um bind hard binding? Se sim, this é o objeto
especificado explicitamente.

var bar = foo.call( obj2 )

3. A função é chamada com um contexto (implicit binding), também conhecido como objeto proprietário ou contido? Se for assim, this
é aquele objeto de contexto.

var bar = obj1.foo()

4. Caso contrário, o padrão é this (default binding). Se em strict mode , escolha undefined , caso contrário escolha o objeto global .

var bar = foo()

É isso aí. Isso é tudo o que é preciso para entender as regras de binding do this para chamadas normais de funções. Bem... quase.

Exceções do Binding
Como de costume, há algumas exceções às "regras".
O comportamento do binding de this pode, em alguns cenários, ser surpreendente, onde você pretendia um binding diferente, mas acaba
tendo um comportamento de binding da regra de default binding (veja anteriormente).

Ignorando o this

Se você passar null ou undefined como um parâmetro de binding do this para call , apply ou bind , esses valores serão de fato
ignorados e, em vez disso, a regra default binding será aplicada à invocação.

function foo() {

console.log( this.a );

var a = 2;

foo.call( null ); // 2

Por que você iria intencionalmente passar algo como null para um binding this ?

É muito comum usar apply(..) para espalhar arrays de valores como parâmetros para uma chamada de função. Da mesma forma,
bind(..) pode arranjar parâmetros (valores pré-definidos), o que pode ser muito útil.

function foo(a,b) {

console.log( "a:" + a + ", b:" + b );

// spreading out array as parameters

foo.apply( null, [2, 3] ); // a:2, b:3

// currying with `bind(..)`

var bar = foo.bind( null, 2 );

bar( 3 ); // a:2, b:3

Ambas das utilidades requerem um binding this para o primeiro parâmetro. Se as funções em questão não se preocuparem com this ,
você precisará de um valor substituto, e null pode parecer uma escolha razoável, conforme mostrado neste trecho.

Nota: Não abordamos neste livro, mas o ES6 tem o operador ... spread que permite sintaticamente "espalhar" um array como parâmetros
sem precisar de apply(..) , tal como foo(... [1,2]) , que equivale a foo(1,2) - sintaticamente evitando um binding 'this' se for
desnecessário. Infelizmente, não há nenhum substituto sintático do ES6 para curry, então o parâmetro this da chamada bind(..) ainda
precisa de atenção.

Entretanto, existe um pequeno "perigo" oculto em sempre usar null quando você não se importa com o binding de this . Se você sempre
usa isso em uma chamada de função (por exemplo, uma função de biblioteca de terceiros que você não controla), e essa função faz uma
referência a this, a regra default binding significa que pode acidentalmente fazer referência (ou pior, mudar!) o objeto global ( window no
navegador).

Obviamente, essa armadilha pode levar a uma variedade de bugs muito difícil de diagnosticar/rastrear .

this Seguro

Talvez uma prática mais "segura" seja passar um objeto para this que garanta que este não seja um objeto que possa criar efeitos
colaterais em seu programa. Pegando emprestado a terminologia da área de redes (e da militar), nós podemos criar um objeto "DMZ" (de-
militarized zone / zona desmilitarizada) -- nada mais especial que um objeto completamente vazio, não delegado (veja no Capítulo 5).

Se nós sempre passarmos um objeto DMZ para bindings this ignorados, nós achamos que não precisamos nos preocupar, temos certeza
que qualquer uso oculto/inesperado de this estará restrito ao objeto vazio, o que isola o objeto global do nosso programa de efeitos
colaterais.

Já que esse objeto é totalmente vazio, eu pessoalmente gosto de dar um nome de variável ø à ele (o símbolo matemático para um
conjunto vazio). Em muitos teclados (como o US-layout no Mac), esse símbolo é facilmente digitado com ⌥ + o (option+ o ). Alguns
sistemas também deixam você definir teclas de atalho para símbolos específicos. Se você não gostar do símbolo ø , ou se seu teclado não o
torna fácil de digitá-lo, claro que você pode chamá-lo como quiser.

Seja do que for que você o chame, a melhor forma de configurá-lo como totalmente vazio é com Object.create(null) (veja o Capítulo 5).
Object.create(null) é similar à { } , mas sem a delegação do Object.prototype , então ele é ainda "mais vazio" do que apenas { } .
function foo(a,b) {

console.log( "a:" + a + ", b:" + b );

// nosso objeto vazio DMZ

var ø = Object.create( null );

// espalhando arrays como parâmetros

foo.apply( ø, [2, 3] ); // a:2, b:3

// currying com `bind(..)`

var bar = foo.bind( ø, 2 );

bar( 3 ); // a:2, b:3

Essa funcionalidade não é apenas "mais segura", há vários benefícios estéticos para ø na medida que se transmite semanticamente "eu
quero que o this seja vazio" mais claramente do que o null poderia. Mas, de novo, nomeie seu objeto DMZ da forma que você preferir.

Referências Indiretas
Outra coisa para ter cuidado é que você pode (intencionalmente ou não!) criar "referências indiretas" para funções, e nesses casos, quando a
referência dessa função é invocada, a regra de default binding também se aplica.

Uma das formas mais comuns pelas quais referências indiretas ocorrem é de uma atribuição:

function foo() {

console.log( this.a );

var a = 2;

var o = { a: 3, foo: foo };

var p = { a: 4 };

o.foo(); // 3

(p.foo = o.foo)(); // 2

O valor do resultado da expressão de atribuição p.foo = o.foo é uma referência apenas ao objeto de função adjacente. Como tal, o local
efetivo das chamadas é foo() , não p.foo() ou o.foo() como você pode ter esperado. De acordo com as regras acima, a regra de default
binding se aplica.

Lembrete: independentemente de como você chega à uma invocação de função usando a regra de default binding, o status strict mode do
conteúdo da função invocada fazendo a referência this -- não ao local de chamada da função -- determinar o valor do default binding: o
objeto global se estiver em modo não strict ou undefined se estiver em strict mode .

Soft Binding
Nós vimos anteriormente que hard binding era uma das estratégias para prevenir que uma chamada de função caia na regra de default
binding acidentalmente, forçando-a a ter o binding em um this específico (a menos que você use o new para substituí-lo!). O problema é,
o hard binding diminui muito a flexibilidade de uma função, prevenindo a substituição manual do this tanto com tentativas de binding
implícito ou até subsequentemente com o binding explícito.

Seria legal se houvesse uma forma de fornecer um padrão diferente para default binding (não global ou undefined ), enquanto
continuamos deixando a função capaz de fazer o binding do this manualmente pelas técnicas de binding implícito ou binding explícito.

Nós podemos construir a utilidade denominada soft binding que emula nosso comportamento desejado.

if (!Function.prototype.softBind) {

Function.prototype.softBind = function(obj) {

var fn = this,

curried = [].slice.call( arguments, 1 ),

bound = function bound() {

return fn.apply(

(!this ||

(typeof window !== "undefined" &&

this === window) ||

(typeof global !== "undefined" &&

this === global)

) ? obj : this,

curried.concat.apply( curried, arguments )

);

};

bound.prototype = Object.create( fn.prototype );

return bound;

};

A utilidade softBind(..) fornecida aqui funciona de forma parecida com a utilidade bind(..) nativa do ES5, exceto com nosso
comportamento de soft binding. Ela encapsula a função especificada na lógica que verifica o this no momento da chamada e se ele for
global ou undefined , usa uma alternativa default de ( obj ) pré-especificada. Caso contrário o this fica intocado. Ele também fornece
currying opcional (veja a discussão bind (..) antes).

Vamos demonstrar seu uso:

function foo() {

console.log("name: " + this.name);

var obj = { name: "obj" },

obj2 = { name: "obj2" },

obj3 = { name: "obj3" };

var fooOBJ = foo.softBind( obj );

fooOBJ(); // name: obj

obj2.foo = foo.softBind(obj);

obj2.foo(); // name: obj2 <---- olha!!!

fooOBJ.call( obj3 ); // name: obj3 <---- olha!

setTimeout( obj2.foo, 10 ); // name: obj <---- volta para o soft binding

A versão da função foo() que sofreu o soft binding pode fazer o bind em this manualmente para obj2 ou obj3 , como demonstrado,
mas cai de volta para obj se o default binding se aplicar.

this Léxico

Funções normais suportam as 4 regras que acabamos de abordar. Mas o ES6 introduz um tipo especial de função que não usa nenhuma
dessas regras: arrow-function.

Arrow-functions são identificadas não pela palavra-chave function , mas pelo operador => então chamado de "fat arrow". Em vez de usar
as quatro regras padrão para o this , as arrow-functions adotam o binding do this para o escopo ao redor (da função ou global).

Vamos ilustrar o escopo léxico da arrow function:

function foo() {

// retorna uma arrow function

return (a) => {

// `this` aqui é adotado léxicamente de `foo()`

console.log( this.a );

};

var obj1 = {

a: 2

};

var obj2 = {

a: 3

};

var bar = foo.call( obj1 );

bar.call( obj2 ); // 2, não 3!


A arrow function criada em foo() lexicamente captura tudo o que this de foo() tem em seu tempo de chamada. Já que foo() teve
binding de this para obj1 , bar (uma referência à arrow function retornada) também terá o binding this para obj1 . O binding léxico
de uma arrow function não pode ser sobrescrito (mesmo com new !).

O caso de uso mais comum provavelmente é o uso de callbacks, como os handlers de evento ou timers:

function foo() {

setTimeout(() => {

// `this` aqui é adotado léxicamente de `foo()`

console.log( this.a );

},100);

var obj = {

a: 2

};

foo.call( obj ); // 2

Enquanto arrow-functions fornecem uma alternativa ao uso de bind(..) em uma função para assegurar seu this , que pode parecer
atraente, é importante notar que elas estão essencialmente desativando o tradicional mecanismo this em favor de mais escopo léxico
amplamente compreendido. Antes do ES6, já temos um padrão bastante comum para isso, que é basicamente quase indistinguível da
essência das arrow-functions do ES6:

function foo() {

var self = this; // captura léxica de `this`

setTimeout( function(){

console.log( self.a );

}, 100 );

var obj = {

a: 2

};

foo.call( obj ); // 2

Embora self = this e arrow-functions pareçam ser boas "soluções" para não querer usar bind(...) , elas estão essencialmente fugindo
de this ao invés de entendê-lo e abraçá-lo.

Se você se encontrar escrevendo o código com this , mas na maior parte ou em todo o tempo, você derrota o mecanismo this com o
léxico self = this ou truques com arrow-functions, talvez você deveria:

1. Usar somente o escopo léxico e esqueçer a falsa pretensão do código com this .

2. Adotar os mecanismos de this completamente, incluindo o uso de bind(..) onde for necessário, e tentar evitar self = this e
truques de arrow-functions com "this léxico".

Um programa pode efetivamente usar ambos os estilos de código (léxico e this ), mas dentro da mesma função, e na verdade para os
mesmos tipos de abordagens, misturar os dois mecanismos geralmente é ter um código mais difícil de manter, e provavelmente
trabalhando muito para ser inteligente.

Revisão (TL;DR)
Determinar a ligação this para uma função em execução requer que se encontre o local de chamada direto dessa função. Uma vez
examinada, quatro regras podem ser aplicadas ao local de chamada, nesta ordem de precedência:

1. Chamada com new ? Use o objeto recém construído.

2. Chamada com call ou apply (ou bind )? Use o objeto especificado.

3. Chamada com um objeto do contexto que possui a chamada? Use esse objeto de contexto.

4. Padrão: undefined em strict mode , do contrário, objeto global.


Cuidado com a invocação acidental/involuntária da regra default binding. Nos casos em que você deseja "com segurança" ignorar o binding
de this , um objeto "DMZ" como ø = Object.create(null) é um bom placeholder que protege o objeto global de efeitos colaterais
indesejados.

Em vez das quatro regras de binding padrão, as arrow functions do ES6 usam o escopo léxico para o binding de this , o que significa que
adotam binding this (o que quer que seja) de sua chamada de função delimitadora. Eles são essencialmente uma substituição sintática de
self = this na codificação pré-ES6.
Capítulo 3: Objetos
Nos capítulos 1 e 2, nós explicamos como a ligação do this aponta para vários objetos dependendo de onde é feita a chamada da função.
Mas o que exatamente são objetos e por que nós precisamos salientá-los? Nós aprenderemos sobre objetos, em detalhes, nesse capítulo.

Sintaxe
Objetos possuem duas formas: a forma declarativa (literal) e a forma construída.

A sintaxe literal de um objeto se parece com isso:

var myObj = {

key: value

// ...

};

A forma construída se parece com isso:

var myObj = new Object();

myObj.key = value;

A forma construída e a forma literal resultam exatamente no mesmo tipo de objeto. A única diferença é que você pode adicionar um ou
mais pares chave/valor na declaração literal, enquanto que com objetos de forma construída, você tem que adicionar as propriedades uma
por uma.

Lembrete: É extremamente raro o uso da "forma construída" para a criação de objetos como foi mostrado. Você praticamente sempre irá
preferir usar a sintaxe da forma literal. O mesmo acontece com a maioria dos objetos nativos (veja abaixo).

Tipo
Objetos são o bloco de construção geral no qual muito do JS é construído. Eles são um do 6 tipos primários (chamados "tipos de
linguagem" na especificação) em JS:

string

number

boolean

null

undefined

object

Note que os primitivos simples ( string , number , boolean , null , e undefined ) não são por si só objetcs . null é algumas vezes referido
como um tipo de objeto, mas esse equívoco surge a partir de um bug na linguagem que faz com que typeof null retorne a string
"object" incorretamente (e de modo confuso). De fato, null é o seu próprio tipo primitivo.

Há uma frequente distorção de que "Tudo em JavaScript é um objeto. Isso claramente não é verdade".

Por outro lado, há alguns subtipos de objeto especiais, os quais podemos referir como primtivos complexos

function é um subtipo de objeto (tecnicamente, um "objeto que pode ser chamado"). Funções em JS são consideradas como "primeira
classe" que são basicamente objetos normais (com a adição de semântica de comportamento de algo que pode ser chamado), e então elas
podem ser manipuladas como qualquer outro objeto simples.

Arrays também são uma forma de objetos, com comportamento extra. A organização de conteúdos nos arrays é uma pouco mais
estruturada do que em objetos gerais.

Objetos nativos
Existem diversos outros subtipos de objetos, normalmente referidos como objetos nativos. Para alguns deles, seus nomes parecem sugerir
que eles estão diretamente relacionados, em contrapartida, a seus primitivos simples, mas na verdade, o relacionamento deles é mais
complicado, o qual iremos explorar em breve.

String

Number

Boolean

Object

Function

Array

Date

RegExp

Error

Esses nativos aparentam ser tipos reais, até mesmo classes, se você considerar a similaridade com outras linguagens como a classe String
do Java.

Mas no JS existem, na verdade, apenas funções nativas. Cada uma dessas funções nativas pode ser usada como um construtor (que é uma
chamada de função com o operador new -- veja o Capítulo 2), com o resultado sendo novamente um objeto construído do subtipo em
questão.

Por exemplo:

var strPrimitive = "I am a string";

typeof strPrimitive; // "string"

strPrimitive instanceof String; // false

var strObject = new String( "I am a string" );

typeof strObject; // "object"

strObject instanceof String; // true

// inspeciona o subtipo de 'object'

Object.prototype.toString.call( strObject ); // [object String]

Nós veremos, em detalhes, mais adiante nesse capítulo exatamente como o trecho Object.prototype.toString... funciona, mas
brevemente podemos inspecionar o interior o subtipo pegando emprestado o padrão base do método toString() e você pode ver que ele
revela que strObject é um objeto que foi de fato criado pelo construtor de String .

O valor primitivo "I am a string" não é um objeto, mas sim um primitivo literal e valor imutável. Para realizar operações com ele, tais
como checar seu comprimento, acessar o conteúdo de caracteres individuais etc, um objeto String é necessário.

Por sorte, a linguagem automaticamente converte um primitivo "string" para um objeto String quando necessário, o que significa que
você quase nunca precisa explicitamente criar a forma de objeto. É fortemente preferível pela maior parte da comunidade de JS usar o
forma literal para um valor, quando possível, em vez da forma de objeto construído.

Considere:

var strPrimitive = "I am a string";

console.log( strPrimitive.length ); // 13

console.log( strPrimitive.charAt( 3 ) ); // "m"

Em ambos os casos, podemos chamar uma propriedade ou método de uma string primitiva, e o Motor JS automaticamente converte a
mesma para um objeto String para que o acesso a propriedade/método funcione.

O mesmo tipo de comportamento acontece entre o número primitivo literal 42 e o invólucro do objeto new Number(42) para o uso de
métodos como 42.359.toFixed(2) . Da mesma maneira para objetos Boolean de primitivos "boolean" .

null e undefined não tem formato de invólucro de objeto, apenas valores primitivos. De maneira oposta, valores de Date só podem ser
criados a partir da forma de objetos construídos, uma vez que eles não possuem uma forma literal.
Object s, Array s, Function s, e RegExp s (expressões regulares) são todos objetos, independente se a forma literal ou construída é usada.
A forma construída oferece, em alguns casos, mais opções na criação do que a forma literal. Uma vez que objetos são criados das duas
formas, a forma literal (mais simples) é quase que universalmente preferida. Apenas use a forma construída se você precisar de opções
extras.

Objetos Error são raramente criados explicitamente em código, mas geralmente são criados automaticamente quando exceções são
lançadas. Eles pode ser criados com a forma de instanciação new Error(..) , mas muitas vezes é desnecessário.

Conteúdos
Como mencionamos anteriormente, o conteúdo de um objeto consiste de valores (qualquer tipo) armazenados em locais específicos, o qual
chamamos de propriedades.

É importante notar que quando dizemos "conteúdos" implica que esses valores são na verdade armazenados dentro do objeto, que é
meramente uma aparência. O motor JS armazena valores de maneiras dependentes de implementação e pode muito bem armazená-los em
algum container de objeto. O que é armazenado no container são nomes de propriedade, que funcionam como ponteiros (tecnicamente,
referências) para onde os valores são armazenados.

Considere:

var myObject = {

a: 2

};

myObject.a; // 2

myObject["a"]; // 2

Para acessar o valor no local a em myObject , precisamos usar o operador . ou o operator [ ] . A sintaxe .a normalmente se refere ao
acesso à "propriedade", enquando que a sintaxe ["a"] é geralmente referente ao acesso a "chave". Na realidade, as duas formas acessam o
mesmo local, e retornarão o mesmo valor, 2 , então os termos podem ser usados indiferetemente. Usaremos o termo mais comum, "acesso
à propriedade" de agora em diante.

A principal diferença entre as duas sintaxes é que o operador . requer um nome de propriedade Identifier compatível logo após ele,
enquanto a sintaxe [".."] pode aceitar basicamente qualquer com string compatível com UTF-8/unicode como o nome da propriedade.
Para referenciar uma propriedade de nome Super-Fun! , por exemplo, você teria que usar o sintaxe de acesso ["Super-Fun!"] , pois Super-
Fun! não é um nome de propriedade Identifier válido.

Além disso, um vez que a sintaxe [".."] usa valor de string para especificar a localização, significa que o programa pode,
programaticamente, construir um valor de string, tal como:

var wantA = true;

var myObject = {

a: 2

};

var idx;

if (wantA) {

idx = "a";

// depois

console.log( myObject[idx] ); // 2

Em objetos, os nomes de propriedade são sempre strings. Se você usa qualquer outro valor além de um string (primitivo) como
propriedade, ele será convertido para string primeiro. Isso inclue até mesmo números, que são normalmente usados como índices de array,
então tenha cuidado para não confundir o uso de números entre objetos e arrays.

var myObject = { };

myObject[true] = "foo";

myObject[3] = "bar";

myObject[myObject] = "baz";

myObject["true"]; // "foo"

myObject["3"]; // "bar"

myObject["[object Object]"]; // "baz"

Nomes de propriedade computados


A sintaxe de acesso às propriedades de myObject[..] que acabamos de descrever é útil se você precisar usar um valor de expressão
computado como o nome da chave, tal como myObject[prefix + name] . Mas não é realmente útil quando se declara objetos usando a
sintaxe de objeto-literal.

ES6 adiciona nomes de propriedade computados, onde você pode especificar uma expressão, cercado por um par de [ ] , na posição do
nome-da-chave de uma declaração de objeto-literal.

var prefix = "foo";

var myObject = {

[prefix + "bar"]: "hello",

[prefix + "baz"]: "world"

};

myObject["foobar"]; // hello

myObject["foobaz"]; // world

O uso mais comum de nomes de propriedade computados serão provavelmente com Símbolo no ES6, o qual não abordaremos em detalhes
nesse livro. Em resumo, eles são um novo tipo de dado primitivo que tem um valor imprevisível (tecnicamente um valor de tipo string ).
Você será fortemente desencorajado a trabalhar com o valor real de um Symbol (que pode, teoricamente, ser diferente entre diferentes
motores JS), portanto o nome do Symbol , como Symbol.Something (apenas um nome inventado!), será o que você vai usar:

var myObject = {

[Symbol.Something]: "hello world"

};

Propriedade vs. Método


Alguns desenvolvedores gostam de fazer uma distinção em relação ao acesso de propriedade de um objeto, se o valor acessado for uma
função. Porque é tentador pensar em uma função como sendo pertencente a um objeto e, em outras linguagens, funções que pertencem a
objetos (conhecidos como "classes") são referidos como "métodos", não é incomum ouvir "acesso de método" como o oposto ao "acesso
de propriedade".

A especificação faz a mesma distinção, curiosamente.

Tecnicamente, funções nunca "pertencem" a objetos, então falando que uma função que apenas foi acessada em uma referência de objeto é
automaticamente um "método" que parece um pouco com uma extensão de semântica.

É verdade que algumas funções tem referências this , e que às vezes essas referências this se referem a uma referência de objeto no call-
site. Mas esse uso realmente não faz uma função ("método") ter algo mais que qualquer outra função, pois o this é vinculado
dinâmicamente em tempo de execução, no call-site, e assim seu relacionamento com o objeto é indireto, na melhor das hipóteses.

Toda vez que você acessa uma propriedade de um objeto, isso é considerado um acesso à propriedade, independente do tipo do valor que
é retornado. Se por acaso você obter uma função a partir do acesso à propriedade, ela não é magicamente um "método" nesse momento.
Não há nada especial (fora o possível bind implícito do this como foi explicado anteriormente) em uma função que é obtida a partir de
um acesso à propriedade.

Por exemplo:

function foo() {

console.log( "foo" );

var someFoo = foo; // variável que referencia `foo`

var myObject = {

someFoo: foo

};

foo; // função foo(){..}

someFoo; // função foo(){..}

myObject.someFoo; // função foo(){..}

someFoo e myObject.someFoo são apenas duas referências separadas para a mesma função, e nenhuma das duas implica em algo sobre a
função ser especial ou "pertencente" a outro objeto. Se foo() acima foi definido tendo uma referência this , o myObject.someFoo binding
implícito será a única diferença observável entre as duas referências. Nenhuma das referências realmente faz sentido em ser chamada de
"método".

Talvez alguém pudesse argumentar que uma função se torna um método não no momento de sua definição, mas durante tempo de
execução, dependendo de como é chamado em seu call-site (com um contexto de referência de objeto ou não -- veja Capítulo 2 para mais
detalhes). Mesmo assim essa interpretação é pouco provável.

Provavelmente, a conclusão mais segura é que "função" e "método" são permutáveis em JavaScript.

Nota: ES6 adiciona uma referência super , que é tipicamente usada com class (veja Apêndice A). A maneira que super se comporta
(binding estático em vez de binding tardio como this ) proporciona mais peso na ideia que uma função que é vinculada ao super em
algum lugar é mais "método" que "função". Mas mais uma vez, são apenas detalhes sutis de semântica (e mecânica).

Mesmo quando você declara uma expressão de função como parte do objeto-literal, a função não pertence magicamente ao objeto --
apenas várias referências ao mesmo objeto de função.

var myObject = {

foo: function foo() {

console.log( "foo" );

};

var someFoo = myObject.foo;

someFoo; // function foo(){..}

myObject.foo; // function foo(){..}

Nota: No capítulo 6, nós veremos uma forma abreviada no ES6 para a sintaxe de declaração de foo: function foo(){ .. } em nosso
objeto-literal.

Arrays
Arrays também usam a forma de acesso [ ] , mas como mencionado acima, eles tem uma organização um pouco mais estruturada com
relação a como e onde valores são armazenados (ainda que não tenha restrição de tipo de valores que são armazenados). Arrays assumem
indexação numérica, onde valores são armazenados em locais, normalmente chamados de índices, em inteiros não-negativos, tal como 0 e
42 .

var myArray = [ "foo", 42, "bar" ];

myArray.length; // 3

myArray[0]; // "foo"

myArray[2]; // "bar"

Arrays são objetos, então mesmo que cada índice seja um inteiro positivo, você também pode adicionar propriedades no array.

var myArray = [ "foo", 42, "bar" ];

myArray.baz = "baz";

myArray.length; // 3

myArray.baz; // "baz"
Note que adicionando propriedades nomeadas (independente da sintaxe do operador . ou [ ] ) não muda o length informado do array.

Você poderia usar um array como um objeto chave/valor simples, e nunca adicionar qualquer índice numérico, mas essa é uma má ideia
porque arrays tem um comportamento e otimizações específicas para o uso que eles são destinados, e da mesma forma acontence com
objetos simples. Use objetos para armazenar pares de chave/valor e arrays para armazenar em índices numéricos.

Tenha cuidado: Se você tentar adicionar uma propriedade em um array, mas o nome da propriedade parecer um número, pode acontecer
da mesma ser interpretada como um índice numérico (consequentemente modificando o conteúdo do array):

var myArray = [ "foo", 42, "bar" ];

myArray["3"] = "baz";

myArray.length; // 4

myArray[3]; // "baz"

Duplicando Objetos
Um dos recursos mais requisitados frequentemente por novos desenvolvedores JavaScript é como duplicar um objeto. Parece como se
devesse existir um método nativo copy() , certo? Acontece que é um pouco mais complicado que isso, porque não é totalmente claro, por
padrão, qual deveria ser o algoritmo para a duplicação.

Por exemplo, considere esse objeto:

function anotherFunction() { /*..*/ }

var anotherObject = {

c: true

};

var anotherArray = [];

var myObject = {

a: 2,

b: anotherObject, // referência, não uma cópia!

c: anotherArray, // outra referência!

d: anotherFunction

};

anotherArray.push( anotherObject, myObject );

O que exatamente deveria ser a representação de um copy de myObject ?

Primeiramente, nós devemos responder se deveria ser uma cópia rasa ou profunda? Uma cópia rasa terminaria com a no novo objeto
como uma cópia do valor 2 , mas as propriedades b , c , e d são apenas referências para o objeto original. Uma cópia profunda
duplicaria não apenas myObject , mas anotherObject e anotherArray . Mas temos problemas nos quais anotherArray possue referências
para anotherObject e myObject , então esses também devem ser duplicados em vez de preservarem referências. Agora temos um problema
de duplicação circular infinita por causa da referência circular.

Devemos detectar uma referência circular e apenas quebrar a transversal circular (deixando o elemento profundo não completamente
duplicado)?

Além disso, não fica realmente claro o que "duplicar" uma função significaria? Existem alguns hacks como retirar a serialização do
toString() do código fonte de uma função (que varia entre diferentes implementações e nem é confiável em todos os motores JS,
dependendo do tipo de função que está sendo inspecionada).

Então como resolvemos todas essas difíceis questões? Vários frameworks JS possuem suas próprias interpretações e decisões. Mas qual
delas (se existe alguma) o JS deveria adotar como o padrão? Durante um longo tempo, não havia uma resposta clara.

Uma parte da solução é que objetos que são JSON-safe (que é, pode ser serializado para uma string JSON e depois re-transformada em um
objeto com a mesma estrutura e valores) podem facilmente ser duplicados com:

var newObj = JSON.parse( JSON.stringify( someObj ) );


É claro que requer que você assegure que seu objeto é JSON-safe. Em algumas situações, é trivial. Em outras, é insuficiente.

Ao mesmo tempo, uma cópia rasa é completamente compreensível e tem muito menos problemas, então ES6 agora definiu
Object.assign(..) para essa tarefa. Object.assign(..) seleciona um objeto alvo como primeiro parâmetro e um ou mais objetos fonte
como parâmetros subsequentes. Ele itera sobre todos os enumerable (veja abaixo), chaves de propriedade (imediatamente presente) no(s)
objeto(s) fonte e copia eles para um alvo. Ele também retorna o alvo, como pode ver abaixo:

var newObj = Object.assign( {}, myObject );

newObj.a; // 2

newObj.b === anotherObject; // true

newObj.c === anotherArray; // true

newObj.d === anotherFunction; // true

Lembrete: Na próxima seção, descrevemos "descritores de propriedade" (características de propriedade) e mostramos o uso de
Object.defineProperty(..) . Entretanto, a duplicação que ocorre com Object.assign(..) é puramente atribuição de estilo = , então
qualquer característica especial de uma propriedade (como writable ) em um objeto de origem não são preservadas em um objeto de
destino.

Descritores de propriedade
Antes do ES5, a linguagem JavaScript não dava nenhuma forma direta no seu código para inspecionar ou obter qualquer distinção entre as
características de propriedades, tais como se a propriedade era somente-leitura ou não.

Mas como no ES5, todas as propriedades são descritas em termos de um descritor de propriedade.

Considere esse código:

var myObject = {

a: 2

};

Object.getOwnPropertyDescriptor( myObject, "a" );

// {

// value: 2,

// writable: true,

// enumerable: true,

// configurable: true

// }

Como pode ver, o descritor da propriedade (chamado de "descritor de dados", um vez que ele apenas guarda um valor de dado) para nossa
propriedade de objeto normal a é muito mais que apenas o valor de 2 . Ele inclue outras 3 características: writable , enumerable , and
configurable .

Enquanto nós podemos ver o que os valores padrão para as características do descritor de propriedade são quando criamos uma
propriedade normal, nós podemos usar Object.defineProperty(..) para adicionar uma nova propriedade ou modificar uma já existente (se
ela for configurable !), com as características desejadas.

Por exemplo:

var myObject = {};

Object.defineProperty( myObject, "a", {

value: 2,

writable: true,

configurable: true,

enumerable: true

} );

myObject.a; // 2

Usando defineProperty(..) , adicionamos uma simples propriedade a ao myObject de maneira explícita manualmente. Entretanto,
geralmente não usaríamos essa forma manual a menos que quiséssemos modificar uma das características do descritor a partir de seu
comportamento normal.
Gravável

A habilidade de você mudar o valor de uma propriedade é controlada por writable .

Considere:

var myObject = {};

Object.defineProperty( myObject, "a", {

value: 2,

writable: false, // não gravável!

configurable: true,

enumerable: true

} );

myObject.a = 3;

myObject.a; // 2

Como pode ver, nossa modificação do value falhou silenciosamente. Se tentarmos em strict mode , obtemos um erro:

"use strict";

var myObject = {};

Object.defineProperty( myObject, "a", {

value: 2,

writable: false, // não gravável!

configurable: true,

enumerable: true

} );

myObject.a = 3; // TypeError

O TypeError diz que não podemos mudar uma propriedade não gravável.

Lembrete: Nós discutiremos getters/setters mais adiante, mas resumidamente, você pode observar que writable:false significa um valor
que não pode ser alterado, equivalente um pouco no caso de quando uma operação de setter não é definida. Na verdade, a não-operação
de setter precisaria lançar um TypeError quando fosse chamada, para se comportar de maneira similar ao writable:false .

Configurável

Contanto que uma propriedade seja configurável, podemos modificar sua definição de descritor usando o mesmo método
defineProperty(..) .

var myObject = {

a: 2

};

myObject.a = 3;

myObject.a; // 3

Object.defineProperty( myObject, "a", {

value: 4,

writable: true,

configurable: false, // não configurável!

enumerable: true

} );

myObject.a; // 4

myObject.a = 5;

myObject.a; // 5

Object.defineProperty( myObject, "a", {

value: 6,

writable: true,

configurable: true,

enumerable: true

} ); // TypeError
A última chamada de defineProperty(..) resulta em um TypeError, independente do strict mode , se você tentar mudar a definição do
descritor de uma propriedade não configurável. Cuidado: como você pode ver, alterando configurable para false é uma ação de via
única, e não pode ser desfeita!

Nota: Há uma exceção diferenciada para estar ciente: mesmo se a propriedade já está com configurable:false , writable sempre pode ser
alterada de true para false sem erro, mas não o contrário, de false para true .

Outra coisa que configurable:false previne é a habilidade de usar o operador delete para remover uma propriedade existente.

var myObject = {

a: 2

};

myObject.a; // 2

delete myObject.a;

myObject.a; // undefined

Object.defineProperty( myObject, "a", {

value: 2,

writable: true,

configurable: false,

enumerable: true

} );

myObject.a; // 2

delete myObject.a;

myObject.a; // 2

Como pode ver, a última chamada de delete falhou (silenciosamente) porque definimos a propriedade a como não-configurável.

delete não é apenas usado para remover propriedades (que podem ser removidas) de objeto. Se uma propriedade de objeto é a última
referência que resta para algum objeto/função, e você aplica delete nela, isso remove a referência e faz com que o objeto/função sem
referência possa ser alocado para garbage collection. Mas, não é adequado pensar no delete como sendo uma ferramenta para liberar
memória alocada, como acontece em outras linguagens (a exemplo de C/C++). delete é apenas uma operação de remoção de
propriedade de objeto -- nada mais.

Enumerável

A última característica de descritor que vamos mencionar aqui (existem outras duas, que veremos em breve quando discutirmos
getter/setters) é enumerable .

O nome provavelmente é óbvio, mas essa característica controla se uma propriedade aparecerá em certas enumerações objeto-propriedade,
tal como o laço for..in . Configure para false para não aparecer em tais enumerações, mesmo que ainda esteja completamente
acessível. Configure true para mantê-lo presente.

Todas as propriedades normais definidas pelo usuário são padronizadas para enumerable , visto que isso é que você geralmente deseja. Mas
se você tem uma propriedade especial que queira ocultar da enumeração, configure-a para enumerable:false .

Iremos demonstrar enumerabilidade em mais detalhes mais adiante, então lembre-se desse tópico.

Imutabilidade

Às vezes é desejável fazer com que propriedades ou objetos não possam ser alterados (por acidente ou intencionalmente). ES5 adiciona
suporte para lidar com isso através de diferentes formas.

É importante notar que todas essas abordagens criam imutabilidade rasa. O que significa que afeta apenas o objeto e suas características de
propriedade direta. Se um objeto tem uma referência para outro objeto (array, objeto, função, etc), o conteúdo desse objeto não é afetado e
permanece mutável.

myImmutableObject.foo; // [1,2,3]

myImmutableObject.foo.push( 4 );

myImmutableObject.foo; // [1,2,3,4]
Nós assumimos nesse trecho de código que myImmutableObject já é criado e protegido como imutável. Mas, para proteger também o
conteúdo de myImmutableObject.foo (que é seu próprio objeto -- array), será necessário fazer com que foo seja imutável também, usando
um ou mais das seguintes funcionalidades.

Nota: Não é extremamente comum criar, em programas JS, objetos imutáveis profundamente enraizados. Casos especiais podem
certamente existir, mas tendo como um padrão de projeto geral, se você quiser usar seal ou freeze em todos os seus objetos, você pode
voltar e reconsiderar o design de seu programa visando uma maior resistência a possíveis mudanças nos valores dos objetos.

Constante de Objeto
Combinando writable:false e configurable:false , você pode essencialmente criar uma constante (não pode ser alterada, redefinida ou
removida) como uma propriedade de objeto, como:

var myObject = {};

Object.defineProperty( myObject, "FAVORITE_NUMBER", {

value: 42,

writable: false,

configurable: false

} );

Prevenir Extensões
Se você quiser prevenir que um objeto tenha novas propriedades adicionadas a ele, mantendo apenas o resto das propriedades do objeto,
chame Object.preventExtensions(..) :

var myObject = {

a: 2

};

Object.preventExtensions( myObject );

myObject.b = 3;

myObject.b; // undefined

No non-strict mode a criação de b falha silenciosamente. No strict mode é lançado um TypeError .

Seal

Object.seal(..) cria um objeto "selado", que pega um objeto existente e essencialmente chama Object.preventExtensions(..) , mas
também marca todas as propriedades existentes como configurable:false .

Então, além de você não poder adicionar mais propriedades, também não pode reconfigurar ou deletar qualquer propriedade existente
(embora ainda possa modificar seu valor).

Freeze

Object.freeze(..) cria um objeto "congelado", que pega um objeto existente e essencialmente chama Object.seal(..) , mas também
marca todas as propriedades "acessores de dados" como writable:false , com isso os valores das propriedades não podem ser alteradas.

Essa abordagem é o nível mais alto de imutabilidade para uma objeto que você pode atingir, visto que previne qualquer mudança no objeto
ou em qualquer de suas propriedades diretas (embora, como mencionado acima, o conteúdo de qualquer objeto referenciado não é
afetado).

Você poderia "deep freeze" um objeto através do Object.freeze(..) , e então iterar recursivamente sobre todos os objetos que dado
objeto referencia (que teriam sido afetados até agora), e chamando Object.freeze(..) para eles também. Cuidado, embora isso possa
afetar outros objetos (compartilhados) você não tem intenção de afetá-los.

[[Get]]

Há um pequeno, mas importante, detalhe sobre como os acessos às propriedades são realizados.

Considere:
var myObject = {

a: 2

};

myObject.a; // 2

O myObject.a é um acesso à propriedade, mas não é apenas procurar em myObject por uma propriedade de nome a , como pode
parecer.

De acordo com a especificação, o código acima na verdade realiza uma operação [[Get]] (tipo uma chamada de função: [[Get]]() ) no
myObject . A operação nativa padrão [[Get]] de um objeto inspeciona primeiro o objeto à procura de uma propriedade com nome
solicitado, se encontrar, retornará o respectivo valor.

Entretanto, o algoritmo de [[Get]] define outro importante comportamento caso não encontre a propriedade com nome solicitado.
Examinaremos no Capítulo 5 o que acontece em seguida (passagem pela cadeia de [[Prototype]] , caso houver).

Mas um importante resultado dessa operação [[Get]] é que se a propriedade solicitada não for encontrada, o valor undefined é
retornado.

var myObject = {

a: 2

};

myObject.b; // undefined

Esse comportamento é diferente de quando você referencia variáveis através de seus nomes de identificadores. Se você referencia uma
variável que não pode ser resolvida no escopo léxico aplicável, o resultado não é undefined como é para propriedades de objeto, mas em
vez disso um ReferenceError é lançado.

var myObject = {

a: undefined

};

myObject.a; // undefined

myObject.b; // undefined

Do ponto de vista de valor, não há diferença entre essas duas referências -- As duas resultam em undefined . Entretanto, por baixo, a
operação [[Get]] , à primeira vista, realizou uma pouco mais de "trabalho" para a referência myObject.b do que para a referência
myObject.a .

Analisando apenas os resultados do valor, você não consegue diferenciar se uma propriedade existe e mantém um valor explícito
undefined ou se a propriedade não existe e undefined foi o valor padrão retornado após [[Get]] falhar em retornar algo explicitamente.
Contudo, nós veremos em breve como podemos diferenciar esses dois cenários.

[[Put]]

Uma vez que há uma operação [[Get]] definida internamente para obter o valor de uma propriedade, deveria ser óbvio que há também
uma operação [[Put]] padrão.

Pode ser tentador pensar que a atribuição de uma propriedade de um objeto iria apenas invocar [[Put]] para definir ou criar a
propriedade do objeto em questão. Mas esse caso possui algumas especificidades a mais.

Quando invocamos [[Put]] , o modo como ele se comporta muda baseado em alguns fatores, incluindo (o mais impactante) se a
propriedade já está presente no objeto ou não.

Se a propriedade estiver presente, o algoritmo de [[Put]] irá checar grosseiramente:

1. A propriedade é um descritor de acessor? (veja a seção abaixo sobre "Getters & Setters") Se sim, chame o setter, caso exista um.
2. A propriedade é um descritor de dado com writable definido como false ? Se sim, falha silenciosamente no non-strict mode ou
lança TypeError no strict mode .
3. Caso contrário, define o valor para a propriedade existente normalmente.
Se a propriedade ainda não estiver presente no objeto em questão, a operação [[Put]] é ainda mais diferenciada e complexa. Nós iremos
rever esse caso com mais clareza no Capítulo 5 quando discutirmos [[Prototype]] .

Getters & Setters


As operações padrão [[Put]] e [[Get]] de objetos controlam como valores são definidos em propriedades novas ou já existentes, ou
obtidos de propriedades existentes, respectivamente.

Nota: Utilizando futuros/avançados recursos da linguagem, pode ser possível sobrescrever as operações padrão [[Get]] ou [[Put]] para
um objeto inteiro (não apenas por propriedade). Isso está além do escopo da nossa discussão nesse livro, mas será abordado depois nas
séries "You Don't Know JS".

ES5 introduziu uma forma de sobrescrever parte dessas operações, não no nível de objeto mas no nível por-propriedade, através do uso de
getters e setters. Getters são propriedades que, na verdade, chamam uma função oculta para recuperar um valor. Setters são propriedades
que chamam uma função oculta para estabelecer um valor.

Quando você define uma propriedade para ter um getter ou um setter ou ambos, a definição da propriedade se torna um "descritor de
acessor" (o oposto de um "descritor de dados"). Para descritores de acessor, as características da propriedade de value e writable são
discutíveis e ignoradas, em vez disso, o JS considera as características da propriedade de set e get (como também configurable e
enumerable ).

Considere:

var myObject = {

// define um getter para `a`

get a() {

return 2;

};

Object.defineProperty(

myObject, // alvo

"b", // nome da propriedade

{ // descritor

// define um getter para `b`

get: function(){ return this.a * 2 },

// certifica que `b` aparece como uma propriedade do objeto

enumerable: true

);

myObject.a; // 2

myObject.b; // 4

Através da sintaxe de objeto-literal com get a() { .. } ou por definição explícita com defineProperty(..) , nos dois casos nós criamos
uma propriedade do objeto que realmente não mantém um valor, mas o acesso à propriedade resulta automaticamente em uma chamada
de função oculta a uma função getter, com qualquer valor que ela retorna sendo o resultado do acesso à propriedade.

var myObject = {

// define um getter para `a`

get a() {

return 2;

};

myObject.a = 3;

myObject.a; // 2

Desde que nós apenas definimos um getter para a , se tentarmos definir o valor de a depois, a operação de set não lançará um erro, mas
irá descartar a atribuição silenciosamente. Mesmo se houvesse um setter válido, nosso getter personalizado é hard-coded para retornar
apenas 2 , logo a operação de set seria discutível.
Para deixar esse cenário mais sensível, propriedades deveriam ser definidas com setters também, que sobrescrevem a operação [[Put]]
padrão (conhecida como atribuição), por-propriedade, apenas como esperaríamos. Você provavelmente vai sempre querer declarar tanto
getter como setter (ter apenas um ou outro geralmente leva a um comportamento inesperado/espantoso):

var myObject = {

// define um getter para `a`

get a() {

return this._a_;

},

// define um setter para `a`

set a(val) {

this._a_ = val * 2;

};

myObject.a = 2;

myObject.a; // 4

Nota: Nesse exemplo, nós armazenamos um específico valor 2 da atribuição ( [[Put]] operation) em outra variável _a_ . O nome _a_ é
uma mera convenção para esse exemplo e não implica em nada especial em relação a comportamento -- é uma propriedade normal assim
como qualquer outra.

Existência
Nós mostramos anteriormente que o acesso à propriedade como myObject.a pode resultar em um valor undefined caso o undefined for
armazenado explicitamente na propriedade ou a propriedade a não existir de forma alguma. Logo, se o valor é o mesmo em ambos os
casos, como é que diferenciamos?

Podemos perguntar a um objeto se ele possui certa propriedade sem pedir para obter o valor da propriedade:

var myObject = {

a: 2

};

("a" in myObject); // true

("b" in myObject); // false

myObject.hasOwnProperty( "a" ); // true

myObject.hasOwnProperty( "b" ); // false

O operador in verificará se a propriedade está no objeto ou se ela existe em algum nível mais alto da cadeia de [[Prototype]] do objeto
(veja Capítulo 5). Em contraste ao in , hasOwnProperty(..) verifica apenas se myObject tem a propriedade ou não, e não consultará a
cadeia de [[Prototype]] . Nós voltaremos a discutir importantes diferenças entre essas duas operações no Capítulo 5, onde examinamos
[[Prototype]] s em detalhes.

hasOwnProperty(..) é acessível para todos os objetos normais via delegação ao Object.prototype (veja o Capítulo 5). Mas é possível criar
um objeto que seja ligado ao Object.prototype (via Object.create(null) ) -- (veja o Capítulo 5). Nesse caso, uma chamada de método
como myObject.hasOwnProperty(..) falharia.

Nesse cenário, um modo mais robusto de realizar tal verificação é Object.prototype.hasOwnProperty.call(myObject,"a") , que toma
emprestado o método base hasOwnProperty(..) e usa um binding explícito de this (veja o Capítulo 2) para aplicar ao nosso myObject .

Nota: O operador in aparenta que verificará a existência de um valor dentro de um container, mas na verdade ele verifica a existência de
um nome de propriedade. Essa diferença é importante de se notar no que se diz respeito a arrays, onde há uma forte tentação de verificar
algo como 4 in [2, 4, 6] , mas isso não se comportará da maneira esperada.

Enumeração

Anteriormente, explicamos brevemente a ideia de "enumerabilidade" quando olhamos para a característica do descritor de propriedade de
enumerable . Vamos rever e examinar isso mais detalhadamente.

var myObject = { };

Object.defineProperty(

myObject,

"a",

// torne `a` enumarável normalmente

{ enumerable: true, value: 2 }

);

Object.defineProperty(

myObject,

"b",

// torne `b` não-enumerável

{ enumerable: false, value: 3 }

);

myObject.b; // 3

("b" in myObject); // true

myObject.hasOwnProperty( "b" ); // true

// .......

for (var k in myObject) {

console.log( k, myObject[k] );
}

// "a" 2

Você vai perceber que myObject.b de fato existe e tem um valor acessível, mas não aparece no laço for..in (embora,
surpreendentemente, ele seja mostrado pelo operador de in que verifica se a propriedade existe). Isso acontece porque "enumerable"
basicamente significa "será incluído se for possível iterar sobre as propriedades do objeto".

Lembrete: Laços for..in aplicados a arrays podem gerar resultados inesperados, nisso a enumeração de um array incluirá não apenas
todos os índices numéricos, mas também qualquer propriedade enumerável. É uma boa ideia usar laços for..in apenas em objetos e laços
tradicionais for com iteração com índices numéricos para os valores armazenados em arrays.

Outra maneira que propriedades enumeráveis e não-enumeráveis podem ser distinguidas:

var myObject = { };

Object.defineProperty(

myObject,

"a",

// Faz com que `a` seja enumerável

{ enumerable: true, value: 2 }

);

Object.defineProperty(

myObject,

"b",

// Faz com que `b` seja não-enumerável

{ enumerable: false, value: 3 }

);

myObject.propertyIsEnumerable( "a" ); // true

myObject.propertyIsEnumerable( "b" ); // false

Object.keys( myObject ); // ["a"]

Object.getOwnPropertyNames( myObject ); // ["a", "b"]

propertyIsEnumerable(..) testa se dado nome de propriedade existe diretamente em um objeto e também se é enumerable:true .

Object.keys(..) retorna um array de todas propriedades enumeráveis, enquanto que Object.getOwnPropertyNames(..) retorna um array
de todas as propriedades, enumerável ou não.

Enquanto que in vs. hasOwnProperty(..) divergem com relação se eles consultam a cadeia de [[Prototype]] ou não, Object.keys(..) e
Object.getOwnPropertyNames(..) inspecionam apenas o objeto direto especificado.

Atualmente, não há uma maneira nativa de obter uma lista de todas as propriedades equivalente ao que o operator in faz (percorrendo
todas as propriedades de toda a cadeia de [[Prototype]] , como foi explicado no Capítulo 5). Você poderia ter um recurso parecido
percorrendo recursivamente a cadeia de [[Prototype]] de um objeto e, para cada nível, capturar a lista de propriedades de
Object.keys(..) -- apenas propriedades enumeráveis.
Iteração
O laço for..in itera sobre a lista de propriedades enumeráveis de um objeto (incluindo a série de [[Prototype]] ). Mas e se você quiser
iterar sobre os valores?

Com arrays indexados numericamente, iterar sobre os valores é geralmente realizado usado o laço padrão for , assim:

var myArray = [1, 2, 3];

for (var i = 0; i < myArray.length; i++) {

console.log( myArray[i] );

// 1 2 3

Essa forma não está iterando sobre os valores, e sim sobre os índices, onde depois você usa tais índices para referenciar os valores, como
myArray[i] .

ES5 também adiciona alguns helpers de interação para arrays, including forEach(..) , every(..) e some(..) . Cada um desses helpers
aceita uma função de callback para ser aplicada em cada elemento do array, diferenciando apenas na forma que cada um responde um
valor de retorno do callback.

forEach(..) irá iterar sobre todos os valores do array, e ignora qualquer valor retornado de callback. every(..) itera até o fim do array ou
até o callback retornar um valor false (ou "algo falso"), enquanto que some(..) itera até o fim ou até o callback retornar um valor true
(ou "algo verdadeiro").

Esses valores de retorno especiais dentro de every(..) e some(..) funcionam um pouco como a declaração break dentro de um laço
for normal, no qual a iteração é interrompida antes de atingir o seu final.

Se você iterar em um objeto com um laço for..in , você também está obtendo o valores indiretamente porque ele está iterando apenas
sobre as propriedades enumeráveis do objeto, fazendo com que você acesse as propriedades manualmente para obter os valores.

Nota: Em contraste a iteração sobre índices de arrays numericamente ordenados (laço for ou outros iteradores), a ordem de iteração
sobre as propriedades de um objeto não é garantida e pode variar entre diferentes motores JS. Não confie em qualquer ordenação
observada para qualquer coisa que requer consistência entre ambientes, uma vez que qualquer acordo observado não é confiável.

Mas se você quiser iterar sobre os valores diretamente em vez dos índices do array (ou propriedades do objeto)? Felizmente, ES6 adiciona
uma sintaxe de laço for..of para iterar sobre arrays (e objetos, se o objeto definir seu próprio iterador customizado):

var myArray = [ 1, 2, 3 ];

for (var v of myArray) {

console.log( v );

// 1

// 2

// 3

O laço for..of precisa de um objeto iterador (de uma função interna padrão conhecido nas especificações como @@iterator ) da coisa a
ser iterada, e o laço então itera sobre sucessivos valores retornado da chamada do método next() do objeto iterador, uma vez para cada
iteração do laço.

Arrays têm um @@iterator nativo, então o for..of funciona facilmente neles, como mostrado. Mas vamos iterar o array usando o
@@iterator nativo para ver como funciona:

var myArray = [ 1, 2, 3 ];

var it = myArray[Symbol.iterator]();

it.next(); // { value:1, done:false }

it.next(); // { value:2, done:false }

it.next(); // { value:3, done:false }

it.next(); // { done:true }
Nota: Nós chegamos ao @@iterator propriedade interna de um objeto usando Symbol do ES6: Symbol.iterator . Nós mencionamos
brevemente sobre a semântica do Symbol nesse capítulo (veja "Nomes de propriedades computadas"), então o mesmo raciocínio se aplica
aqui. Você sempre irá querer referenciar tais propriedades especiais pela referência de nome do Symbol em vez de um valor especial. Além
disso, apesar das implicações de nomes, @@iterator não é o objeto iterador, mas uma função que retorna o objeto iterador -- um detalhe
simples, mas importante!

Como o trecho acima revela, o valor de retorno de uma chamada next() do iterador é um objeto na forma de { value: .. , done: .. } ,
onde value é o atual valor da iteração e o done é um boolean que indica se há algo mais para iterar.

Note que o valor 3 foi retornado com done:false , que parece estranho à primeira vista. Você tem que chamar o next() uma quarta vez
(que o laço for..of no trecho de código anterior faz automaticamente) para obter done:true e saber que você realmente finalizou a
iteração. O motivo dessa peculiaridade está além do escopo do que iremos discutir aqui, mas vem de semânticas de funções do gerador no
ES6.

Enquanto arrays iteram automaticamente nos laços for..of , objetos comuns não possuem um @@iterator nativo.
As razões para omissão
intencional são mais complexas do que examinaremos aqui, mas em geral foi melhor não incluir alguma implementação que pudesse ser
problemática em futuros tipos de objetos.

É possível definir seu próprio @@iterator padrão para qualquer objeto que você queira iterar. Por exemplo:

var myObject = {

a: 2,

b: 3

};

Object.defineProperty( myObject, Symbol.iterator, {

enumerable: false,

writable: false,

configurable: true,

value: function() {

var o = this;

var idx = 0;

var ks = Object.keys( o );

return {

next: function() {
return {

value: o[ks[idx++]],

done: (idx > ks.length)


};

};

} );

// itera `myObject` manualmente

var it = myObject[Symbol.iterator]();

it.next(); // { value:2, done:false }

it.next(); // { value:3, done:false }

it.next(); // { value:undefined, done:true }

// itera `myObject` com `for..of`

for (var v of myObject) {

console.log( v );

// 2

// 3

Nota: Nós usamos Object.defineProperty(..) para definir nosso @@iterator personalizado (na maioria das vezes poderíamos definí-lo
como não-numérico), mas usando o Symbol como um nome de propriedade computado (abordado mais cedo nesse capítulo), nós
poderíamos ter declarado diretamente, como var myObject = { a:2, b:3, [Symbol.iterator]: function(){ /* .. */ } } .

Cada vez que o laço for..of chama next() no objeto iterador de myObject , o ponteiro interno avançará e retornará o próximo valor da
lista de propriedades do objeto (veja uma nota anterior sobre ordenação de iteração nas propriedaes/valores de objeto).

A iteração que acabamos de demonstrar é uma simples iteração valor-por-valor, mas claro que você pode definir arbitrariamente iterações
complexas para suas estruturas de dados como achar melhor. Iteradores personalizados combinados com o laço for..of do ES6 são um
nova e poderosa ferramenta sintática para manipulação de objetos definidos pelo o usuário.
Por exemplo, uma lista de objetos Pixel (com valores de coordenadas x e y ) poderia decidir ordenar sua iteração baseada na distância
linear da origem (0,0) ou filtrar pontos que são "muito distantes" etc. Contanto que seu iterador retorne os valores de retorno { value:
.. } esperados das chamadas do next() , e um { done: true } depois que a iteração esteja completa, o for..of do ES6 pode iterar
sobre os valores.

Na verdade, você pode até mesmo gerar iteradores "infinitos" que nunca "terminam" e sempre retornam um novo valor (tal como um
número randômico, um valor incrementado, um identificador único etc), contudo você provavelmente não usará tais iteradores com um laço
for..of infinito, pois isso nunca terminaria e suspenderia o seu programa.

var randoms = {

[Symbol.iterator]: function() {

return {

next: function() {
return { value: Math.random() };

};

};

var randoms_pool = [];

for (var n of randoms) {

randoms_pool.push( n );

// não continue infinitamente!

if (randoms_pool.length === 100) break;

Esse iterador gerará números randômicos "para sempre", por isso tivemos cuidado em pegar apenas 100 valores, fazendo com que nosso
programa não fique suspenso.

Revisão (TL;DR)
Objetos em JS possuem uma forma literal (tal como var a = { .. } ) e uma forma construída (tal como var a = new Array(..) ). A forma
literal é quase sempre preferível, mas a forma construída oferece, em alguns casos, mais opções de criação.

Muitas pessoas alegam erradamente que "tudo em JavaScript é um objeto", mas essa afirmação é incorreta. Objetos são um dos 6 (ou 7,
dependendo de sua perspectiva) tipos primitivos. Objetos têm subtipos, incluindo function , e também podem ser comportamento-
especializado, como [object Array] , rótulo interno representando o subtipo de objeto de array.

Objetos são coleções de pares chave/valor. Os valores podem ser acessados como propriedades, via sintaxe .propName ou ["propName"] . A
qualquer momento que uma propriedade é acessada, o motor JS, na verdade, invoca a operação interna [[Get]] padrão (e [[Put]] para
definir valores), que não só procura pela propriedade diretamente no objeto, mas irá percorrer a cadeia de [[Prototype]] se não for
encontrada.

Propriedades têm certas características que podem ser controladas por decritores de propriedade, tais como writable e configurable .
Além disso, objetos podem ter suas mutabilidades (e de suas propriedades) controladas para vários níveis de imutabilidade usando
Object.preventExtensions(..) , Object.seal(..) e Object.freeze(..) .

Propriedades não têm que conter valores -- elas podem ser "propriedades de acessor" também, com getters/setters. Elas também podem
ser enumeráveis ou não, que controlam se eles aparecem nas iterações do laço for..in , por exemplo.

Você também pode iterar sobre os valores nas estruturas de dados (arrays, objetos etc) usando a sintaxe for..of do ES6, que procura por
um objeto @@iterator nativo ou personalizado consistindo de um método next() para avançar pelos valores de dados um de cada vez.
Capítulo 4: Confundindo Objetos com "Classes"
Continuando com nossa exploração de objetos do capítulo anterior, é natural que agora nós demos atenção a "programação orientada a
objetos (OO)", com "classes". Nós primeiro iremos olhar para "orientação à classes" como um padrão de projeto, antes de examinar as
mecânicas de "classes": "instanciação", "herança" e "polimorfismo (relativo)".

Nós veremos que esses conceitos não mapeiam muito naturalmente para o mecanismo de objetos no JS e também até onde muitos
desenvolvedores JavaScript vão para superar estes desafios (mixins etc.).

Nota: Este capítulo gasta um tempo considerável (a primeira metade!) na exploração massiva da teoria por trás da "programação orientada
a objetos". Nós eventualmente relacionaremos a um código JavaScript concreto na segunda metade, quando nós falamos sobre "Mixins".
Mas há uma grande quantidade de conceito e pseudocódigo para percorrer primeiro, então não se perca -- apenas fique com isso!

Teoria das Classes


Classe/Herança descreve uma certa forma de organização de código e arquitetura -- uma maneira de modelar problemas de domínio do
mundo real em nosso software.

OO ou programação orientada à classes enfatiza que os dados têm um comportamento associado intrinsecamente (claro, diferente
dependendo do tipo e natureza do dado!) que opera sobre ele, então o design adequado é empacotar (também conhecido como
encapsular) o dado e o comportamento juntos. Isso às vezes é chamado "estruturas de dados" na ciência da computação.

Por exemplo, uma série de caracteres que representa uma palavra ou frase geralmente é chamada de "string". Os caracteres são os dados.
Mas você quase nunca se importa apenas com os dados, você geralmente quer fazer coisas com os dados, assim os comportamentos que
podem se aplicar para aquele dado (calcular seu tamanho, anexar dados, busca etc.) são todos concebidos como métodos de uma classe
String .

Qualquer string é apenas uma instância dessa classe, o que significa que é um pacote cuidadosamente organizado contendo tanto os
caracteres quanto as funcionalidades que podemos executar neles.

Classe também insinuam uma maneira de classificar uma certa estrutura de dados. A maneira que fazemos isso é assumindo que qualquer
estrutura é uma variação específica de uma definição base mais ampla.

Vamos explorar esse processo de classificação olhando para um exemplo comumente citado. Um carro pode ser descrito como uma
implementação específica de uma "classe" de coisas, chamada de veículo.

Nós modelamos esse relacionamento em software com classes definindo uma classe Vehicle e uma classe Car .

A definição de Vehicle pode incluir coisas como propulsão (motores etc.), a capacidade de transportar pessoas etc., que seriam todos os
comportamentos. O que nós definimos em Vehicle é todo o material que é comum a todos (ou a maioria) dos diferentes tipos de veículos
(como os "aviões, trens e automóveis").

Pode não fazer sentido no nosso software redefinir a essência básica da "capacidade de transportar pessoas"
repetidas vezes para cada tipo
diferente de veículo. Ao invés disso, nós definimos essa capacidade uma vez em Vehicle , e então quando definimos Car , nós
simplesmente indicamos que ele "herda" (ou "estende") sua definição base de Vehicle . Diz-se que a definição de Car especializa a
definição geral de Vehicle .

Enquanto Vehicle e Car definem coletivamente o comportamento por meio de métodos, os dados em uma instância seriam coisas como
o "número do chassi" de um carro específico etc.

E assim, classes, herança e instanciação surgem.

Outro conceito chave com classes é o "polimorfismo", o qual descreve a ideia que um comportamento genérico de uma classe pai pode ser
sobrescrito em uma classe filha para dar mais especificidade. De fato, o polimorfismo relativo nos permite referenciar o comportamento
base a partir do comportamento sobrescrito.

A teoria de Classe sugere fortemente que uma classe pai e uma classe filha compartilhem o mesmo nome de método para um determinado
comportamento, de modo que esse filho substitui o pai (diferentemente). Como nós veremos mais tarde, fazer isso no seu código JavaScript
é optar pela frustração e pela fragilidade do código.

Padrão de projeto "Classe"


Você pode nunca ter pensando sobre classes como um "padrão de projeto", já que é mais comum ver discussões sobre os populares
"Padrões de projeto OO", como "Iterator", "Observer", "Factory", "Singleton" etc. Apresentado dessa maneira, é quase uma suposição de que
as classes OO são a mecânica baixo nível pela qual implementamos todos os padrões de projeto (alto nível), como se OO fosse uma base
para todo o código (apropriado).

Dependendo do seu nível de educação formal em programação, você pode ter ouvido falar sobre "programação procedural" como uma
maneira de descrever o código que consiste apenas de procedimentos (também conhecido como funções) chamando outras funções, sem
nenhuma maior abstração. Você pode ter sido ensinado que classes eram a maneira adequada de transformar o estilo procedural "código
espaguete" em um código bem moldado e organizado.

Claro que, se você tem experiência com "programação funcional" (Monads etc.), você sabe muito bem que as classes são apenas um dos
vários padrões de projetos comuns. Mas para outras pessoas, esta pode ser a primeira vez que você se pergunta se as classes realmente são
uma base fundamental para o código, ou se elas são uma abstração opcional sobre o código.

Algumas linguagens (como Java) não te dão a opção de escolha, então não é muito opcional -- tudo é uma classe. Outras linguagens como
C/C++ ou PHP fornecem tanto a sintaxe procedural quanto a orientada a classes deixando mais para o desenvolvedor escolher qual estilo
ou mistura de estilos é apropriado.

"Classes" JavaScript
Onde o JavaScript se enquadra nesse aspecto? JS já possuía alguns elementos sintáticos baseados em classes (como new e instaceof ) por
um tempo, e mais recentemente no ES6, foram feitas algumas adições, como a palavra chave class (veja o Apêndice A).

Mas isso significa que atualmente o Javascript possui classes? Claro e simples: Não.

Como as classes são um design pattern, você pode, com um pouco de esforço (como veremos ao longo desse capítulo), implementar
aproximações de muitas funcionalidades clássicas das classes. O JS tenta satisfazer o desejo extremamente comum de projetar com classes,
fornecendo uma sintaxe aparentemente semelhante com a de uma classe.

Embora possamos ter uma sintaxe parecida com as das classes, é como se o mecanismo do Javascript estivesse lutando contra o seu desejo
de utilizar o design pattern de classes, porque por trás da cortina, os mecanismos que você constrói estão operando de forma bastante
diferente. Açúcar sintático e (as amplamente utilizadas) bibliotecas "Class" JS percorrem um longo caminho para esconder essa realidade de
você, porém mais cedo ou mais tarde você vai enfrentar o fato de que classes que você tem em outras linguagens não são como as
"classes" do Javascript.

Isso resume que as classes são um pattern opcional no design de um software, e você tem a escolha de usá-los no Javascript ou não. Como
muitos desenvolvedores possuem uma grande afinidade com design de software orientado a classes, passaremos o restante deste capítulo
explorando o que é necessário para manter a ilusão de que o JS provem classes, e os pontos problemáticos que teremos.

Mecânica das Classes


Na maioria das linguagens orientadas a classes, a "biblioteca padrão" provê uma "stack" de estruturas de dados (push, pop, etc) como uma
classe Stack .
Essa classe teria um conjunto interno de variáveis que armazena dados e um conjunto de comportamentos publicamente
acessíveis ("métodos") fornecidos pela classe, o que dá ao seu código a capacidade de interagir (adicionando e removendo, etc.) com os
dados (ocultos).

Porém em tais linguagens você não precisa trabalhar diretamente com essa classe Stack (a menos que esteja fazendo uma referência a um
membro de uma classe do tipo Static, o que está fora do escopo da nossa discussão). A classe Stack é apenas uma explicação abstrata do
que qualquer objeto do tipo "stack" pode fazer, mas não é em si uma "stack". Você deve instanciar a classe Stack antes de ter uma
estrutura de dados concreta onde possa operar.

Construção
O pensamento metafórico tradicional baseado em "classe" e "instância" vêm da construção civil.

Uma arquiteta planeja todos as características de um edifício: quão largo, quão alto, janelas e em quais locais, até mesmo o tipo de material
que será usado no teto e nas paredes. Até esse ponto, ela não necessáriamente se importa, onde o prédio será construido, nem se importa
em quantas cópias do edifício serão construídas.

Ela também não se importa muito com o conteúdo do edifício -- o mobiliário, o papel de parede, ventiladores de teto, etc. - somente o tipo
de estrutura que serão contidos.
Os projetos arquitetônicos que ela produz são apenas planos para um edifício. Eles não constituem realmente um prédio onde podemos
caminhar e sentar. Nós precisamos de um construtor para essa tarefa. Um construtor irá pegar esses planos e os seguir, minuciosamente,
enquanto constrói o prédio. Em um sentido mais real, ele está copiando as características dos planos para o prédio físico.

Uma vez concluído, o prédio é uma instanciação física do projeto arquitetônico, com sorte um cópia perfeita. E então o construtor pode se
mover para o lote aberto ao lado e fazer tudo novamente, criando outra cópia.

O relacionamento entre o prédio e o plano arquitetônico é indireto. Você pode examinar um projeto arquitetônico para entender como o
prédio foi estruturado, para todas as partes em que a inspeção direta do edifício em si fosse insuficiente. Mas se você quer abrir uma porta,
você tem que ir para o prédio em si -- o projeto tem apenas linhas desenhadas em uma página representando onde a porta deveria estar.

Uma classe é um projeto arquitetônico. Para realmente conseguir um objeto com o qual podemos interagir, nós devemos construir (também
conhecido como "instanciar") algo da classe. O resultado final dessa "construção" é um objeto, tipicamente chamado de "instância", no qual
podemos chamar métodos diretamente e acessar quaisquer propriedades de dados públicos, conforme o necessário.

O objeto é uma cópia de todas as características descritas pela classe.

Você provavelmente não esperaria entrar em um prédio e encontrar, emoldurado na parede, uma cópia do projeto arquitetônico usado para
planejar o prédio, embora projetos arquitetônicos provavelmente estejam arquivadas em um escritório de registros públicos. Similarmente,
você geralmente não usa uma instância de objeto para diretamente acessar e manipular a classe, mas é geralmente possível ao menos
determinar de qual classe um determinado objeto vem.

É mais útil considerar a relação direta entre a classe e uma instância de objeto, ao invés do relacionamento indireto entre uma instância de
objeto e a classe do qual ela vem. Uma classe é instanciada em um objeto por uma operação de cópia.

Como você pode ver, as setas se movem da esquerda para direita, e de cima para baixo, o que indica as operações de cópia que ocorrem
tanto conceitualmente quanto fisicamente.

Construtores
Instâncias das classes são construídas por um método especial da classe, que geralmente possui o mesmo nome da classe, chamado de
construtor. O trabalho desse método é inicializar qualquer informação (estado) que a instância irá precisar.

Por exemplo, considere esse pseudo-código solto (sintaxe inventada) para as classes:

class CoolGuy {

specialTrick = nothing

CoolGuy( trick ) {

specialTrick = trick

showOff() {

output( "Here's my trick: ", specialTrick )

Para criar uma instância de CoolGuy , nós vamos chamar o construtor da classe:
Joe = new CoolGuy( "jumping rope" )

Joe.showOff() // Here's my trick: jumping rope

Perceba que a classe CoolGuy tem um construtor CoolGuy() , que é o que chamamos quando dizemos new CoolGuy(..) . Nós temos como
retorno um objeto (uma instância da nossa classe) do construtor, e nós podemos chamar o método showOff() , que vai mostrar o truque
especial daquele determinado CoolGuy

Obviamente, pular corda torna Joe um cara muito legal

O construtor de uma classe pertence a classe, quase universalmente com o mesmo nome da classe. Além disso, construtores quase sempre
precisam ser chamados com o operador new para que o motor da linguagem saiba que você quer construir uma nova instância da classe.

Heranças de classe
Em linguagens orientadas a classes, você pode definir não somente qual classe pode ser instânciada, como também pode definir que outra
classe que herda da primeira classe.

A segunda classe é também chamada de classe-filha enquanto que a primeira é chamada de classe-pai. Esses termos se originam,
obviamente, de uma metáfora entre pais e filhos, embora as metáforas aqui estejam empregadas em um conceito mais amplo, como você
verá em breve.

Quando um pai tem um filho/filha biológico, as características genéticas do pai são copiadas no filho. Obviamente, na maioria dos sistemas
de reprodução biológicos, há dois pais que contribuem igualmente para a variabilidade genética. Mas por efeitos da metáfora, vamos
considerar somente um pai.

A partir do momento que o filho existe, ele ou ela é separado do pai. O filho foi fortemente influenciado pela herança genética do seu pai,
mas é único e distinto. Se uma criança tem cabelo ruivo isso não necessariamente significa que algum de seus pais tenha cabelo vermelho.

De maneira similar, uma vez que uma classe filha é definida, ela é separada e distinta da classe pai. A classe filha possui uma cópia inicial do
comportamento do pai, mas elas podem sobrescrever qualquer comportamento herdado e até mesmo definir um novo comportamento.

É importante relembrar que estamos falando sobre classes pais e filhos, que não são coisas físicas. E é isso o que torna a metáfora de pais e
filhos um tanto quanto confusas, por que o correto seria dizer que uma classe pai é na verdade o DNA de um pai, e uma classe filho seria
como o DNA de um filho. Nós podemos criar (também conhecido como "instanciar") uma pessoa de cada conjunto de DNA para realmente
ter uma pessoa com a qual podemos conversar.

Vamos colocar de o conceito biológico de pais e filhos, e olhar a herança através de uma lente ligeramente diferente: diferentes tipos de
veículos. Essa é uma das metáforas mais canônicas (e muitas vezes dignas) para entender herança.

Vamos revisitar a discussão sobre Vehicle (veículo) e Car (carro) que tivemos anteriormente nesse capítulo. Considere esse pseudo-
código solto (sintaxe inventada) para classes herdadas:

class Vehicle {

engines = 1

ignition() {

output( "Turning on my engine." )

drive() {

ignition()

output( "Steering and moving forward!" )

class Car inherits Vehicle {

wheels = 4

drive() {

inherited:drive()

output( "Rolling on all ", wheels, " wheels!" )

class SpeedBoat inherits Vehicle {

engines = 2

ignition() {

output( "Turning on my ", engines, " engines." )

pilot() {

inherited:drive()

output( "Speeding through the water with ease!" )

Nota: Para clareza e abreviação, os construtores dessas classes foram omitidos

Nós definimos a classe Veiculo para que ela assumisse um motor, a forma de ligar a ignição e uma maneira de dirigir. Mas você não
fabricaria um veículo genérico, então até esse ponto ela é um conceito abstrato.

Então, definimos dosi tipos específicos de veículos: Carro e SpeedBoat . Ambos herdam as características gerais de Veiculo , mas
especializam características apropriadamente para cada tipo. Um carro precisa de 4 rodas, e um SpeedBoat de dois motores, o que significa
que é preciso atenção extra para ligar a ignição de ambos motores.

Polimorfismo
Car define seu próprio método drive() , que sobrescreve o método de mesmo nome herdado da classe Veiculo . Mas então o método
drive() pertencente a Car chama inherited:drive() , o que indica que o carro pode referenciar o método original drive() pré-
sobrescrito herdado. O método pilot() do SpeedBoat também faz referência à sua cópia herdada do drive() .

Essa técnica é chamada de "polimorfismo", ou "polimorfismo virtual". Mais especificamente para nosso ponto atual, vamos chamar isso de
"polimorfismo relativo".

Polimorfismo é um tópico muito mais abrangente do que vamos abordar aqui, mas nossa atual semântica "relativa" refere-se a um aspecto
em particular: a ideia de que qualquer método pode referenciar outro método (com o mesmo nome, ou um nome diferente) em um nível
mais alto da hierarquia de herança. Nós chamamos de "relativo" porque não estabelecemos absolutamente qual nível de herança (também
conhecido como classe) queremos acessar, mas referencia-lo relativamente basicamente dizendo "procure um nível acima".

Em muitas linguagens, a palavra chave super é usada, no lugar de inherited desse exemplo, apoiando-se na ideia de que uma
"superclasse" é o pai/ancestral da classe atual.

Outro aspecto do polimorfismo é que um nome específico de método pode ter mútiplas definições em diferentes níveis da cadeia de
herança, e essas definiçõessão selecionadas de forma automática de acordo com os métodos que estão sendo chamados.

Nós vemos duas ocorrências desse comportamento em nosso exemplo acima: drive() é definido tanto em Veiculo como em Car , e
ignition() é definido tanto em Veiculo como em SpeedBoat .

Nota: Outra coisa que linguagens orientadas a classes fornecem a você através do super() é uma forma do construtor da classe filho
referenciar diretamente o construtor de sua classe pai. Isso é verdade principalmente porque, com classes reais, o construtor pertence a
classe. No entanto em JS é o contrário - é mais apropriado pensar que a "classe" pertence ao construtor (as referências Foo.prototype ). Já
que em JS o relacionamento entre pai e filho existe apenas entre os dois objetos .prototype dos respectivos construtores, os próprios
construtores não estão diretamente relacionados, e portanto, não há uma forma de referenciar um do outro (consulte o apêndice A para ver
como class resolve isso com super ).

Uma implicação interessante do polimorfismo pode ser visto especificamente com ignition() . Dentro de pilot() , uma referência
polimórfica é feita para (a herdada) versão drive() de Veiculo . Mas esse método drive() faz referência ao método ignition() apenas
pelo nome (sem termos referência relativa).

Qual versão de ignition() o motor da linguagem irá usar?, a de Veiculo ou a de SpeedBoat ? Ela usa a versão de SpeedBoat de
ignition() . Se você fosse instanciar a clase Veiculo , e chamar o seu método drive() , o motro da linguagem usaria apenas a definição do
método ignition() pertencete a Veiculo .

Dito de outra forma, a definição para o método ignition() polimorfa (muda) de acordo com a classe (nível de herança) que a sua instância
está referenciando.

Isso pode parecer um detalhe acadêmico muito profundo. Mas entender esses detalhes é necessário para diferenciar adequadamente
comportamentos semelhantes (porém diferentes) no mecanismo [[Prototype]] do Javascript.
Quando classes são herdadas, existe uma maneira de as próprias classes (não as instâncias de objetos criadas a partir delas!) referirem-se
relativamente à classe herdada, e essa referência relativa é geralmente chamada de super() .

Lembre se dessa figura anterior:

Note como todas as instanciações ( a1 , a2 , b1 e b2 ) e a herança ( Bar ) indicam operações de cópia.

Conceitualmente, parece que uma classe filho Bar pode acessar o comportamento em sua classe pai Foo usando uma referência
polimórfica relativa (também conhecida como super ). No entanto, na realidade, a classe filho recebe meramente uma cópia do
comportamento herdado da classe pai. Se o filho "sobrescrever" um método que ele herdou, as versões originais e sobrescritas do método
são mantidas, para que ambas sejam acessíveis.

Não deixe o polimorfismo confundir você em pensar que uma classe filho é ligada a uma classe pai. Uma classe filho recebe uma cópia do
que precisa da classe pai. Herança de classes significa cópias.

Herança múltipla
Se lembra da nossa conversa sobre pais, filhos e DNA? Nós dizemos que a metáfora era um pouco estranha porque biologicamente a
maioria dos descendentes vêm de dois pais. Se uma classe pudesse herdar de duas outras classes distintas, haveria uma maior aproximação
com a metáfora de pais e filhos.

Algumas linguagens orientadas a classes permitem que você especifique mais de uma classe "pai" para "herdar". Herança múltipla significa
que cada definição das classes pai foram copiadas para a classe filha.

Superficialmente, isso parece um adicional poderoso para a orientação a classes, nos dando a possibilidade de compor mais funcionalidades
juntos. No entanto, há certamente algumas questões complicadas que se levantam. Se ambas as classes pai possuem um método chamado
drive() , qual versão de drive() será referenciada pela classe filha? Você sempre teria que manualmente especificar qual o método pai
drive() você quis dizer, perdendo um pouco da graça da herança polimórfica?

Há uma outra variação, o chamado "Problema do Diamante", que se refere a um cenário onde uma classe filha "D" herda de outras duas
classes pai ("B" e "C"), e essas classes, por sua vez, herdam de um pai "A" comum. Se "A" provem um método drive() , e "B" e "C"
sobrescrevem (polimorficamente) esse método, quando D referencia drive() , qual versão ele irá usar( B:drive() ou C:drive() )?

Essas complicações são ainda mais profundas do que essa rápida análise. Nós as expomos aqui apenas para que possamos contrastar com o
funcionamento dos mecanismos Javascript.
Javascript é mais simples: ele não fornece um mecanismo nativo para "herança múltipla". Muitos vêem isso como uma coisa boa, porque a
economia de complexidade é mais compensatória do que a funcionalidade "reduzida". Mas isso não impede os programadores de fingirem
fazer isso de várias formas diferentes, como vamos ver a seguir.

Mixins
O mecânismo objeto do Javascript não executa automaticamente o comportamento de uma cópia quando você "herda" ou "instancia".
Obviamente, não há classes no Javascript que serão instanciadas, apenas objetos. E objetos não são cópiados para outros objetos, eles são
linkados juntos (mais sobre isso no capítulo 5).

Como comportamentos de classes observados em outras linguagens implicam em cópias, vamos examinar como desenvolvedores JS
falsificam o comportamento de cópia ausente no mecânismo de classes JavaScript: Mixins. Vamos ver dois tipos de "mixin": explícito e
implícito.

Mixin Explícito
Vamos revisitar nosso exemplo anterior sobre Vehicle e Car . Como o JavaScript não irá copiar automaticamente o comportamento de
Vehicle para Car , podemos criar um utilitário que copie manualmente. Tal utilidade é frequentemente chamada de extend(..) por
muitas bibliotecas/frameworks, mas aqui vamos chamar isso de mixin(..) por motivos ilustrativos.

// Exemplo bem simplificado de`mixin(..)`:

function mixin( sourceObj, targetObj ) {

for (var key in sourceObj) {

// só copie se ainda não estiver presente

if (!(key in targetObj)) {

targetObj[key] = sourceObj[key];

return targetObj;

var Vehicle = {

engines: 1,

ignition: function() {

console.log( "Turning on my engine." );

},

drive: function() {

this.ignition();

console.log( "Steering and moving forward!" );

};

var Car = mixin( Vehicle, {

wheels: 4,

drive: function() {

Vehicle.drive.call( this );

console.log( "Rolling on all " + this.wheels + " wheels!" );

} );

Nota: De maneira sutil, mas importante, nós não estamos mais lidando com classes, porque não há classes no JavaScript. Vehicle e Car
são apenas objetos dos quais fazemos cópias de e para, respectivamente.

Car agora tem um cópia das propriedades e funções estabelecidas em Vehicle . Tecnicamente, as funções não são realmente duplicadas,
mas as referências a elas são copiadas. Então Car possui agora uma propriedade chamada ignition , que é uma referência copiada para a
função ignition() , assim como uma propriedade chamada engines com o valor copiado de 1 de Vehicle .

Car já possui uma propriedade (função) drive() , de modo que a referência de propriedade não foi substituida (consulte a instução if no
mixin(..) acima).

"Polimorfismo" revisado
Vamos examinar essa instrução: Vehicle.drive.call(this) . Isso é o que eu chamo de "pseudo-polimorfismo explícito". Lembre-se que no
nosso pseudo-código anterior, essa linha era inherited:drive() , que nós chamamos de "polimorfismo relativo".

O JavaScript não tem (antes do ES6; veja apêndice A) um utilitário para o polimorfismo relativo. Então, já que Car e Vehicle tinahm uma
função com o mesmo nome: drive() , para distinguir uma chamada de um ou outra, temos de fazer uma referência absoluta (não relativa).
Nós explicitamente especificamos o objeto Vehicle pelo nome, e chamamos a função drive() nele.

Mas se dissermos Vehicle.drive , o binding do this para essa função será o objeto Vehicle em vez do objeto Car (veja o capítulo 2), o
que não é o que queremos. Então, em vez disso usamos .call( this ) (capítulo 2) para garantir que drive() seja executado no contexto
do objeto Car .

Nota: Se o identificador da função para Car.drive() não tinha se sobreposto com (também conhecido como "sombreado"; veja capítulo 5)
Vehicle.drive() , não teríamos exercido o "polimorfismo de método". Então, uma referência para Vehicle.drive() teria sido copiada pela
chamada mixin(..) , e nós poderiamos tê-la acessado diretamente através de this.drive() . O identificador escolhido se sobrepondo ao
sombreamento é o porque de termos de usar uma abordagem mais complexa de pseudo-polimorfismo explícito.

Em linguagens orientadas a classes, que possuem polimorfismo relativo, a linkagem entre Car e Vehicle é estabelecida uma vez, no topo
da definição da classe, o que faz apenas um lugar para manter esses tais relacionamentos.

Mas por causa das peculiaridades do JavaScript, o pseudo-polimorfismo explícito (por causa do sombreamento!) cria vinculação
manual/explícita frágil em cada função onde você precisa de uma referência (pseudo) polimórfica. Isso pode aumentar muito o custo de
manutenção. Além disso, enquanto o pseudo-polimorfismo explícito pode emular o comportamento de "herança múltipla", ele apenas
aumenta a complexidade e fragilidade.

O resultado dessas abordagens é normalmente um código mais complexo, difícil de ler, e difícil de manter. Pseudo polimorfismo explícito
deve ser evitado sempre que possivel, porque o custo supera o benefício na maioria dos aspectos.

Mesclando cópias

Relembre o utilitário mixin(..) acima:

// Exemplo bem simplificado de`mixin(..)`:

function mixin( sourceObj, targetObj ) {

for (var key in sourceObj) {

// só copie se ainda não estiver presente

if (!(key in targetObj)) {

targetObj[key] = sourceObj[key];

return targetObj;

Agora, vamos examinar como mixin(..) funciona. Ele itera sobre as propriedades de sourceObj ( Vehicle em nosso exemplo) e se não
tiver uma propriedade compatível em targetObj ( Car em nosso exemplo) ele cria uma cópia. Como estamos fazendo uma cópia depois
que o objeto inicial existe, tomamos o cuidado de não copiar uma propriedade de destino.

Se fazermos as cópias primeiro, antes de especificar o conteúdo específico para Car , poderiamos omitir essa verificação contra o
targetObj , mas isso é um pouco mais desajeitado e menos eficiente, então geralmente é menos preferido:

// alternate mixin, less "safe" to overwrites

function mixin( sourceObj, targetObj ) {

for (var key in sourceObj) {

targetObj[key] = sourceObj[key];

return targetObj;

var Vehicle = {

// ...

};

// first, create an empty object with

// Vehicle's stuff copied in

var Car = mixin( Vehicle, { } );

// now copy the intended contents into Car

mixin( {

wheels: 4,

drive: function() {

// ...

}, Car );

Em qualquer abordagem, copiamos explicitamente o conteúdo não sobreposto do Vehicle no Car . O nome "mixin" vem de uma maneira
alternativa de explicar a tarefa: O Car tem o conteúdo de Vehicle misturado a ele, assim como você mistura pedaços de chocolate em sua
massa de biscoito favorita.

Como resultado da operação, Car irá operar de forma pouco separada de Vehicle , e vice versa.

Nota: Alguns pequenos detalhes foram desdobrados por aqui. Ainda existem algumas maneiras sutis que os dois objetos podem "afetar"
uns aos outros, mesmo após a cópia, como se ambos compartilhassem uma referência para um mesmo objeto (como um array).

Como os dois objetos também compartilham referências para as suas funções comuns, isso significa que mesmo copias manuais de
funções (também conhecidos como mixins) de um objeto para outro não emulam a duplicação real de classe para a instância que ocorre
em linguagens orientadas a classes.

Funções JavaScript não podem ser duplicadas (de uma maneira padrão e confiável), então, o que você acaba fazendo é má referência
duplicada ao mesmo objeto de função compartilhada (funções são objetos; veja capítulo 3). Se você modificar uma das funções objetos
compartilhadas (como ignition() ) adicionando propriedades sobre elas, por exemplo, tanto Vehicle quanto Car seriam "afetados" por
meio da referência compartilhada.

Mixins explícitos são um ótimo mecânismo em JavaScript. Mas eles parecem ser mais fortes que realmente são. Poucos benefícios são
realmente derivados da cópia de uma propriedade para outra, ao invés de apenas definir as propriedades duas vezes, uma vez em cada
objeto. E isso é especialmente verdadeiro, dada a nuance de referência de objeto de função que acabamos de citar.

Se você mixar dois ou mais objetos dentro de um objeto alvo, você pode emular parcialmente o comportamento de "herança multipla",
mas não há uma forma direta de lidar com colisões se o mesmo método ou propriedade está sendo copiado de mais de uma origem.
Alguns desenvolvedores e bibliotecas surgiram com técnicas de "binding tardio" e outras abordagens exóticas, mas fundamentalmente
esses "truques" são geralmente mais trabalhosos (e têm desempenho menor) do que o resultado.

Tome cuidado de apenas usar mixins explícitos onde ele realmente ajuda a tornar o código mais legível, e evite esse padrão sempre que
você achar que ele está tornando o código mais difícil de rastrear, ou se achar que ele cria dependencias desnecessárias ou difíceis de
manipular entre objetos.

Se começar a ficar mais difícil usar mixins do que antes, você provavelmente deveria para de usar mixins. De fato, se você tem que usar
uma biblioteca/utilitário complexo para trabalhar todos esses detalhes, pode ser um sinal que você está seguindo pelo caminho mais difícil,
talvez desnecessariamente. No capítulo 6, tentaremso destilar uma maneira mais simples que realize os resultados desejados sem toda
confusão.

Herança Parasita

Uma variação desse padrão de mixin explícito, que é de algumas formas explícita e de outras formas implícita, é chamada de "herança
parasita", popularizado principalmente por Douglas Crockford.

Aqui está como isso pode funcionar:

// "Classe Tradicional JS" `Vehicle`

function Vehicle() {

this.engines = 1;

Vehicle.prototype.ignition = function() {

console.log( "Turning on my engine." );

};

Vehicle.prototype.drive = function() {

this.ignition();

console.log( "Steering and moving forward!" );

};

// "Classe Parasita" `Car`

function Car() {

// primeiramente, `car` é um `Vehicle`

var car = new Vehicle();

// agora, vamos modificar nosso `car` para especificá-lo

car.wheels = 4;

// salve uma referência especial para `Vehicle::drive()`

var vehDrive = car.drive;

// substitua `Vehicle::drive()`

car.drive = function() {

vehDrive.call( this );

console.log( "Rolling on all " + this.wheels + " wheels!" );

};

return car;

var myCar = new Car();

myCar.drive();

// Turning on my engine.

// Steering and moving forward!

// Rolling on all 4 wheels!

Como você pode ver, nós inicialmente fizemos uma cópia da definição da "classe pai" Vehicle (objeto), então misturamos nossa definição
da "classe filha" (objeto) (preservando as referências especiais da classe pai conforme necessário), e passamos esse objeto composto car
como nossa instância filha.

Nota: quando nós chamamos new Car() , um novo objeto é criado e especificado pela referência this de Car (veja o Capítulo 2). Mas
uma vez que nós não usamos aquele objeto, e ao invés disso retornamos nosso próprio objeto car , o objeto criado inicialmente é
descartado. Então, Car() poderia ser chamado sem a palavra-chave new , e o funcionamento acima seria idêntico, mas sem o desperdício
da criação e coleta de lixo do objeto.

Mixins Implícitos
Mixins implícitos estão intimamente relacionados com pseudo-polimorfismo explícito como explicado anteriormente. Sendo assim, eles vêm
com as mesmas advertências e alertas.

Considere este código:

var Something = {

cool: function() {

this.greeting = "Hello World";

this.count = this.count ? this.count + 1 : 1;

};

Something.cool();

Something.greeting; // "Hello World"

Something.count; // 1

var Another = {

cool: function() {

// mixin implícito de `Something` para `Another`

Something.cool.call( this );

};

Another.cool();

Another.greeting; // "Hello World"

Another.count; // 1 (não compartilha estado com `Something`)

Com Something.cool.call( this ) , que pode acontecer tanto numa chamada de "constructor" (mais comum), ou em uma chamada de
método (mostrado aqui), nós essencialmente "emprestamos" a função Something.cool() e a chamamos no contexto de Another (via o
vínculo this ; veja o Capítulo 2) no lugar de Something . O resultado final é que as atribuições que Something.cool() faz são aplicadas no
objeto Another ao invés do objeto Something .

Então, é dito que nós "misturamos" o comportamento de Something com (ou em) Another .
Enquanto esse tipo de técnica parece tirar proveito da funcionalidade de re-ligação do this , é na frágil chamada Something.cool.call(
this ) , que não pode ser transformada em uma referência relativa (e então, mais flexível), que você deve prestar atenção com cuidado.
Geralmente, evite tais construções sempre que possível para manter um código mais limpo e mais sustentável.

Revisão (TL;DR)
Classes são um design pattern. Muitas linguagens disponibilizam una sintaxe que permite um design de software orientado a classes
natural. JS também tem uma sintaxe semelhante, mas ela funciona muito diferente do que você está acostumado com as classes nessas
outras linguagens.

Classes significa cópias

Quando classes tradicionais são instanciadas, ocorre uma cópia do comportamento da classe para a instância. Quando uma classe é
herdada, também ocorre uma cópia do comportamento de pai para filho.

Polimorfismo (diferentes funções em múltiplos níveis dentro de uma cadeia de herança com o mesmo nome) pode parecer que implica um
link de referência relativa do filho de volta para o pai, mas isso ainda é o resultado da cópia do comportamento.

JavaScript não cria cópias entre objetos (como classes fazem) automaticamente.

O padrão de mixin (ambos, explícito e implícito) é geralmente usado como uma forma de emular o comportamento de cópia de classes,
mas isso geralmente leva a sintaxes feias e frágeis como o pseudo-polimorfismo explícito ( OtherObj.methodName.call(this, ...) ), que
resultam em códigos difíceis de entender e manter.

Mixins explícitos também não são exatamente iguais a uma cópia de classe, uma vez que objetos (e funções!) só terão compartilhado
referências duplicadas, e não os objetos/funções duplicados. Não prestar atenção a tais detalhes é a fonte de uma variedade de armadilhas.

Em geral, imitar classes em JS geralmente criam mais minas terrestres para o código futuro do que soluções de problemas atuais reais.
Capítulo 5: Protótipos
Nos capítulos 3 e 4, nós mencionamos a cadeia [[Prototype]] por diversas vezes, mas não havíamos dito ainda do que exatamente se
trata. Agora, nós vamos examinar protótipos em detalhes.

Nota: Todas as tentativas de emular o comportamento de cópia de classes, como descrito anteriormente no Capítulo 4, rotuladas como
variações de "mixins", contornam completamente o mecanismo de cadeia de prototipagem [[Prototype]] que iremos examinar neste
capítulo.

[[Prototype]]

Objetos em JavaScript possuem propriedades internas, denominadas em sua especificação como [[Prototype]] , que trata-se
simplesmente de uma referência à um outro objeto. Quase todos os objetos recebem um valor não nulo para essa propriedade, no
momento de sua criação.

Nota: Nós veremos em breve que é possível para um objeto ter uma ligação vazia de [[Prototype]] , embora isso seja pouco comum.

Considere:

var myObject = {

a: 2

};

myObject.a; // 2

Qual é a referência de [[Prototype]] utilizada? No capítulo 3, nós examinamos a operação [[Get]] que é invocada quando você
referencia uma propriedade à um objeto, como o myObject.a . Para uma operação [[Get]] padrão, o primeiro passo é checar se o próprio
objeto possui uma propriedade a , e caso possua, se a mesma é utilizada.

Nota: ES6 Proxies estão fora do escopo de discussão deste livro (isso será visto em um futuro livro da série!), mas tudo que nós discutimos
aqui sobre comportamentos normais de [[Get]] e [[Put]] não se aplicam caso um Proxy seja envolvido.

Mas é o que acontece se a não está presente em myObject que chama nossa atenção agora para a ligação [[Prototype]] do objeto.

O procedimento de uma operação [[Get]] padrão segue a ligação [[Prototype]] do objeto caso não consiga encontrar a propriedade
que for solicitada diretamente no objeto.

var anotherObject = {

a: 2

};

// cria um objeto ligado com `anotherObject`


var myObject = Object.create( anotherObject );

myObject.a; // 2

Nota: Nós explicaremos o que Object.create(..) faz, e como ele opera, em breve. Por enquanto, apenas assuma que ele cria um objeto
com uma ligação [[Prototype]] no objeto especificado que estamos examinando.

Então, temos myObject que agora está ligado através do [[Prototype]] com anotherObject . Embora myObject.a não exista realmente, o
acesso à propriedade é bem sucedido (encontrado em anotherObject ao invés disso) e de fato encontra o valor 2 .

Mas, caso a também não seja encontrado em anotherObject , sua cadeia [[Prototype]] , caso não esteja vazia, é novamente consultada e
seguida.

Esse processo continua até que seja encontrada uma propriedade com o mesmo nome, ou até que a cadeia [[Prototype]] termine. Se
nenhuma propriedade for encontrada até o final da cadeia [[Prototype]] , o resultado que a operação [[Get]] retorna é undefined .
Similar à esse processo de busca na cadeia [[Prototype]] , se você usar o laço for..in para iterar um objeto, qualquer propriedade que
seja alcançada através da cadeia (e que também seja enumerable -- veja Capítulo 3) será enumerada. Se você utilizar o operador in para
testar a existência de uma propriedade dentro de um objeto, in irá verificar por toda a cadeia do objeto (independentemente da
enumerabilidade do mesmo).

var anotherObject = {

a: 2

};

// cria um objeto ligado ao `anotherObject`

var myObject = Object.create( anotherObject );

for (var k in myObject) {

console.log("found: " + k);

// found: a

("a" in myObject); // retorna true

Ou seja, a cadeia [[Prototype]] é consultada, uma ligação por vez, quando você realiza buscas de propriedades de diferentes maneiras. As
buscas param assim que a propriedade é encontrada ou assim que a cadeia termina.

Object.prototype

Mas onde exatamente a cadeia [[Prototype]] "termina"?

No topo de toda cadeia [[Prototype]] normal está objeto nativo Object.prototype . Este objeto inclui uma varidade de utilitários
normalmente utilizados por todo JS, porque todos objetos normais (nativos, não extensões self-hosted) em JavaScript "descendem" (ou
constam no topo de sua cadeia [[Prototype]] ) do objeto Object.prototype .

Alguns utilitários encontrados aqui com os quais você pode estar familiarizado incluem .toString() e .valueOf() . No Capítulo 3, nós
introduzímos um outro: .hasOwnProperty(..) . E uma outra função dentro do Object.prototype da qual você não deve estar familiriazado,
mas que iremos tratar adiante neste capítulo, é .isPrototypeOf(..) .

Configuração e Sombreamento de Propriedades


No Capítulo 3, nós havíamos mencionado que a configuração das propriedades de um objeto é mais do que simplesmente adicionar uma
nova propriedade ao objeto ou alterar o valor de uma propriedade existente. Nós vamos agora revisitar essa situação de uma forma mais
completa.

myObject.foo = "bar";

Se o objeto myObject já possui uma propriedade de acesso à dados normal chamada de foo diretamente presente, a atribuição é tão
simples quanto alterar o valor de uma propriedade existe.

Se foo não estiver diretamente presente em myObject , a cadeia [[Prototype]] é percorrida, assim como em uma operação [[Get]] . Se
foo não for encontrada na cadeia, a propriedade foo é adicionada diretamente para myObject com seu valor especificado, como
esperado.

Entretanto, se foo já está presente em algum lugar mais alto da cadeia, comportamentos sutilmente (e talvez surpreendentemente)
diferentes podem ocorrer com a atribuição myObject.foo = "bar" . Nós examinaremos isso em instantes.

Se a propriedade com nome foo acabar tanto no próprio myObject quanto em um nível mais alto da cadeia [[Prototype]] que começa
em myObject , trata-se de algo chamado de sombreamento. A propriedade foo que se encontra diretamente em myObject faz sombra à
qualquer propriedade foo que apareça mais alto na cadeia, porque a busca de myObject.foo irá sempre encontrar a propriedade foo que
estiver em um lugar mais baixo na cadeia.

Como acabamos de indicar, sombrear foo em myObject não é tão simples quanto pode parecer. Nós vamos agora examinar três cenários
para a atribuição myObject.foo = "bar" quando foo não está diretamente presente em myObject , mas está presente em um nível mais
alto da cadeia [[Prototype]] de myObject .

1. Se uma propriedade de acesso à dados normal (veja Capítulo 3) de nome foo é encontrada em qualquer lugar da cadeia
[[Prototype]] , e não está marcada como somente leitura ( writable:false ) então uma nova propriedade de nome foo é
diretamente adicionada à myObject , resultando em uma propriedade sombreada.
2. Se foo é encontrada no alto da cadeia [[Prototype]] , mas está marcada como somente leitura ( writable:false ), então tanto a
configuração de uma propriedade existente quanto a criação de uma propriedade sombreada em myObject não são permitidas. Se o
código estiver rodando em strict mode , um erro será retornado. Caso não esteja, uma atribuição de valor à propriedade será
silenciosamente ignorada. Em todo caso, não haverá sombreamento.
3. Se foo é encontrada no alto da cadeia [[Prototype]] e for um setter (veja Capítulo 3), então o setter sempre será chamado.
Nenhuma foo será adicionada (ou sombreada) em myObject , e nem o setter de foo será redefinido.

Muitos desenvolvedores assumem que a atribuição de uma propriedade ( [[Put]] ) sempre resultará em sombreamento se a propriedade já
existir no alto da cadeia [[Prototype]] , mas como se nota, isso só é verdadeiro em uma (a primeira) das três situações descritas acima.

Se você quer sombrear foo nos casos 2 e 3, não poderá usar = para fazer a atribuição, ao invés disso deve utilizar
Object.defineProperty(..) (veja Capítulo 3) para adicionar foo em myObject .

Nota: O caso 2 pode ser o mais surpreendente dos três. A presença de uma propriedade somente leitura evita que uma propriedade de
mesmo nome seja implicitamente criada (sombreada) em um nível mais baixo da cadeia [[Prototype]] . O motivo para esta restrição é
primariamente para reforçar a ilusão de se ter propriedades herdadas de classes. Se você pensar em foo em um nível alto da cadeia como
tendo sido herdado (copiado para baixo) para myObject , então faz sentido impor a natureza não-gravável desta propriedade foo em
myObject . Se você no entanto separar a ilusão e o fato, e reconhecer que nenhuma cópia de herança realmente ocorreu (veja Capítulos 4 e
5), é um pouco antinatural que myObject seja impedido de ter uma propriedade foo apenas porque algum outro objeto tinha um foo
não-gravável nele. É ainda mais estranho que esta restrição só se aplique à designação = , mas não seja aplicada quando
Object.defineProperty (..) é utilizado.

Sombreamento com métodos leva ao terrível pseudo-polimorfismo explícito (veja Capítulo 4), se você precisa delegar entre eles.
Normalmente, sombreamento é mais complicado e específico do que sua importância, então você deve tentar evitar de usar, se possível.
Veja o Capítulo 6 para um design pattern alternativo, que entre outras coisas desencoraja o uso do sombreamento em favor de alternativas
mais limpas.

Sombreamento pode até ocorrer implicitamente de maneiras sutis, então um cuidado deve ser tomado na tentativa de evitá-lo. Considere:

var anotherObject = {

a: 2

};

var myObject = Object.create( anotherObject );

anotherObject.a; // 2

myObject.a; // 2

anotherObject.hasOwnProperty( "a" ); // true

myObject.hasOwnProperty( "a" ); // false

myObject.a++; // ops, sombreamento implicito!

anotherObject.a; // 2

myObject.a; // 3

myObject.hasOwnProperty( "a" ); // true

Embora possa parecer que myObject.a++ deveria (via delegação) procurar e apenas incrementar a propriedade anotherObject.a
propriamente dita em seu lugar, em vez disso a operação ++ corresponde à myObject.a = myObject.a + 1 . O resultado é um [[Get]]
procurando a propriedade a através de [[Prototype]] para obter o valor atual 2 de anotherObject.a , incrementando o valor em um, e
então um [[Put]] atribuindo o valor 3 à uma nova propriedade sombreada a em myObject . Ops!

Tenha muito cuidado ao lidar com as propriedades delegadas que modifica. Se você quer incrementar anotherObject.a , a única maneira
apropriada é anotherObject.a++ .

"Classes"
Neste momento, você deve estar imaginando: "Por que um objeto precisa ser ligado à um outro objeto?" Qual é o real benefício? Esta é uma
pergunta muito pertinente de se fazer, mas primeiro precisamos entender o que [[Prototype]] não é antes de entendê-lo completamente
e apreciar o que é e como pode ser útil.
Como explicamos no Capítulo 4, em JavaScript, não há padrões abstratos para objetos chamados de "classes" como no caso de linguagens
orientadas à classes. JavaScript tem apenas objetos.

Na verdade, JavaScript é quase único entre as linguagens pelo fato de talvez ser a única linguagem com o direito de ser rotulada de
"orientada à objetos", porque é uma entre uma lista bem pequena de linguagens em que objetos podem ser criados diretamente, sem a
existência de uma classe.

Em JavaScript, classes não podem (já que não existem!) descrever o que um objeto pode fazer. O objeto define diretamente seu próprio
comportamento. Existe apenas o objeto.

Funções de "Classes"
Existe um tipo de comportamento peculiar em JavaScript que vem sendo descaradamente abusado por anos para hackear algo que
somente parece com "classes". Nós examinaremos essa abordagem em detalhes.

O peculiar comportamento de "espécie de classe" depende de uma estranha característica das funções: por padrão, todas as funções obtêm
uma propriedade pública, não enumerável (veja o Capítulo 3) nelas chamada prototype , que aponta para um objeto arbitrário.

function Foo() {

// ...

Foo.prototype; // { }

Esse objeto é frequentemente chamado de "prototype de Foo", porque o acessamos atráves de uma referência de propriedade com o infeliz
nome de Foo.prototype . Entretanto, essa terminologia está fatalmente destinada à nos confundir, como veremos em breve. Ao invés disso,
irei chamar de "o objeto que antes era conhecido como prototype de Foo". Brincadeira. Que tal: "o objeto arbitrariamente rotulado de 'Foo
ponto prototype'"?

Independente de como chamamos, o que exatamente é este objeto?

A forma mais direta de se explicar é que cada objeto criado ao chamar new Foo() (veja Capítulo 2) acabará (de certa forma,
arbitrariamente) ligado pelo [[Prototype]] com esse objeto 'Foo ponto prototype'.

Vamos ilustrar:

function Foo() {

// ...

var a = new Foo();

Object.getPrototypeOf( a ) === Foo.prototype; // true

Quando a é criado ao chamar new Foo() , uma das coisas (ceja Capítulo 2 para todos os quatro passos) que acontecem é que a obtêm
uma ligação [[Prototype]] interna com o objeto que Foo.prototype está apontando.

Pare um momento e pondere as implicações desta declaração.

Em linguagens orientadas à classes, multiplas cópias (também conhecidas como "instâncias") de uma classe podem ser criadas, como
carimbar algo à partir de um molde. Como vimos no Capítulo 4, isso acontece porque o processo de instanciação (ou de herdar de) uma
classe significa "copiar o plano de comportamento desta classe para um objeto físico", e isso é feito novamente para cada nova instância.

Mas em JavaScript, não há ações de cópias deste tipo. Você não cria múltiplas instâncias de uma classe. Você cria múltiplos objetos que são
ligados pelo [[Prototype]] com um objeto comum. Mas por padrão, nenhuma cópia ocorre, e portanto esses objetos acabam não sendo
totalmente separados e nem desconectados um do outro, pelo contrário, estão bem ligados.

new Foo() resulta em um novo objeto (que chamamos de a ), e este novo objeto a é internamente ligado por [[Prototype]] com o
objeto Foo.prototype .

Nós acabamos com dois objetos, um ligado ao outro. E é isso. Nós não instanciamos uma classe. Nós certamente não fizemos nenhuma
cópia de comportamento de uma "classe" para um objeto concreto. Nós só fizemos com que dois objetos fossem ligados um ao outro.
Na verdade, o segredo, que ilude a maioria dos desenvolvedores JS, é que a chamada da função new Foo() não tem praticamente
nenhuma relação direta com o processo de criar a ligação. Foi uma espécie de efeito colateral acidental. new Foo() é uma forma indireta
de se conseguir o que queremos: um novo objeto ligado à um outro objeto.

Nós conseguimos ter o que queremos de uma forma mais direta? Sim! o herói é Object.create(..) . Mas nós chegaremos lá daqui à pouco.

O que está em um nome?

Em JavaScript, nós não fazemos cópias de um objeto ("classe") para outro ("instância"). Nós criamos ligações entre objetos. Para o
mecanismo [[Prototype]] , visualmente, as setas movem da direita pra esquerda, e de baixo pra cima.

Este mecanismo é frequentemente chamado de "herança prototípica" (nós examinaremos o código em detalhes à seguir), e é comumente
considerado como a versão em linguagem dinâmica para a "herança clássica". É uma tentativa de se apoiar no entendimento comum do
que "herança" significa no mundo orientado à classes, mas ajustar (leia: pavimentar) a semântica compreendida para se adequar ao script
dinâmico.

A palavra "herança" tem um significado poderoso (veja Capítulo 4), com muito precedente mental. Meramente adicionar o termo
"prototípica" para distinguir um comportamento que é na verdade quase oposto em JavaScript deixou um rastro de quase duas décadas de
confusão.

Eu gosto de dizer que o rótulo de "protótipo" para uma "herança" inverte drásticamente o seu real significado, é como segurar uma laranja
em uma mão, uma maçã na outra, e insistir para que chamem a maçã de "laranja vermelha". Não importa o quão confuso seja o rótulo que
eu utilize, isso não muda o fato que uma fruta é maçã e a outra é laranja.

A melhor abordagem é simplesmente chamar uma maçã de maçã -- para usar a terminologia mais precisa e direta. Isso facilita o
entendimento tanto em suas similaridades quanto suas muitas diferenças, porque tudo que temos é um entendimento simples e
compartilhado do que "maçã" significa.

Graças à confusão e conflação de termos, acredito que o próprio rótulo de "herança prototípica" (e a tentativa de se aplicar incorretamente
toda a sua terminologia de orientação à classes associada, como "classe", "construtor", "instância", "polimorfismo", etc.) tem causado efeitos
mais negativos que positivos em explicar como o mecanismo de JavaScript realmente funciona.

"Herança" implica em uma operação de cópia, e JavaScript não copia propriedades de objetos (nativamente, por padrão). Ao invés disso, JS
cria uma ligação entre dois objetos, onde um objeto pode essencialmente delegar acesso às propriedades/funções para outro objeto.
"Delegação" (veja Capítulo 6) é um termo muito mais preciso para o mecanismo de ligação entre objetos do JavaScript.

Outro termo que às vezes é jogado em JavaScript é "herança diferencial". A ideia aqui é que descrevemos o comportamento de um objeto
em termos do que é diferente de um descritor mais generalizado. Por exemplo, você explica que um carro é um tipo de veículo, mas que
tem exatamente 4 rodas, ao invés de descrever todos os detalhes do que compõe um veículo em geral (motor, etc).

Se você tentar pensar em qualquer objeto em JS como a soma total de todo o comportamento que está disponível via delegação, e em sua
mente você nivela todo esse comportamento em apenas uma coisa tangível, então você pode (de certa forma) ver como "herança
diferencial" poderá se encaixar.

Mas assim como "herança prototípica", "herança diferencial" finge que seu modelo mental é mais importante que o que está fisicamente
acontecendo na linguagem. Ela negligencia o fato que o objeto B não é realmente diferencialmente construído, mas é construído com
características específicas definidas, ao lado de "buracos" onde nada é definido. É nesses "buracos" (lacunas, ou falta de definição) que a
delegação pode assumir e, na mosca, "preenchê-los" com o comportamento delegado.
O objeto não é, por padrão nativo, nivelado em um único objeto diferencial, através de cópia, que o modelo mental de "herança diferencial"
implica. Como tal, "herança diferencial" não é apenas um ajuste natural para descrever como o mecanismo [[Prototype]] do JavaScript
realmente funciona.

Você pode escolher em preferir a terminologia de "herança diferencial" e o modelo mental, por questão de gosto, mas não há como negar o
fato de que isso se encaixa apenas nas acrobacias mentais dentro de sua mente, e não do comportamento físico da engine.

"Construtores"
Vamos voltar à um código visto mais cedo:

function Foo() {

// ...

var a = new Foo();

O que exatamente nos leva a pensar que Foo é uma "classe"?

Primeiramente, nós vimos o uso da palavra-chave new , assim como em linguagens orientadas à classes quando se instânciam classes. Além
disso, parece que estamos na verdade executando um método construtor de uma classe, porque Foo() é o método de que de fato é
chamado, assim como construtores reais de classes são chamados quando se instância está classe.

Para aumentar a confusão sobre a semântica de um "construtor", o objeto arbitrariamente rotulado de Foo.prototype tem outro truque na
manga. Considere este código:

function Foo() {

// ...

Foo.prototype.constructor === Foo; // true

var a = new Foo();

a.constructor === Foo; // true

O objeto Foo.prototype por padrão (no momento da declaração na linha 1 do código) recebe uma propriedade pública, não enumerável
(veja Capítulo 3) chamada .constructor , e essa propriedade é uma referência de volta à função ( Foo neste caso) em que o objeto está
associado. Além disso, vemos que o objeto a criado através da chamada do "construtor" new Foo () parece ter também uma propriedade
nele chamada .constructor que similarmente aponta para "a função que o criou".

Nota: Isso não é realmente verdade. a não possui uma propriedade .constructor , e ainda que a.constructor de fato funcione para a
função Foo , "construtor" não significa realmente "foi construído por", como parece. Nós explicaremos essa estranha situação em breve.

Ah sim, outra coisa... por convenção no mundo JavaScript, "classes" são nomeadas com letra maiúscula, então o fato de ser Foo ao invés de
foo é uma forte pista de que a intenção é de que seja uma "classe". Isso é totalmente óbvio pra você, certo!?

Nota: Essa convenção é tão forte que muitos linters de JS de fato reclamam se você chama new em um método com letra minúscula, ou se
não chamamos new em uma função que por acaso comece com uma letra maiúscula. Isso meio que confunde a ideia de que lutamos tanto
para obter uma (falsa) "orientação à classe" do jeito certo em JavaScript em que criamos regras de linter para assegurar que usamos letras
maiúsculas, mesmo que letra maiúscula não signifique absolutamente nada para o motor (engine) JS.

Construtor ou Chamada?

No código acima, é tentador pensar que Foo é um "construtor", porque nós o chamamos com new e observamos que isso "constrói" um
objeto.

Na realidade, Foo não é mais "construtor" que qualquer outra função em seu programa. Funções por si só não são construtores.
Entretanto, quando se coloca a palavra-chave new em frente à chamada de uma função normal, isso faz com que a função chame uma
"chamada de construtor". Na verdade, new meio que se apropria de qualquer função normal e a chama de uma forma que constrói um
objeto, junto com qualquer outra coisa que irá fazer.

Por exemplo:
function NothingSpecial() {

console.log( "Don't mind me!" );


}

var a = new NothingSpecial();

// "Don't mind me!"

a; // {}

NothingSpecial é apenas uma função normal, mas quando chamada com new , ela constrói um objeto, quase como um efeito colateral, que
nós por acaso atribuímos à a . A chamada foi uma chamada de construtor, mas NothingSpecial não é, por si só, um construtor.

Em outras palavras, em JavaScript, é mais apropriado dizer que um "construtor" é qualquer função chamada através de uma palavra-chave
new na frente.

Funções não são construtores, mas chamadas de funções são "chamadas de construtores" se e apenas se new é utilizado.

Mecânicas
São esses os únicos gatilhos comuns para as malfadadas discussões sobre "classes" em JavaScript?

Não exatamente. Desenvolvedores JS se esforçaram o máximo possível para simular orientação à classes:

function Foo(name) {

this.name = name;

Foo.prototype.myName = function() {

return this.name;

};

var a = new Foo( "a" );

var b = new Foo( "b" );

a.myName(); // "a"

b.myName(); // "b"

Este código mostra dois truques adicionais "orientados à classe" em jogo:

1. this.name = name : adiciona a propriedade .name em cada objeto ( a e b , respectivamente; veja Capítulo 2 sobre ligação this ),
similar em como instâncias de classes encapsulam dados.

2. Foo.prototype.myName = ... : talvez seja a técnica mais interessante, ela adiciona uma propriedade (função) para o objeto
Foo.prototype . Agora, talvez surpreendentemente, a.myName() funciona. Como?

No código acima, é fortemente tentador pensar que quando a e b são criados, as propriedades/funções no objeto Foo.prototype são
copiadas sobre cada objeto a e b . Entretanto, não é isso o que acontece.

No início deste capítulo, nós explicamos a ligação [[Prototype]] , e cada passo que ela segue para realizar as buscas caso uma referência
da propriedade não seja diretamente encontrada no objeto, como parte do padrão do algoritmo [[Get]] .

Então, por virtude do que criamos, cada a e b acabam com uma ligação [[Prototype]] interna com Foo.prototype . Quando myName
não é encontrada em a ou b , respectivamente, é encontrada em seu lugar (através de delegação, veja Capítulo 6) em Foo.prototype .

"Construtor" Redux

Você se lembra da discussão mais cedo sobre a propriedade .constructor , e como ela faz parecer que a.constructor === Foo sendo
verdadeiro significa que a tem de fato uma propriedade .constructor em si, apontando para Foo ? Isso não está correto.

Essa é apenas uma infeliz confusão. Na realidade, a refêrencia de .constructor também é delegada para Foo.prototype , que acontece de,
por padrão, ter um .constructor que aponta para Foo .

Parece muito conveniente que um objeto a "construído por" Foo deveria ter acesso à propriedade .constructor que aponta para Foo .
Mas isso nada mais é que uma sensação falsa de segurança. É um feliz acidente, quase tangencial, que a.constructor por acaso aponta
para Foo através desta delegação [[Prototype]] padrão. Existem diversas maneiras da malfadada suposição de que .constructor
significa "foi constrúido por" voltar para te assombrar.
Uma delas é que, a propriedade .constructor em Foo.prototype só está lá por padrão no objeto criado quando a função Foo é
declarada. Se você cria um novo objeto, e substitui a referência padrão do objeto .prototype de uma função, o novo objeto não irá
magicamente obter um .constructor em si.

Considere:

function Foo() { /* .. */ }

Foo.prototype = { /* .. */ }; // cria um novo objeto prototype

var a1 = new Foo();

a1.constructor === Foo; // false!

a1.constructor === Object; // true!

Object(..) não "construiu" a1 , construiu? Certamente parece que Foo() "construiu". Muitos desenvolvedores pensam que Foo() estava
fazendo a construção, mas esse pensamento cai por terra quando se pensa que "construtor" significa "foi construído por", porque seguindo
essa lógica, a1.constructor deveria ser Foo , mas não é!

O que está acontecendo? a1 não possui nenhuma propriedade .constructor , então ele delega até a cadeia [[Prototype]] para
Foo.prototype . Mas este objeto também não tem um .constructor (como o objeto padrão Foo.prototype teria!), então ele continua
delegando, dessa vez até Object.prototype , o topo da cadeia de delegação. Este objeto de fato possui um .constructor em si, que aponta
para a função nativa Object(..) .

Equívoco encontrado.

Claro, você pode adicionar .constructor de volta ao objeto Foo.prototype , mas isso requer trabalho manual, especialmente se você quer
que tenha um comportamento nativo e que seja não enumerável (veja Capítulo 3).

Por exemplo:

function Foo() { /* .. */ }

Foo.prototype = { /* .. */ }; // cria um novo objeto prototype

// É preciso "consertar" de forma apropriada a propriedade

// `.constructor` que está faltando no novo objeto

// servindo como `Foo.prototype`.

// Veja o Capítulo 3 sobre `defineProperty(..)`.

Object.defineProperty( Foo.prototype, "constructor" , {

enumerable: false,

writable: true,

configurable: true,

value: Foo // aponta `.constructor` para `Foo`

} );

Trata-se de muito trabalho manual só para poder consertar o .constructor . Além disso, nós estamos realmente perpetuando o equívoco
de que "construtor" significa "foi construído por". Essa é uma ilusão bem cara.

O fato é, .constructor em um objeto aponta arbitrariamente, por padrão, para uma função que, recíprocamente, possui uma referência de
volta ao objeto -- uma referência que se chama .prototype . As palavras "constructor" e "prototype" neste caso possuem um significado
padrão que podem ou não se manter o mesmo mais tarde. A melhor coisa a se fazer é lembrar a si mesmo, "construtor não significa
construído por".

.constructor não é uma propriedade imutável mágica. Ela é não enumerável (veja código abaixo), mas seu valor é gravável (pode ser
alterado), e além disso, você pode adicionar ou sobreescrever (de forma intencional ou acidental) uma propriedade com nome de
constructor em qualquer objeto que esteja em qualquer cadeia [[Prototype]] , com o valor que julgar necessário.

Em virtude de como o algoritmo [[Get]] percorre a cadeia [[Prototype]] , uma referência da propriedade .constructor encontrada em
qualquer lugar pode se resolver de forma bem diferente do que se espera.

Vê como é arbitrário seu real significado?

O resultado? Alguma referência arbitrária de objeto-propriedade como a1.constructor não pode ser confiada como sendo a referência de
função que se assume como padrão. Além disso, como veremos em breve, apenas por simples omissão, a1.constructor pode até apontar
pra algum lugar bem surpreendente e insensível.
a1.constructor não é nada confiável, e é uma referência insegura para se colocar em seu código. Geralmente, essas referências devem ser
evitadas quando possível.

"Herança (Prototípica)"
Nós vimos algumas aproximações de mecânicas de "classe" como normais na escrita de programas JavaScript. Mas as "classes" JavaScript
seriam vazias se não tivéssemos também uma aproximação de "herança".

Na verdade, nós já vimos o mecânismo que geralmente é chamado de "herança prototípica" funcionando quando a foi capaz de "ser
herdada de" Foo.prototype ,e por isso recebe acesso à função myName() . Mas nós tradicionalmente pensamos em "herança" como sendo
uma relação entre duas "classes", e não entre "classe" e "instância".

Reveja essa figura exibida anteriormente, que mostra não só a delegação de um objeto (também conhecido como "instância") a1 para o
objeto Foo.prototype , como também de Bar.prototype para Foo.prototype , o que assemelha-se ao conceito de herança de classe
Parent-Child. Assemelha-se, exceto claro pela direção das setas, que mostram que são ligações via delegações ao invés de operações de
cópia.

Abaixo temos o típico código "estilo protótipo" que cria ligações como essas:

function Foo(name) {

this.name = name;

Foo.prototype.myName = function() {

return this.name;

};

function Bar(name,label) {

Foo.call( this, name );

this.label = label;

// aqui, nós criamos um novo `Bar.prototype`


// ligado à `Foo.prototype`

Bar.prototype = Object.create( Foo.prototype );

// Cuidado! Agora `Bar.prototype.constructor` se foi,

// e pode precisar ser manualmente "consertado" se

// você tiver o hábito de confiar em propriedades deste tipo!

Bar.prototype.myLabel = function() {

return this.label;

};

var a = new Bar( "a", "obj a" );

a.myName(); // "a"

a.myLabel(); // "obj a"

Nota: Para entender por que this aponta para a no código acima, veja o Capítulo 2.
A parte importante é Bar.prototype = Object.create( Foo.prototype ) . Object.create(..) cria um "novo" objeto do nada, e liga o
[[Prototype]] interno deste objeto com o objeto que você especificar ( Foo.prototype neste caso).

Em outras palavras, essa linha diz: "faça um novo objeto 'Bar ponto prototype' que seja ligado à 'Foo ponto prototype'."

Quando function Bar() { .. } é declarado, Bar , como qualquer outra função, tem uma ligação .prototype com seu objeto padrão. Mas
esse objeto não está ligado à Foo.prototype como queremos que seja. Então, nós criamos um novo objeto que é ligado como queremos,
efetivamente jogando fora seu objeto original que foi incorretamente ligado.

Nota: Um equívoco ou confusão comum aqui é que qualquer uma das abordagens a seguir também podem funcionar, mas não da forma
que se espera:

// não funciona como você quer!

Bar.prototype = Foo.prototype;

// funciona mais ou menos como quer, mas com


// efeitos colaterais que você provavelmente não quer :(

Bar.prototype = new Foo();

Bar.prototype = Foo.prototype não cria um novo objeto para ser ligado à Bar.prototype . Apenas faz com que Bar.prototype seja outra
referência para Foo.prototype , o que efetivamente liga Bar diretamente com o mesmo objeto que Foo se conecta: Foo.prototype . Isso
significa que quando você começa a atribuir, como em Bar.prototype.myLabel = ... , você está modificando não um objeto separado mas
o próprio objeto Foo.prototype compartilhado, o que poderia afetar qualquer objeto ligado à Foo.prototype . Isso é quase certo que não
seja o que quer. Se é o que você quer, então você provavelmente nem sequer precisa de Bar , e deve apenas usar Foo e simplificar seu
código.

Bar.prototype = new Foo() cria de fato um novo objeto que é devidamente ligado à Foo.prototype como gostaríamos. Mas, ele usa a
"chamada de construtor" Foo(..) para fazer isso. Se esta função tiver qualquer efeito colateral (como logging, mudança de estado, registro
contra outros objetos, adição de propriedades para this , etc.), esses efeitos colaterais acontecerão no momento dessa ligação (e
provavelmente no objeto errado!), ao invés de apenas quando eventuais "descendentes" de Bar() são criados, como provavelmente é
esperado.

Então, acabamos usando Object.create(..) para criar um novo objeto que esteja ligado da forma apropriada, mas sem os efeitos
colaterais de se chamar Foo(..) . O lado ligeiramente negativo disso é que temos que criar um novo objeto, jogando fora o antigo, ao invés
de modificar o objeto padrão existente que havíamos criado.

Seria bom se existisse uma maneira padrão e confiável de se modificar a ligação de um objeto existente. Antes do ES6, existia uma maneira
fora do padrão e não inteiramente cross-browser, através da propriedade .__proto__ , que é configurável. ES6 adiciona o utilitário auxiliar
Object.setPrototypeOf(..) , que consegue fazer esse truque de forma mais padrão e previsível.

Compare as técnicas pré-ES6 e no padrão ES6 de ligar Bar.prototype à Foo.prototype , lado à lado:

// pré-ES6

// joga fora o `Bar.prototype` padrão existente

Bar.prototype = Object.create( Foo.prototype );

// ES6+

// modifica o `Bar.prototype` existente

Object.setPrototypeOf( Bar.prototype, Foo.prototype );

Ignorando a pequena desvantagem na performance (jogando fora um objeto que mais tarde será lixo coletado) da abordagem
Object.create(..) , ela é um pouco menor e talvez seja um pouco mais fácil de se ler que a abordagem em ES6+. Mas é uma provável
limpeza na síntaxe de qualquer forma.

Inspecionando o Relacionamento de "Classes"


E se você tiver um objeto como a e quer descobrir para qual objeto (se houver algum) ele delega? Inspecionar uma instância (que é só um
objeto em JS) para buscar sua herança ancestral (que é uma ligação por delegação em JS) é frequentemente chamado de introspecção (ou
reflexão) em ambientes tradicionais orientados à classes.

Considere:
function Foo() {

// ...

Foo.prototype.blah = ...;

var a = new Foo();

Como introspectamos a para descobrir sua "ancestralidade" (ligação por delegação)? A primeira abordagem abraça a confusão de "classe":

a instanceof Foo; // true

O operador instanceof pega um objeto simples como seu operando à esquerda e uma função como seu operando à direita. A questão
que instanceof responde é: em toda a cadeia [[Prototype]] de a , o objeto arbitrariamente apontado por Foo.prototype aparece
alguma vez?

Infelizmente, isso significa que você pode apenas questionar sobre a "ancestralidade" de algum objeto ( a ) se você tiver alguma função
( Foo , que está anexada à referência .prototype ) para poder testar. Se você tiver dois objetos arbitrários, digamos a e b , e quiser
descobrir se os objetos estão relacionados entre si através da cadeia [[Prototype]] , o instanceof sozinho não poderá te ajudar.

Nota: Se você usar o utilitário nativo .bind(..) para criar uma função hard-bound (veja Capítulo 2), a função criada não terá uma
propriedade .prototype . Usando instanceof com este tipo de função substitui de forma transparente o .prototype da função de destino
da qual a função hard-bound foi criada.

É muito comum de se utilizar funções hard-bound como "chamadas de construtor", mas ao invés disso se você o fizer, irá se comportar
como a função de destino original que foi invocada, o que significa que usar instanceof com uma função hard-bound também faz com que
se comporte de acordo com a função original.

Este código ilustra o quão rídicula é a tentativa de racionalizar sobre as relações entre dois objetos usando semânticas de "classe" e
instanceof :

// utilitário de ajuda para ver se `o1`

// está relacionado (delegado) à `o2`

function isRelatedTo(o1, o2) {

function F(){}

F.prototype = o2;

return o1 instanceof F;

var a = {};

var b = Object.create( a );

isRelatedTo( b, a ); // true

Dentro de isRelatedTo(..) , nós pegamos uma função descartável F , reatribuímos sua propriedade .prototype para apontar
arbitrariamente para algum objeto o2 , e em seguida perguntamos se o1 é uma "instância de" F . Obviamente o1 não é realmente
herdada ou descende de ou até mesmo construída a partir de F , então deveria ser claro o porquê desse tipo de exercício ser tolo e
confuso. O problema aparece devido à embaraçosa semântica de classe forçada em cima do JavaScript, neste caso revelada através da
semântica de instanceof .

A segunda, e muito mais clara, abordagem para refletir [[Prototype]] é:

Foo.prototype.isPrototypeOf( a ); // true

Perceba que neste caso, nós não nos importamos realmente (ou sequer precisamos) de Foo , nós só precisamos de um objeto (em nosso
caso, arbitrariamente rotulado de Foo.prototype ) para testar contra outro objeto. A questão que isPrototypeOf(..) responde é: em toda
a cadeia [[Prototype]] de a , alguma vez Foo.prototype é encontrado?

Mesma questão, e exatamente a mesma resposta. Mas nesta segunda abordagem, nós não precisamos de verdade da indireção de
referenciar uma função ( Foo ) cuja propriedade .prototype será automaticamente consultada.

Nós só precisamos de dois objetos para inspecionar uma relação entre eles. Por exemplo:
// Simplesmente: `b` aparece em algum lugar

// da cadeia [[Prototype]] de `c`?

b.isPrototypeOf( c );

Perceba, essa abordagem não requer nenhuma função ("classe"). Ela só usa as referências de objeto diretamente para b e c , e pergunta
sobre sua relação. Em outras palavras, nosso utilitário isRelatedTo(..) acima é nativo da linguagem, e é chamado de isPrototypeOf(..) .

Nós também podemos recuperar diretamente o [[Prototype]] de um objeto. Em ES5, a maneira padrão de fazer isso é:

Object.getPrototypeOf( a );

E você irá notar que a referência do objeto é o que esperamos:

Object.getPrototypeOf( a ) === Foo.prototype; // true

Muitos browsers (não todos!) também suportam há muito tempo uma alternativa fora do padrão para acesso ao [[Prototype]] interno:

a.__proto__ === Foo.prototype; // true

O estranho como .__proto__ (que não fazia parte do padrão até ES6!) "magicamente" recupera o [[Prototype]] interno de um objeto
como uma referência, o que é bem útil se você quer inspecionar (ou até cruzar: .__proto__.__proto__... ) diretamente a cadeia.

Assim como vimos anteriormente com .constructor , .__proto__ não existe de fato no objeto que você está inspecionando ( a no nosso
exemplo atual). Na verdade, existe (não enumerável; veja Capítulo 2) no Object.prototype nativo, junto com outros utilitários comuns
( .toString() , .isPrototypeOf(..) , etc).

Além disso, .__proto__ parece com uma propriedade, mas é mais apropriado considerá-la como um getter/setter (veja Capítulo 3).

À grosso modo, nós podemos visualizar .__proto__ implementada (veja Capítulo 3 para definições de propriedade de objetos) desta
forma:

Object.defineProperty( Object.prototype, "__proto__", {

get: function() {

return Object.getPrototypeOf( this );

},

set: function(o) {

// setPrototypeOf(..) a partir do ES6

Object.setPrototypeOf( this, o );

return o;

} );

Então, quando nós acessamos (recuperamos o valor de) a.__proto__ , é como se chamassemos a.__proto__() (chamando a função getter).
Essa chamada de função possui a como seu this mesmo que a função getter exista no objeto Object.prototype (veja Capítulo 2 para as
regras de vínculo de this ), então é como dizer Object.getPrototypeOf( a ) .

.__proto__ também é uma propriedade configurável, assim como o uso de Object.setPrototypeOf(..) exibido anteriormente. Entretanto,
geralmente você não deve mudar o [[Prototype]] de um objeto existente.

Existem algumas técnicas avançadas e muito complexas utilizadas no fundo de alguns frameworks que permitem truques como criar
"subclasses" em um Array , mas em geral isso não é algo visto como uma boa prática de programação, já que leva há um entendimento e
manutenção muito mais difíceis do código.

Nota: A partir do ES6, a palavra-chave class permitirá algo que que se aproxima da criação de "subclasses" para utilitários nativos como o
Array . Veja o Apêndice A para a discussão da síntaxe class adicionada no ES6.

A única pequena exceção (como mencionado antes) seria configurar o [[Prototype]] de um objeto .prototype como uma função padrão
para referenciar algum outro objeto (além de Object.prototype ). Isso evitaria substituir inteiramente o objeto padrão com um novo objeto
ligado. Caso contrário, é melhor tratar a ligação [[Prototype]] do objeto como uma característica de somente leitura para facilitar a
leitura de seu código mais tarde.
Nota: A comunidade JavaScript cunhou um termo não oficial para o underscore duplo, especificamente para propriedades como o favorito
__proto__ : "dunder". Então, os "descolados" em JavaScript geralmente pronunciam __proto__ como "dunder proto".

Ligações de Objeto
Como já vimos, o mecânismo [[Prototype]] é uma ligação interna que existe em um objeto que referencia algum outro objeto.

Essa ligação é (primariamente) exercida quando uma propriedade/método é feita contra o primeiro objeto, e tal propriedade/método não
existe. Nesse caso, a ligação [[Prototype]] diz ao motor para procurar pela propriedade/método no objeto ligado. Por sua vez, se esse
objeto não puder completar a busca, seu [[Prototype]] é seguido, e assim por diante. Essa série de ligações entre formas de objetos é o
que chamados de "cadeia de protótipos".

create() Criando ligações


Nós revelamos em detalhes porque o mecanismo [[Prototype]] de JavaScript não é o mesmo que classes, e vimos como ele ao invés disso
cria ligações entre os objetos.

Qual o sentido do mecânismo [[Prototype]] ? Por que é tão comum para desenvolvedores JS fazer tanto esforço (emulando classes) em
seu código para criar essas ligações?

Lembra-se que dizemos bem cedo neste capítulo que Object.create(..) seria o herói? Agora, estamos preparados para ver como.

var foo = {

something: function() {

console.log( "Tell me something good..." );

};

var bar = Object.create( foo );

bar.something(); // Tell me something good...

Object.create(..) cria um novo objeto ( bar ) ligado ao objeto que especificamos ( foo ), o que nos dá todo o poder (delegação) do
mecânismo [[Prototype]] , mas sem qualquer complicação desnecessária de funções new atuando como classes e chamadas de
construtores, confundindo referências .prototype e .constructor , ou qualquer outra coisa a mais.

Nota: Object.create(null) cria um objeto que possui uma ligação [[Prototype]] vazia (também conhecida como null ), e portanto esse
objeto não pode delegar à lugar nenhum. Como um objeto desse tipo não tem uma cadeia de protótipo, o operador instanceof (explicado
mais cedo) não tem nada pra checar, então sempre irá retornar false . Esses objetos especiais de [[Prototype]] vazios são
frequentemente chamados de "dicionários" já que eles são tipicamente usados apenas para guardar dados em propriedades, na maioria das
vezes porque eles não tem nenhum efeito surpresa vindo de qualquer propriedade/função delegada na cadeia [[Prototype]] , e por isso
servem somente para armazenagem de dados.

Nós não precisamos de classes para criar relacionamentos relevantes entre dois objetos. A única coisa que realmente devemos nos
importar são objetos ligados entre si por delegação, e Object.create(..) nos dá essa ligação sem toda a desnecessária complexidade de
uma classe.

Object.create() com Polyfill

Object.create(..) foi adicionado no ES5. Você pode precisar de suporte a ambientes pré-ES5 (como os antigos Internet Explorers), então
vamos dar uma olhada em um simples polyfill parcial para Object.create(..) que nos dá a capacidade que precisamos mesmo nesses
ambientes antigos de JS:

if (!Object.create) {

Object.create = function(o) {

function F(){}

F.prototype = o;

return new F();

};

}
Esse polyfill funciona atrávés do uso da função descartável F e sobrescrevendo sua propriedade .prototype para apontar para o objeto
que desejamos criar uma ligação. Então nós usamos a construção new F() para criar um novo objeto que será ligado conforme
especificamos.

Esse uso do Object.create(..) é de longe o mais comum, porque é a parte dele em que pode ser aplicado o polyfill. Existe uma série de
funcionalidades adicionais que o Object.create(..) nativo de ES5 fornece, e que não podem ter o polyfill aplicado em ambientes pré-ES5.
Devido à isso, sua capacidade é muito menos explorada. Para tornar isso mais claro, vamos dar uma olha nessa funcionalidade adicional:

var anotherObject = {

a: 2

};

var myObject = Object.create( anotherObject, {

b: {

enumerable: false,

writable: true,

configurable: false,

value: 3

},

c: {

enumerable: true,

writable: false,

configurable: false,

value: 4

} );

myObject.hasOwnProperty( "a" ); // false


myObject.hasOwnProperty( "b" ); // true

myObject.hasOwnProperty( "c" ); // true

myObject.a; // 2

myObject.b; // 3

myObject.c; // 4

O segundo argumento para o Object.create(..) especifica nomes de propriedades para serem adicionadas ao novo objeto criado, através
da declaração do descritor de propriedade (veja Capítulo 3) de cada nova propriedade. Devido ao fato de não ser possível aplicar o pollyfill
em descritores de propriedades pré-ES5, também não é possível aplicá-lo nessa funcionalidade adicional em Object.create(..) .

Em grande parte do uso de Object.create(..) também se faz uso de uma série de funcionalidades aptas para realizar o polyfill, então a
maioria dos desenvolvedores não se importam em ter um polyfill parcial em ambientes pré-ES5.

Alguns desenvolvedores possuem uma visão mais rígida, de que nenhuma função pode ter o polyfill aplicado se não for por completo.
Como Object.create(..) é um desses utilitários parcialmente aptos para o polyfill, essa perspectiva mais limitada diz que se você precisa
de qualquer funcionalidade de um Object.create(..) em um ambiente pré-ES5, ao invés de polyfill, deve-se utilizar um utilitário
customizado, e evitar completamente o uso do nome Object.create . Você deve ao invés disso definir seu próprio utilitário, como em:

function createAndLinkObject(o) {

function F(){}

F.prototype = o;

return new F();

var anotherObject = {

a: 2

};

var myObject = createAndLinkObject( anotherObject );

myObject.a; // 2

Eu não partilho dessa visão mais rígida. Eu endosso completamente o uso de Object.create(..) mesmo sendo parcialmente apto ao
polyfill como mostrado acima, e utilizá-lo em seu código mesmo em situações pré-ES5. Fica à seu critério para decidir o que é melhor para
você.

Ligações como Fallbacks?


Pode parecer tentador pensar que essas ligações entre objetos fornecem primariamente um tipo de fallback para propriedades ou métodos
que "sumiram". Apesar de ser possível chegar à esse resultado, eu não penso que represente a forma correta de se pensar sobre
[[Prototype]] .

Considere:

var anotherObject = {

cool: function() {

console.log( "cool!" );

};

var myObject = Object.create( anotherObject );

myObject.cool(); // "cool!"

Esse código irá funcionar em virtude do [[Prototype]] , mas se você escreveu dessa forma para que anotherObject agisse como um
fallback só por garantia myObject não poderá manipular alguma propriedade/método que algum desenvolvedor pode tentar chamar, as
chances são de seu software se tornar um pouco mais "mágico" e mais difícil de se entender e manter.

Isso não quer dizer que não existam casos onde fallbacks sejam um design pattern apropriado, mas não é muito comum ou idiomático em
JS, então se perceber que está fazendo isso, você deve parar pra pensar se isso está realmente apropriado e sensível ao design.

Nota: Em ES6, uma funcionalidade avançada chamada de Proxy foi introduzida e fornece um tipo de comportamento de "método não
encontrado". Proxy está além do escopo deste livro, mas será abordado em detalhes em um futuro livro da série "You Don't Know JS".

Não perca um importante ponto mas que pode ter diferentes nuances por aqui.

Projetando um software onde você tenha intenção de que um desenvolvedor, por exemplo, chame myObject.cool() e tenha isso
funcionando mesmo que não exista o método cool() em myObject , introduz um tipo de "mágica" no desenho de sua API que pode
causar surpresa em futuros desenvolvedores que irão realizar uma manutenção em seu software.

Você pode no entanto desenhar sua API com menos "mágica", e ainda assim tirar vantagem do poder da ligação [[Prototype]] .

var anotherObject = {

cool: function() {

console.log( "cool!" );

};

var myObject = Object.create( anotherObject );

myObject.doCool = function() {

this.cool(); // delegação interna!

};

myObject.doCool(); // "cool!"

Aqui, nós chamamos myObject.doCool() , que é o método que de fato existe em myObject , fazendo com que o desenho de sua API seja
mais explícito (menos "mágico"). Internamente, nossa implementação segue o design pattern de delegação (veja Capítulo 6), tirando
vantagem da delegação de [[Prototype]] para anotherObject.cool() .

Em outras palavras, delegação tende à ser menos surpreendente/confusa se for um detalhe de implementação interno ao invés de
abertamente exposto no desenho da interface de sua API. Nós iremos explorar delegação em muitos detalhes no próximo capítulo.

Revisão (TL;DR)
Ao tentar acessar uma propriedade em um objeto que não possui essa propriedade, a ligação [[Prototype]] interna do objeto irá definir
onde a operação [[Get]] (veja Capítulo 3) irá procurar na sequência. Essa ligação em cascata de um objeto para outro essencialmente
define uma "cadeia de protótipos" (de certa forma similar à cadeia de escopo aninhada) de objetos à serem percorridos para resolução da
propriedade.
Todos os objetos normais possuem o Object.prototype nativo como o topo da cadeia de protótipos (como o escopo global na buscar de
escopo), onde a resolução da propriedade será interrompida caso a mesma não seja encontrada em nenhum lugar da cadeia. toString() ,
valueOf() , e muitos outros utilitários comuns existem nesse objeto Object.prototype , o que explica como todos os objetos na linguagem
são capazes de acessá-los.

A maneira mais comum de se ligar dois objetos entre si é usando a palavra-chave new com a chamada de uma função, que entre seus
quatro passos (veja Capítulo 2) cria um novo objeto ligado à outro objeto.

O "outro objeto" à que esse novo objeto está ligado acontece de ser o objeto referenciado pela propriedade arbitrariamente nomeada de
.prototype da função que foi chamada com new . Funções chamadas com new são frequentemente chamadas de "construtores", apesar
do fato de que não estão realmente instanciando uma classe como construtores fazem em linguagens orientadas à classe tradicionais.

Enquanto esses mecânismos JavaScript podem lembrar "instanciamento de classe" e "herança de classe" de linguagens orientadas à classe
tradicionais, a chave para distinguir isso é que em JavaScript, nenhuma cópia é realizada. Ao invés disso, objetos acabam ligados um ao
outro através da cadeia [[Prototype]] interna.

Por uma variedade de motivos, não menos que uma terminologia precedente, "herança" (e "herança prototípica") e todos os termos de
orientação à objetos não fazem nenhum sentido quando considera-se como JavaScript funciona de fato (e não apenas aplicados aos nossos
modelos mentais forçados).

Ao invés disso, "delegação" é um termo muito mais apropriado, porque esses relacionamentos não são cópias mas sim ligações delegadas.
Capítulo 6: Delegação de Comportamentos
No Capítulo 5, nós abordamos detalhadamente o mecânismo [[Prototype]] , e o porquê de ser confuso e inapropriado (apesar das
incontáveis tentativas por quase duas décadas) descrevê-lo como "classe" ou "herança". Vimos à fundo não só sua síntaxe razoavelmente
prolixa ( .prototype sujando o código), mas também as armadilhas (como a surpreendente resolução de .constructor ou a horrível síntaxe
pseudo-polimórfica). E exploramos as variações da abordagem "mixin", que muitas pessoas utilizam para tentar suavizar áreas mais pesadas.

É uma reação comum à essa altura imaginar qual a necessidade de ser tão complexo algo que parece ser tão simples de ser feito. Agora que
abaixamos as cortinas e vimos o quão poluído o código fica, não é uma surpresa a maioria dos desenvolvedores JS nunca mergulharem tão
fundo, e ao invés disso relegarem toda a bagunça para uma biblioteca de "classes" cuidar para eles.

Eu espero que agora você não se contente em encobrir e deixar tais detalhes para uma biblioteca "caixa preta" cuidar. Vamos ver à seguir
como nós podemos e devemos pensar sobre o mecânismo do objeto [[Prototype]] em JS, de uma forma muito mais simples e direta que
a confusão de classes.

Como uma breve revisão de nossas conclusões do Capítulo 5, o mecânismo [[Prototype]] é uma ligação interna que existe em um objeto
que referencia outro objeto.

Essa ligação é exercida quando uma referência de uma propriedade/método é feita contra o primeiro objeto, e tal propriedade/método não
existe. Neste caso, a ligação [[Prototype]] diz ao motor para buscar pela propriedade/método no objeto que está ligado. Por sua vez, caso
o objeto não consiga completar a busca, seu [[Prototype]] é seguido, e assim por diante. Essa série de ligações entre formas de objetos é
chamada de "cadeia de protótipos".

Em outras palavras, o mecânismo atual, a essência do que é importante para a funcionalidade do que podemos fazer com JavaScript, se
resume à objetos sendo ligados à outros objetos.

Essa observação por si só é fundamental e crítica para entender as motivações e abordagens ao longo deste capítulo!

Em Direção ao Design Orientado à Delegação


Para focar adequadamente nossa forma de pensar em como usar o [[Prototype]] da maneira mais direta, nós devemos reconhecer que
isso representa uma diferença fundamental de design pattern em relação às classes (veja Capítulo 4).

Nota: Alguns princípios de design orientado à classes continuam muito válidos, então não jogue fora tudo que sabe (apenas a maior parte!).
Por exemplo, encapsulamento é bem poderoso, e é compátivel (embora não muito comum) com delegação.

Nós devemos tentar mudar nossa forma de pensar do padrão classe/herança para o padrão de delegação de comportamento. Se a maior
parte do que programou em sua educação/carreira pensando em classes, essa maneira pode ser desconfortável ou não parecer natural.
Você pode precisar experimentar fazer esse exercício mental algumas vezes até conseguir pegar o jeito dessa forma tão diferente de se
pensar.

Eu vou orientá-lo através de alguns exercícios teóricos primeiro, e então vamos ver lado à lado em um exemplo mais concreto para te dar
um contexto prático para seu próprio código.

Teoria de Classe
Digamos que temos várias tarefas semelhantes ("XYZ", "ABC", etc) que precisamos modelar em nosso software.

Com classes, a forma de se projetar esse cenário é: definir uma classe geral pai (base) como Task , definindo o comportamento
compartilhado para todas as tarefas "parecidas". Então, você define as classes filhas XYZ e ABC , ambas herdadas de Task , e cada uma
adiciona um comportamento especial para lidar com suas respectivas tarefas.

Mais importante, o design pattern de classes irá encorajá-lo a obter o máximo de herança, você irá querer empregar sobreescrita de
métodos (e polimorfismo), onde você sobreescreve a definição de algum método geral Task em sua tarefa XYZ , talvez até fazendo uso de
super para chamar a versão base desse método ao adicionar mais comportamento à ele. Você provavelmente encontrará alguns lugares
nos quais você pode "abstrair" o comportamento geral da classe pai e especializá-lo (substituí-lo) em suas classes filhas.

Aqui vai um pseudo-código para esse cenário:

class Task {

id;

// construtor `Task()`

Task(ID) { id = ID; }

outputTask() { output( id ); }

class XYZ inherits Task {

label;

// construtor `XYZ()`

XYZ(ID,Label) { super( ID ); label = Label; }

outputTask() { super(); output( label ); }

class ABC inherits Task {

// ...

Agora, você pode instanciar uma ou mais cópias da classe filha XYZ e usar essas instâncias para executar a tarefa "XYZ". Estas instâncias
possuem cópias tanto do comportamento geral definido para a classe Task como também do comportamento especificamente definido
para XYZ . Da mesma forma, instâncias da classe ABC teriam cópias do comportamento de Task e do comportamento específico de ABC .
Após a construção, você geralmente só interage com essas instâncias (e não as classes), já que as instâncias têm cópias de todo o
comportamento necessário para executar a tarefa pretendida.

Teoria da Delegação
Mas agora vamos tentar pensar sobre o mesmo domínio do problema, mas usando delegação de comportamento ao invés de classes.

Primeiro você irá definir um objeto (não uma classe, nem uma function como muitos desenvolvedores JS o levariam à crer) chamado
Task , e ele terá um comportamento concreto em si que inclui métodos utilitários que várias outras tarefas podem usar (leia: delegar para!).
Então, para cada tarefa ("XYZ", "ABC"), você define um objeto para manter esses dados/comportamentos específicos. Você liga seu(s)
objetos(s) de tarefas específicas com o objeto utilitário Task , permitindo que deleguem à ele quando precisam.

Basicamente, você pensa em executar a tarefa "XYZ" como se precisasse de comportamentos de dois objetos irmãos ( XYZ e Task ) para
realizá-lo. Mas, ao invés de precisar compô-los juntos, por meio de cópias de classe, podemos mantê-los em seus objetos separados, e
podemos permitir que o objeto XYZ delegue para Task quando preciso.

Aqui tem um código simples para sugerir como se conseguir isso:

var Task = {

setID: function(ID) { this.id = ID; },

outputID: function() { console.log( this.id ); }

};

// faz `XYZ` delegar para `Task`

var XYZ = Object.create( Task );

XYZ.prepareTask = function(ID,Label) {

this.setID( ID );

this.label = Label;

};

XYZ.outputTaskDetails = function() {

this.outputID();

console.log( this.label );

};

// ABC = Object.create( Task );

// ABC ... = ...

Neste código, Task e XYZ não são classes (ou funções), eles são apenas objetos, XYZ é configurado via Object.create(..) para o
[[Prototype]] delegar para o objeto Task (veja Capítulo 5).

Em comparação com orientação à classes (também conhecido como OO -- orientado à objetos), eu chamo esse estilo de código de "OLOO"
(objetos-ligados-à-outros-objetos). Tudo que realmente nos importa é que o objeto XYZ delega para o objeto Task (assim como também
faz o objeto ABC ).
Em Javascript, o mecânismo [[Prototype]] liga objetos com outros objetos. Não há mecânismos abstratos como "classes" não importa o
quanto tentarem te convencer do contrário. É como remar uma canoa rio acima: você pode fazer isso, mas se você estará escolhendo ir
contra a corrente natural, então obviamente será muito mais difícil de se chegar onde estiver indo.

Algumas outras diferenças para se notar com o código no estilo OLOO:

1. Ambos os membros de dados id e label no exemplo de classe anterior são propriedades de dados diretamente em XYZ (sem estar
em Task ). Em geral, com delegação [[Prototype]] involvida, você quer que o estado esteja em quem delega ( XYZ , ABC ), não em
quem é delegado ( Task ).

2. Com o padrão de classes, nós intencionalmente nomeamos de outputTask tanto na classe pai ( Task ) quanto na filha ( XYZ ), para que
pudessemos tirar vantagem da substituição (polimorfismo). Em delegação de comportamentos, nós fazemos o oposto: nós evitamos
dar o mesmo nome à qualquer coisa sempre que possível em diferentes níveis da cadeia [[Prototype]] (chamado de sombreamento
-- veja Capítulo 5), porque a colisão desses nomes cria uma síntaxe estranha e frágil para se tirar a ambiguidade de suas referências
(veja Capítulo 4), e gostaríamos de evitar isso se pudermos.

Esse padrão exige menos nomes de métodos gerais que tendem à substituir outros métodos e mais nomes descritivos, específicos para
o tipo de comportamento que cada objeto está executando. Isso pode de fato criar códigos mais fáceis de se entender/manter,
porque os nomes dos métodos (não só no local de definição como espalhado por outros códigos) são mais óbvios (se auto
documentando).

3. this.setID (ID); dentro de um método no objeto XYZ primeiro olha em XYZ para setID (..) , mas já que não encontra um
método com esse nome em XYZ , delegação [[Prototype]] significa que ele pode seguir a ligação para Task procurar por setID
(..) , que obviamente o encontra. Além disso, devido às regras de ligação implícitas em chamadas this (veja o Capítulo 2), quando
setID (..) é executado, mesmo que o método tenha sido encontrado em Task , a ligação this para essa chamada de função é XYZ
exatamente como esperávamos e queríamos. Nós vemos a mesma coisa com this.outputID () depois na listagem de código.
Em
outras palavras, os métodos gerais utilitários que existem em Task estão disponíveis para nós enquanto interagimos com XYZ , porque
XYZ pode delegar para Task .

Delegação de comportamentos significa: deixar algum objeto ( XYZ ) fornecer uma delegação (para Task ) para referências de métodos ou
propriedades, caso não sejam encontradas no objeto ( XYZ ).

Esse é um padrão de design extremamente poderoso, muito diferente da ideia de classes pai e filha, herança, polimorfismo, etc. Ao invés de
organizar objetos mentalmente de forma vertical, com as classes Pais fluindo para as classes Filhas, pense em objetos lado à lado, como
pares, com qualquer direção de ligação de delegação entre os objetos, conforme a necessidade.

Nota: O uso de delegação é mais apropriado como um detalhe de implementação interno ao invés de algo exposto diretamente no
desenho da interface da API. No exemplo acima, nós não necessariamente pretendemos com o desenho de nossa API que desenvolvedores
chamem XYZ.setID() (embora seja possível, é claro!). Nós meio que escondemos a delegação como um detalhe interno de nossa API, onde
XYZ.prepareTask(..) delega para Task.setID(..) . Veja a discussão de "Ligações como Fallbacks?" no Capítulo 5 para maiores detalhes.

Delegação Mútua (Não permitida)

Você não pode criar um ciclo onde dois ou mais objetos estão mutualmente delegados um para o outro (bidirecionalmente). Se você fizer
B ligado a A , e então tentar ligar A para B , você receberá um erro.

É uma vergonha (não é terrivelmente surpreendente, mas um pouco irritante) que isso não seja permitido. Se você criasse uma referência a
uma propriedade/método que não exista em nenhum lugar, você teria um loop de recursão infinita no [[Prototype]] . Mas se todas as
referências estiverem estritamente presentes, então B poderia delegar para A , e vice-versa, e isso poderia funcionar. Isso significa que você
poderia usar um dos objetos para delegar para o outro, para várias tarefas. Existem alguns poucos casos de uso onde isso poderia ser útil.

Mas isto não é permitido porque os implementadores dos motores JS observaram que é mais eficiente verificar (e rejeitar!) referências
circulares infinitas uma vez na sua definição, do que precisar ter o impacto de execução dessas verificações toda vez que você procurar por
uma propriedade em um objeto.

Depurado

Vamos abordar brevemente um detalhe sutil que pode ser confuso aos desenvolvedores. No geral, a especificação do JS não controla como
as ferramentas de desenvolvimento do navegador devem apresentar valores/estruturas específicos para uma desenvolvedora, então cada
navegador/motor é livre para interpretar algumas coisas como eles acharem melhor. Dessa forma, navegadores/ferramentas nem sempre
estão de acordo. Especificamente, o comportamentos que iremos examinar é atualmente observado somente no Chrome's Developer Tools.

Considere como esse tradicional estilo de código JS para "construtor de classe" apareceria no console do Chrome Developer Tools:
function Foo() {}

var a1 = new Foo();

a1; // Foo {}

Vamos olhar para a última linha do código: o output computado da expressão a1 , que imprime Foo {} . Se você tentar o mesmo código
no Firefox, provavelmente verá algo como Object {} . Por que a diferença? O que esses outputs significam?

Chrome está essencialmente dizendo "{} é um objeto vazio que foi contruído por uma função com nome 'Foo'". Firefox está dizendo "{} é um
objeto vazio da construção geral de Object". A sutil diferença é que o Chrome está rastreando ativamente, como uma propriedade interna, o
nome da função que realmente fez a construção, enquanto outros navegadores não fazem o rastreamento dessa informação adicional.

Seria tentador tentar explicar isso com o funcionamento do JavaScript:

function Foo() {}

var a1 = new Foo();

a1.constructor; // Foo(){}

a1.constructor.name; // "Foo"

Então, é assim que o Chrome está mostrando "Foo", por uma simples olhada na .constructor.name do objeto? Confusamente, a resposta é
tanto "sim" quanto "não".

Considere este código:

function Foo() {}

var a1 = new Foo();

Foo.prototype.constructor = function Gotcha(){};

a1.constructor; // Gotcha(){}

a1.constructor.name; // "Gotcha"

a1; // Foo {}

Mesmo que nós mudemos a1.constructor.name para legitimamente ser alguma outra coisa ("Gotcha"), o console do Chrome ainda vai usar
o nome "Foo".

Então, parece que a resposta da pergunta anterior (o Chrome usa .constructor.name ?) é não, ele deve olhar algum outro lugar,
internamente.

Mas, não tão rápido! Vamos ver como esse tipo de comportamento funciona com código estilo OLOO:

var Foo = {};

var a1 = Object.create( Foo );

a1; // Object {}

Object.defineProperty( Foo, "constructor", {

enumerable: false,

value: function Gotcha(){}

});

a1; // Gotcha {}

Ah-ha! Gotcha! Aqui, o console do Chrome realmente achou e usou a .constructor.name . Na verdade, enquanto escrevia esse livro, esse
exato comportamento era identificado no Chrome como um bug, e no momento que você estiver lendo isso, ele pode ter sido corrigido.
Então pode ser que você veja a1; // Object {} corretamente.

Deixando o bug de lado, o rastreamento interno (aparentemente somente para propósitos de depuração) de "constructor name" que o
Chrome faz (mostrado nos trechos anteriores) é uma extensão intencional do Chrome, além do que a especificação do JS exige.
Se você não usar um "constructor" para criar seus objetos, como desencorajamos com o estilo de código OOLO aqui nesse capítulo, então
você terá objetos que o Chrome não rastreia um "constructor name" interno, e esses objetos serão exibidos corretamente como "Object {}",
significando "objeto gerado pelo construtor Object()".

Não pense que isso representa uma desvantagem do estilo de código OLOO. Quando você escreve código com OLOO e delegação de
comportamento como seu padrão de design, quem "construiu" (isso é, qual função foi chamada com new ?) algum objeto é um detalhe
irrelevante. O rastreamento interno de "constructor name" específico do Chrome só é realmente útil se você adotar totalmente o estilo de
código de "classe", mas é discutível se, no lugar, você adotar delegação de OLOO.

Modelos Mentais Comparados


Agora que você pode ver a diferença entre os padrões de design "classe" e "delegação", pelo menos teoricamente, vamos ver as implicações
que esses padrões de design tem nos modelos mentais que usamos para pensar sobre nosso código.

Nós vamos examinar um código mais hipotético ("Foo", "Bar"), e comparar as duas formas (OO vs. OLOO) de se implementar o código. O
primeiro trecho usa o clássico estilo OO (com "prototype"):

function Foo(who) {
this.me = who;

Foo.prototype.identify = function() {

return "I am " + this.me;

};

function Bar(who) {
Foo.call( this, who );

Bar.prototype = Object.create( Foo.prototype );

Bar.prototype.speak = function() {

alert( "Hello, " + this.identify() + "." );

};

var b1 = new Bar( "b1" );

var b2 = new Bar( "b2" );

b1.speak();

b2.speak();

A classe pai Foo , herdada pela classe filha Bar , é então instanciada duas vezes como b1 e b2 . O que nós temos é b1 delegando para
Bar.prototype que delega para Foo.prototype . Isso deve parecer bastante familiar para você, nesse momento. Nada muito inovador
acontecendo.

Agora, vamos implementar exatamente a mesma funcionalidade usando o estilo de código OLOO:

var Foo = {

init: function(who) {

this.me = who;

},

identify: function() {

return "I am " + this.me;

};

var Bar = Object.create( Foo );

Bar.speak = function() {

alert( "Hello, " + this.identify() + "." );

};

var b1 = Object.create( Bar );

b1.init( "b1" );

var b2 = Object.create( Bar );

b2.init( "b2" );

b1.speak();

b2.speak();
Nós pegamos exatamente as mesmas vantagens da delegação via [[Prototype]] de b1 para Bar para Foo como nós fizemos no
exemplo anterior entre b1 , Bar.prototype , e Foo.prototype . Nós ainda temos os mesmos 3 objetos ligados entre si.

Mas, mais importante, nós simplificamos bastante todas as outras coisas, porque agora nós apenas ligamos objetos uns aos outros, sem
precisar de toda a confusão de coisas que parecem (mas não se comportam!) como classes, com contrutores e prototypes e chamadas new .

Pergunte a si mesmo: se eu posso ter a mesma funcionalidade com código OLOO como eu tenho com código estilo "class", mas OLOO é
mais simples e tem menos coisas para me preocupar, OLOO é melhor?

Vamos examinar os modelos mentais involvidos entre esses dois exemplos.

Primeiro, o trecho de código no estilo de classe implica esse modelo mental de entidades e seus relacionamentos:

Na verdade, isso é um pouco desleal/ilusório, porque mostra vários detalhes extras que você tecnicamente não precisa saber o tempo todo
(embora você precise entendê-los!). Um deles é que é uma série bastante complexa de relacionamentos. Mas outra coisa é: se você dedicar
tempo para seguir essas setas de relacionamentos, há uma incrível quantidade de consistência interna nos mecanismos do JS.

Por exemplo, a habilidade de uma função JS acessar call(..) , apply(..) , e bind(..) (veja o Capítulo 2) é porque funções em si são
objetos, e funções-objeto também tem um vínculo de [[Prototype]] , para o objeto Function.prototype , que define esses métodos
padrões que qualquer função-objeto pode usar por delegação. JS pode fazer essas coisas, e você também pode!.

OK, agora vamos olhar para uma versão ligeiramente simplificada desse diagrama que é um pouco mais "justa" para comparação -- mostra
apenas as entidades e relacionamentos relevantes.
Ainda é bem complexo, né? As linhas tracejadas estão descrevendo os relacionamentos implícitos de quando você define a "herança" entre
Foo.prototype e Bar.prototype e ainda não corrigiu a referência da propriedade .constructor ausente (veja "Constructor Redux" no
Capítulo 5). Mesmo com essas linhas tracejadas removidas, o modelo mental ainda é um malabarismo muito terrível toda vez que você
trabalha com vínculos de objetos.

Agora, vamos dar uma olhada no modelo mental para código estilo OLOO:

Como você pode ver comparando eles, é óbvio que o código estilo OLOO tem muito menos coisa para se preocupar, porque código OLOO
abraça o fato que a única coisa que nos preocupamos foi com objetos ligados a outros objetos.

Todas as outras sujeiras de "classes" foram uma confusa e complexa forma de se conseguir o mesmo resultado. Remova essas coisas, e as
coisas ficam muito mais simples (sem perder nenhuma funcionalidade).
Classes vs. Objetos
Acabamos de ver várias explorações teóricas e modelos mentais de "classes" vs. "delegação de comportamento". Mas, agora vamos analisar
cenários de código mais concretos para mostrar como você realmente usa essas ideias.

Primeiro, examinaremos um cenário típico no desenvolvimento web front-end: criação de widgets de UI (botões, menus suspensos etc.).

Widget "Classes"
Porque você provavelmente ainda está tão acostumado com o padrão de design OO, provavelmente irá pensar imediatamente no domínio
deste problema em termos de uma classe pai (talvez chamada de Widget ) com todo o comportamento do widget base comum e depois
classes filhas derivadas para tipos de widget específicos (como Button ).

Nota: Vamos usar jQuery para manipulação de DOM e CSS aqui, apenas porque é um detalhe que realmente não importa para os
propósitos de nossa discussão atual. Nenhum desses códigos se importam com qual framework JS (jQuery, Dojo, YUI, etc.), se houver, você
pode resolver tarefas comuns desse tipo.

Vamos examinar como implementaríamos o design "classe" no estilo clássico em JS puro sem nenhuma biblioteca ou sintaxe auxiliar de
"classe":

// Classe pai

function Widget(width,height) {

this.width = width || 50;

this.height = height || 50;

this.$elem = null;

Widget.prototype.render = function($where){

if (this.$elem) {

this.$elem.css( {

width: this.width + "px",

height: this.height + "px"

} ).appendTo( $where );

};

// Class filha

function Button(width,height,label) {

// chamada do construtor "super"

Widget.call( this, width, height );

this.label = label || "Default";

this.$elem = $( "<button>" ).text( this.label );

// faz `Button` "herdar" de `Widget`

Button.prototype = Object.create( Widget.prototype );

// sobrescreve `render(..)` "herdado"

Button.prototype.render = function($where) {

// chamada "super"

Widget.prototype.render.call( this, $where );

this.$elem.click( this.onClick.bind( this ) );

};

Button.prototype.onClick = function(evt) {

console.log( "Button '" + this.label + "' clicked!" );

};

$( document ).ready( function(){

var $body = $( document.body );

var btn1 = new Button( 125, 30, "Hello" );

var btn2 = new Button( 150, 40, "World" );

btn1.render( $body );

btn2.render( $body );

} );

O padrão de design OO nos diz para declarar um render(..) base na classe pai, e então sobrescrevê-lo na nossa classe filha, mas não
necessariamente substituí-lo, preferindo aumentar a funcionalidade base com o comportamento específico de botão.
Observe a feiura do pseudo-polimorfismo explícito (veja o Capítulo 4) com as referências Widget.call e Widget.prototype.render.call para
fingir chamadas "super" a partir dos métodos da "classe" filha para os métodos base da "classe" pai. Que nojo.

Açúcar Sintático ES6 class

Nós cobrimos o açúcar sintático class do ES6 em detalhes no Apêndice A, mas vamos demonstrar brevemente como nós poderíamos
implementar o mesmo código usando class :

class Widget {

constructor(width,height) {

this.width = width || 50;

this.height = height || 50;

this.$elem = null;

render($where){

if (this.$elem) {

this.$elem.css( {

width: this.width + "px",

height: this.height + "px"

} ).appendTo( $where );

class Button extends Widget {

constructor(width,height,label) {

super( width, height );

this.label = label || "Default";

this.$elem = $( "<button>" ).text( this.label );

render($where) {

super.render( $where );

this.$elem.click( this.onClick.bind( this ) );

onClick(evt) {

console.log( "Button '" + this.label + "' clicked!" );

$( document ).ready( function(){

var $body = $( document.body );

var btn1 = new Button( 125, 30, "Hello" );

var btn2 = new Button( 150, 40, "World" );

btn1.render( $body );

btn2.render( $body );

} );

Sem dúvidas, um pouco da feia sintaxe da abordagem clássica anterior foi suavizada pela class do ES6. A presença do super(..) em
particular parece bastante agradável (embora quando você cava mais fundo nisso, nem tudo são flores!).

Apesar das melhorias sintáticas, essas não são classes reais, uma vez que elas ainda operam sobre o mecanismo do [[Prototype]] . Eles
sofrem das mesmas discrepâncias de modelo mental que exploramos nos Capítulos 4, 5 e até agora neste capítulo. O Apêndice A irá expôr a
sintaxe class do ES6 e suas implicações em detalhes. Nós vamos ver por que resolver contratempos de linguagem não resolve
substancialmente nossas confusões com classes no JS, embora isso faça um grande esforço para se mascarar de uma solução!

Se você usa a sintaxe prototípica clássica ou o novo açúcar sintático do ES6, ainda fez uma escolha para modelar o domínio do problema
(widgets da interface do usuário) com "classes". E, como os poucos capítulos anteriores tentam demonstrar, essa opção no JavaScript está
deixando você com dores de cabeça extras e esgotamento mental.

Delegating Widget Objects


Here's our simpler Widget / Button example, using OLOO style delegation:

var Widget = {

init: function(width,height){

this.width = width || 50;

this.height = height || 50;

this.$elem = null;

},

insert: function($where){

if (this.$elem) {

this.$elem.css( {

width: this.width + "px",

height: this.height + "px"

} ).appendTo( $where );

};

var Button = Object.create( Widget );

Button.setup = function(width,height,label){

// delegated call

this.init( width, height );

this.label = label || "Default";

this.$elem = $( "<button>" ).text( this.label );

};

Button.build = function($where) {

// delegated call

this.insert( $where );

this.$elem.click( this.onClick.bind( this ) );

};

Button.onClick = function(evt) {

console.log( "Button '" + this.label + "' clicked!" );

};

$( document ).ready( function(){

var $body = $( document.body );

var btn1 = Object.create( Button );

btn1.setup( 125, 30, "Hello" );

var btn2 = Object.create( Button );

btn2.setup( 150, 40, "World" );

btn1.build( $body );

btn2.build( $body );

} );

With this OLOO-style approach, we don't think of Widget as a parent and Button as a child. Rather, Widget is just an object and is sort of
a utility collection that any specific type of widget might want to delegate to, and Button is also just a stand-alone object (with a
delegation link to Widget , of course!).

From a design pattern perspective, we didn't share the same method name render(..) in both objects, the way classes suggest, but
instead we chose different names ( insert(..) and build(..) ) that were more descriptive of what task each does specifically. The
initialization methods are called init(..) and setup(..) , respectively, for the same reasons.

Not only does this delegation design pattern suggest different and more descriptive names (rather than shared and more generic names),
but doing so with OLOO happens to avoid the ugliness of the explicit pseudo-polymorphic calls ( Widget.call and
Widget.prototype.render.call ), as you can see by the simple, relative, delegated calls to this.init(..) and this.insert(..) .

Syntactically, we also don't have any constructors, .prototype or new present, as they are, in fact, just unnecessary cruft.

Now, if you're paying close attention, you may notice that what was previously just one call ( var btn1 = new Button(..) ) is now two calls
( var btn1 = Object.create(Button) and btn1.setup(..) ). Initially this may seem like a drawback (more code).

However, even this is something that's a pro of OLOO style code as compared to classical prototype style code. How?

With class constructors, you are "forced" (not really, but strongly suggested) to do both construction and initialization in the same step.
However, there are many cases where being able to do these two steps separately (as you do with OLOO!) is more flexible.

For example, let's say you create all your instances in a pool at the beginning of your program, but you wait to initialize them with specific
setup until they are pulled from the pool and used. We showed the two calls happening right next to each other, but of course they can
happen at very different times and in very different parts of our code, as needed.

OLOO supports better the principle of separation of concerns, where creation and initialization are not necessarily conflated into the same
operation.
Simpler Design
In addition to OLOO providing ostensibly simpler (and more flexible!) code, behavior delegation as a pattern can actually lead to simpler
code architecture. Let's examine one last example that illustrates how OLOO simplifies your overall design.

The scenario we'll examine is two controller objects, one for handling the login form of a web page, and another for actually handling the
authentication (communication) with the server.

We'll need a utility helper for making the Ajax communication to the server. We'll use jQuery (though any framework would do fine), since it
handles not only the Ajax for us, but it returns a promise-like answer so that we can listen for the response in our calling code with
.then(..) .

Note: We don't cover Promises here, but we will cover them in a future title of the "You Don't Know JS" series.

Following the typical class design pattern, we'll break up the task into base functionality in a class called Controller , and then we'll derive
two child classes, LoginController and AuthController , which both inherit from Controller and specialize some of those base behaviors.

// Parent class

function Controller() {

this.errors = [];

Controller.prototype.showDialog = function(title,msg) {

// display title & message to user in dialog

};

Controller.prototype.success = function(msg) {

this.showDialog( "Success", msg );

};

Controller.prototype.failure = function(err) {

this.errors.push( err );

this.showDialog( "Error", err );

};

// Child class

function LoginController() {

Controller.call( this );

// Link child class to parent

LoginController.prototype = Object.create( Controller.prototype );

LoginController.prototype.getUser = function() {

return document.getElementById( "login_username" ).value;

};

LoginController.prototype.getPassword = function() {

return document.getElementById( "login_password" ).value;

};

LoginController.prototype.validateEntry = function(user,pw) {
user = user || this.getUser();

pw = pw || this.getPassword();

if (!(user && pw)) {

return this.failure( "Please enter a username & password!" );

else if (pw.length < 5) {

return this.failure( "Password must be 5+ characters!" );

// got here? validated!

return true;

};

// Override to extend base `failure()`

LoginController.prototype.failure = function(err) {

// "super" call

Controller.prototype.failure.call( this, "Login invalid: " + err );

};

// Child class

function AuthController(login) {

Controller.call( this );

// in addition to inheritance, we also need composition

this.login = login;

// Link child class to parent

AuthController.prototype = Object.create( Controller.prototype );

AuthController.prototype.server = function(url,data) {

return $.ajax( {

url: url,

data: data

} );

};

AuthController.prototype.checkAuth = function() {

var user = this.login.getUser();

var pw = this.login.getPassword();

if (this.login.validateEntry( user, pw )) {

this.server( "/check-auth",{

user: user,

pw: pw

} )

.then( this.success.bind( this ) )

.fail( this.failure.bind( this ) );

};

// Override to extend base `success()`

AuthController.prototype.success = function() {

// "super" call

Controller.prototype.success.call( this, "Authenticated!" );

};

// Override to extend base `failure()`

AuthController.prototype.failure = function(err) {

// "super" call

Controller.prototype.failure.call( this, "Auth Failed: " + err );

};

var auth = new AuthController(

// in addition to inheritance, we also need composition

new LoginController()

);

auth.checkAuth();

We have base behaviors that all controllers share, which are success(..) , failure(..) and showDialog(..) . Our child classes
LoginController and AuthController override failure(..) and success(..) to augment the default base class behavior. Also note that
AuthController needs an instance of LoginController to interact with the login form, so that becomes a member data property.

The other thing to mention is that we chose some composition to sprinkle in on top of the inheritance. AuthController needs to know
about LoginController , so we instantiate it ( new LoginController() ) and keep a class member property called this.login to reference it,
so that AuthController can invoke behavior on LoginController .

Note: There might have been a slight temptation to make AuthController inherit from LoginController , or vice versa, such that we had
virtual composition through the inheritance chain. But this is a strongly clear example of what's wrong with class inheritance as the model for
the problem domain, because neither AuthController nor LoginController are specializing base behavior of the other, so inheritance
between them makes little sense except if classes are your only design pattern. Instead, we layered in some simple composition and now
they can cooperate, while still both benefiting from the inheritance from the parent base Controller .

If you're familiar with class-oriented (OO) design, this should all look pretty familiar and natural.

De-class-ified
But, do we really need to model this problem with a parent Controller class, two child classes, and some composition? Is there a way to
take advantage of OLOO-style behavior delegation and have a much simpler design? Yes!

var LoginController = {

errors: [],

getUser: function() {

return document.getElementById( "login_username" ).value;

},

getPassword: function() {

return document.getElementById( "login_password" ).value;

},

validateEntry: function(user,pw) {

user = user || this.getUser();

pw = pw || this.getPassword();

if (!(user && pw)) {

return this.failure( "Please enter a username & password!" );

else if (pw.length < 5) {

return this.failure( "Password must be 5+ characters!" );

// got here? validated!

return true;

},

showDialog: function(title,msg) {

// display success message to user in dialog

},

failure: function(err) {

this.errors.push( err );
this.showDialog( "Error", "Login invalid: " + err );

};

// Link `AuthController` to delegate to `LoginController`

var AuthController = Object.create( LoginController );

AuthController.errors = [];

AuthController.checkAuth = function() {

var user = this.getUser();

var pw = this.getPassword();

if (this.validateEntry( user, pw )) {

this.server( "/check-auth",{

user: user,

pw: pw

} )

.then( this.accepted.bind( this ) )

.fail( this.rejected.bind( this ) );

};

AuthController.server = function(url,data) {

return $.ajax( {

url: url,

data: data

} );

};

AuthController.accepted = function() {

this.showDialog( "Success", "Authenticated!" )

};

AuthController.rejected = function(err) {

this.failure( "Auth Failed: " + err );

};

Since AuthController is just an object (so is LoginController ), we don't need to instantiate (like new AuthController() ) to perform our
task. All we need to do is:

AuthController.checkAuth();

Of course, with OLOO, if you do need to create one or more additional objects in the delegation chain, that's easy, and still doesn't require
anything like class instantiation:

var controller1 = Object.create( AuthController );

var controller2 = Object.create( AuthController );

With behavior delegation, AuthController and LoginController are just objects, horizontal peers of each other, and are not arranged or
related as parents and children in class-orientation. We somewhat arbitrarily chose to have AuthController delegate to LoginController --
it would have been just as valid for the delegation to go the reverse direction.
The main takeaway from this second code listing is that we only have two entities ( LoginController and AuthController ), not three as
before.

We didn't need a base Controller class to "share" behavior between the two, because delegation is a powerful enough mechanism to give
us the functionality we need. We also, as noted before, don't need to instantiate our classes to work with them, because there are no classes,
just the objects themselves. Furthermore, there's no need for composition as delegation gives the two objects the ability to cooperate
differentially as needed.

Lastly, we avoided the polymorphism pitfalls of class-oriented design by not having the names success(..) and failure(..) be the same
on both objects, which would have required ugly explicit pseudopolymorphism. Instead, we called them accepted() and rejected(..) on
AuthController -- slightly more descriptive names for their specific tasks.

Bottom line: we end up with the same capability, but a (significantly) simpler design. That's the power of OLOO-style code and the power of
the behavior delegation design pattern.

Nicer Syntax
One of the nicer things that makes ES6's class so deceptively attractive (see Appendix A on why to avoid it!) is the short-hand syntax for
declaring class methods:

class Foo {

methodName() { /* .. */ }

We get to drop the word function from the declaration, which makes JS developers everywhere cheer!

And you may have noticed and been frustrated that the suggested OLOO syntax above has lots of function appearances, which seems like
a bit of a detractor to the goal of OLOO simplification. But it doesn't have to be that way!

As of ES6, we can use concise method declarations in any object literal, so an object in OLOO style can be declared this way (same short-hand
sugar as with class body syntax):

var LoginController = {

errors: [],

getUser() { // Look ma, no `function`!

// ...

},

getPassword() {

// ...

// ...

};

About the only difference is that object literals will still require , comma separators between elements whereas class syntax doesn't.
Pretty minor concession in the whole scheme of things.

Moreover, as of ES6, the clunkier syntax you use (like for the AuthController definition), where you're assigning properties individually and
not using an object literal, can be re-written using an object literal (so that you can use concise methods), and you can just modify that
object's [[Prototype]] with Object.setPrototypeOf(..) , like this:

// use nicer object literal syntax w/ concise methods!

var AuthController = {

errors: [],

checkAuth() {

// ...

},

server(url,data) {

// ...

// ...

};

// NOW, link `AuthController` to delegate to `LoginController`

Object.setPrototypeOf( AuthController, LoginController );


OLOO-style as of ES6, with concise methods, is a lot friendlier than it was before (and even then, it was much simpler and nicer than
classical prototype-style code). You don't have to opt for class (complexity) to get nice clean object syntax!

Unlexical
There is one drawback to concise methods that's subtle but important to note. Consider this code:

var Foo = {

bar() { /*..*/ },

baz: function baz() { /*..*/ }

};

Here's the syntactic de-sugaring that expresses how that code will operate:

var Foo = {

bar: function() { /*..*/ },

baz: function baz() { /*..*/ }

};

See the difference? The bar() short-hand became an anonymous function expression ( function().. ) attached to the bar property,
because the function object itself has no name identifier. Compare that to the manually specified named function expression ( function
baz().. ) which has a lexical name identifier baz in addition to being attached to a .baz property.

So what? In the "Scope & Closures" title of this "You Don't Know JS" book series, we cover the three main downsides of anonymous function
expressions in detail. We'll just briefly repeat them so we can compare to the concise method short-hand.

Lack of a name identifier on an anonymous function:

1. makes debugging stack traces harder


2. makes self-referencing (recursion, event (un)binding, etc) harder
3. makes code (a little bit) harder to understand

Items 1 and 3 don't apply to concise methods.

Even though the de-sugaring uses an anonymous function expression which normally would have no name in stack traces, concise methods
are specified to set the internal name property of the function object accordingly, so stack traces should be able to use it (though that's
implementation dependent so not guaranteed).

Item 2 is, unfortunately, still a drawback to concise methods. They will not have a lexical identifier to use as a self-reference. Consider:

var Foo = {

bar: function(x) {

if (x < 10) {

return Foo.bar( x * 2 );

return x;

},

baz: function baz(x) {

if (x < 10) {

return baz( x * 2 );

return x;

};

The manual Foo.bar(x*2) reference above kind of suffices in this example, but there are many cases where a function wouldn't necessarily
be able to do that, such as cases where the function is being shared in delegation across different objects, using this binding, etc. You
would want to use a real self-reference, and the function object's name identifier is the best way to accomplish that.

Just be aware of this caveat for concise methods, and if you run into such issues with lack of self-reference, make sure to forgo the concise
method syntax just for that declaration in favor of the manual named function expression declaration form: baz: function baz(){..} .

Introspection
If you've spent much time with class oriented programming (either in JS or other languages), you're probably familiar with type introspection:
inspecting an instance to find out what kind of object it is. The primary goal of type introspection with class instances is to reason about the
structure/capabilities of the object based on how it was created.

Consider this code which uses instanceof (see Chapter 5) for introspecting on an object a1 to infer its capability:

function Foo() {

// ...

Foo.prototype.something = function(){

// ...

var a1 = new Foo();

// later

if (a1 instanceof Foo) {

a1.something();

Because Foo.prototype (not Foo !) is in the [[Prototype]] chain (see Chapter 5) of a1 , the instanceof operator (confusingly) pretends to
tell us that a1 is an instance of the Foo "class". With this knowledge, we then assume that a1 has the capabilities described by the Foo
"class".

Of course, there is no Foo class, only a plain old normal function Foo , which happens to have a reference to an arbitrary object
( Foo.prototype ) that a1 happens to be delegation-linked to. By its syntax, instanceof pretends to be inspecting the relationship between
a1 and Foo , but it's actually telling us whether a1 and (the arbitrary object referenced by) Foo.prototype are related.

The semantic confusion (and indirection) of instanceof syntax means that to use instanceof -based introspection to ask if object a1 is
related to the capabilities object in question, you have to have a function that holds a reference to that object -- you can't just directly ask if
the two objects are related.

Recall the abstract Foo / Bar / b1 example from earlier in this chapter, which we'll abbreviate here:

function Foo() { /* .. */ }

Foo.prototype...

function Bar() { /* .. */ }

Bar.prototype = Object.create( Foo.prototype );

var b1 = new Bar( "b1" );

For type introspection purposes on the entities in that example, using instanceof and .prototype semantics, here are the various checks
you might need to perform:

// relating `Foo` and `Bar` to each other

Bar.prototype instanceof Foo; // true

Object.getPrototypeOf( Bar.prototype ) === Foo.prototype; // true

Foo.prototype.isPrototypeOf( Bar.prototype ); // true

// relating `b1` to both `Foo` and `Bar`

b1 instanceof Foo; // true

b1 instanceof Bar; // true

Object.getPrototypeOf( b1 ) === Bar.prototype; // true

Foo.prototype.isPrototypeOf( b1 ); // true

Bar.prototype.isPrototypeOf( b1 ); // true

It's fair to say that some of that kinda sucks. For instance, intuitively (with classes) you might want to be able to say something like Bar
instanceof Foo (because it's easy to mix up what "instance" means to think it includes "inheritance"), but that's not a sensible comparison in
JS. You have to do Bar.prototype instanceof Foo instead.

Another common, but perhaps less robust, pattern for type introspection, which many devs seem to prefer over instanceof , is called "duck
typing". This term comes from the adage, "if it looks like a duck, and it quacks like a duck, it must be a duck".

Example:
if (a1.something) {
a1.something();

Rather than inspecting for a relationship between a1 and an object that holds the delegatable something() function, we assume that the
test for a1.something passing means a1 has the capability to call .something() (regardless of if it found the method directly on a1 or
delegated to some other object). In and of itself, that assumption isn't so risky.

But "duck typing" is often extended to make other assumptions about the object's capabilities besides what's being tested, which of course
introduces more risk (aka, brittle design) into the test.

One notable example of "duck typing" comes with ES6 Promises (which as an earlier note explained are not being covered in this book).

For various reasons, there's a need to determine if any arbitrary object reference is a Promise, but the way that test is done is to check if the
object happens to have a then() function present on it. In other words, if any object happens to have a then() method, ES6 Promises will
assume unconditionally that the object is a "thenable" and therefore will expect it to behave conformantly to all standard behaviors of
Promises.

If you have any non-Promise object that happens for whatever reason to have a then() method on it, you are strongly advised to keep it
far away from the ES6 Promise mechanism to avoid broken assumptions.

That example clearly illustrates the perils of "duck typing". You should only use such approaches sparingly and in controlled conditions.

Turning our attention once again back to OLOO-style code as presented here in this chapter, type introspection turns out to be much
cleaner. Let's recall (and abbreviate) the Foo / Bar / b1 OLOO example from earlier in the chapter:

var Foo = { /* .. */ };

var Bar = Object.create( Foo );

Bar...

var b1 = Object.create( Bar );

Using this OLOO approach, where all we have are plain objects that are related via [[Prototype]] delegation, here's the quite simplified
type introspection we might use:

// relating `Foo` and `Bar` to each other

Foo.isPrototypeOf( Bar ); // true

Object.getPrototypeOf( Bar ) === Foo; // true

// relating `b1` to both `Foo` and `Bar`

Foo.isPrototypeOf( b1 ); // true

Bar.isPrototypeOf( b1 ); // true

Object.getPrototypeOf( b1 ) === Bar; // true

We're not using instanceof anymore, because it's confusingly pretending to have something to do with classes. Now, we just ask the
(informally stated) question, "are you a prototype of me?" There's no more indirection necessary with stuff like Foo.prototype or the
painfully verbose Foo.prototype.isPrototypeOf(..) .

I think it's fair to say these checks are significantly less complicated/confusing than the previous set of introspection checks. Yet again, we
see that OLOO is simpler than (but with all the same power of) class-style coding in JavaScript.

Review (TL;DR)
Classes and inheritance are a design pattern you can choose, or not choose, in your software architecture. Most developers take for granted
that classes are the only (proper) way to organize code, but here we've seen there's another less-commonly talked about pattern that's
actually quite powerful: behavior delegation.

Behavior delegation suggests objects as peers of each other, which delegate amongst themselves, rather than parent and child class
relationships. JavaScript's [[Prototype]] mechanism is, by its very designed nature, a behavior delegation mechanism. That means we can
either choose to struggle to implement class mechanics on top of JS (see Chapters 4 and 5), or we can just embrace the natural state of
[[Prototype]] as a delegation mechanism.
When you design code with objects only, not only does it simplify the syntax you use, but it can actually lead to simpler code architecture
design.

OLOO (objects-linked-to-other-objects) is a code style which creates and relates objects directly without the abstraction of classes. OLOO
quite naturally implements [[Prototype]] -based behavior delegation.
Apêndice A: ES6 class
Se há uma mensagem a ser guardada da segunda metade deste livro (Capítulos 4-6), é que classes são um padrão de design opcional para
códigos (não uma regra necessária), e que elas são normalmente um pouco estranhas de se implementar em uma linguagem
[[Prototype]] como JavaScript.

Essa estranheza não ocorre apenas na sintaxe, embora ela tenha grande parte nisso. Os Capítulos 4 e 5 examinaram uma boa quantidade de
horrores sintáticos, desde a verbosidade de referências .prototype poluindo o código, até o pseudo-polimorfismo explicito (veja o Capítulo
4) onde você dá o mesmo nome a métodos em diferentes níveis da cadeia e tenta implementar uma referência polimórfica de um método
de baixo nível para outro de nível maior. .constructor ser erroneamente interpretado como "foi construído por" e ainda ser incerto para
essa definição é mais um horror sintático.

Mas os problemas com design de classes são muito mais profundos. O Capítulo 4 mostra que, em linguagens tradicionais orientadas a
classe, as classes na verdade produzem uma cópia da ação de pai para filho para instância, enquanto em [[Prototype]] essa ação não é
uma cópia, mas sim o contrário -- um link de delegação.

Quando comparadas à simplicidade de código no estilo OLOO e delegação de comportamentos (veja Capítulo 6), que abraçam o
[[Prototype]] ao invés de se esconderem dele, as classes parecem um peixe fora da água em JS.

class

But we don't need to re-argue that case again. I re-mention those issues briefly only so that you keep them fresh in your mind now that we
turn our attention to the ES6 class mechanism. We'll demonstrate here how it works, and look at whether or not class does anything
substantial to address any of those "class" concerns.

Let's revisit the Widget / Button example from Chapter 6:

class Widget {

constructor(width,height) {

this.width = width || 50;

this.height = height || 50;

this.$elem = null;

render($where){

if (this.$elem) {

this.$elem.css( {

width: this.width + "px",

height: this.height + "px"

} ).appendTo( $where );

class Button extends Widget {

constructor(width,height,label) {

super( width, height );

this.label = label || "Default";

this.$elem = $( "<button>" ).text( this.label );

render($where) {

super.render( $where );

this.$elem.click( this.onClick.bind( this ) );

onClick(evt) {

console.log( "Button '" + this.label + "' clicked!" );

Beyond this syntax looking nicer, what problems does ES6 solve?

1. There's no more (well, sorta, see below!) references to .prototype cluttering the code.
2. Button is declared directly to "inherit from" (aka extends ) Widget , instead of needing to use Object.create(..) to replace a
.prototype object that's linked, or having to set with .__proto__ or Object.setPrototypeOf(..) .
3. super(..) now gives us a very helpful relative polymorphism capability, so that any method at one level of the chain can refer
relatively one level up the chain to a method of the same name. This includes a solution to the note from Chapter 4 about the weirdness
of constructors not belonging to their class, and so being unrelated -- super() works inside constructors exactly as you'd expect.
4. class literal syntax has no affordance for specifying properties (only methods). This might seem limiting to some, but it's expected that
the vast majority of cases where a property (state) exists elsewhere but the end-chain "instances", this is usually a mistake and surprising
(as it's state that's implicitly "shared" among all "instances"). So, one could say the class syntax is protecting you from mistakes.
5. extends lets you extend even built-in object (sub)types, like Array or RegExp , in a very natural way. Doing so without class ..
extends has long been an exceedingly complex and frustrating task, one that only the most adept of framework authors have ever been
able to accurately tackle. Now, it will be rather trivial!

In all fairness, those are some substantial solutions to many of the most obvious (syntactic) issues and surprises people have with classical
prototype-style code.

class Gotchas

It's not all bubblegum and roses, though. There are still some deep and profoundly troubling issues with using "classes" as a design pattern
in JS.

Firstly, the class syntax may convince you a new "class" mechanism exists in JS as of ES6. Not so. class is, mostly, just syntactic sugar on
top of the existing [[Prototype]] (delegation!) mechanism.

That means class is not actually copying definitions statically at declaration time the way it does in traditional class-oriented languages. If
you change/replace a method (on purpose or by accident) on the parent "class", the child "class" and/or instances will still be "affected", in
that they didn't get copies at declaration time, they are all still using the live-delegation model based on [[Prototype]] :

class C {

constructor() {

this.num = Math.random();

rand() {

console.log( "Random: " + this.num );

var c1 = new C();

c1.rand(); // "Random: 0.4324299..."

C.prototype.rand = function() {

console.log( "Random: " + Math.round( this.num * 1000 ));

};

var c2 = new C();

c2.rand(); // "Random: 867"

c1.rand(); // "Random: 432" -- oops!!!

This only seems like reasonable behavior if you already know about the delegation nature of things, rather than expecting copies from "real
classes". So the question to ask yourself is, why are you choosing class syntax for something fundamentally different from classes?

Doesn't the ES6 class syntax just make it harder to see and understand the difference between traditional classes and delegated objects?

class syntax does not provide a way to declare class member properties (only methods). So if you need to do that to track shared state
among instances, then you end up going back to the ugly .prototype syntax, like this:

class C {

constructor() {

// make sure to modify the shared state,

// not set a shadowed property on the

// instances!

C.prototype.count++;

// here, `this.count` works as expected

// via delegation

console.log( "Hello: " + this.count );

// add a property for shared state directly to

// prototype object

C.prototype.count = 0;

var c1 = new C();

// Hello: 1

var c2 = new C();

// Hello: 2

c1.count === 2; // true

c1.count === c2.count; // true

The biggest problem here is that it betrays the class syntax by exposing (leakage!) .prototype as an implementation detail.

But, we also still have the surprise gotcha that this.count++ would implicitly create a separate shadowed .count property on both c1 and
c2 objects, rather than updating the shared state. class offers us no consolation from that issue, except (presumably) to imply by lack of
syntactic support that you shouldn't be doing that at all.

Moreover, accidental shadowing is still a hazard:

class C {

constructor(id) {

// oops, gotcha, we're shadowing `id()` method

// with a property value on the instance

this.id = id;

id() {

console.log( "Id: " + id );

var c1 = new C( "c1" );

c1.id(); // TypeError -- `c1.id` is now the string "c1"

There's also some very subtle nuanced issues with how super works. You might assume that super would be bound in an analogous way
to how this gets bound (see Chapter 2), which is that super would always be bound to one level higher than whatever the current
method's position in the [[Prototype]] chain is.

However, for performance reasons ( this binding is already expensive), super is not bound dynamically. It's bound sort of "statically", as
declaration time. No big deal, right?

Ehh... maybe, maybe not. If you, like most JS devs, start assigning functions around to different objects (which came from class definitions),
in various different ways, you probably won't be very aware that in all those cases, the super mechanism under the covers is having to be
re-bound each time.

And depending on what sorts of syntactic approaches you take to these assignments, there may very well be cases where the super can't
be properly bound (at least, not where you suspect), so you may (at time of writing, TC39 discussion is ongoing on the topic) have to
manually bind super with toMethod(..) (kinda like you have to do bind(..) for this -- see Chapter 2).

You're used to being able to assign around methods to different objects to automatically take advantage of the dynamism of this via the
implicit binding rule (see Chapter 2). But the same will likely not be true with methods that use super .

Consider what super should do here (against D and E ):

class P {

foo() { console.log( "P.foo" ); }

class C extends P {

foo() {

super();

var c1 = new C();

c1.foo(); // "P.foo"

var D = {

foo: function() { console.log( "D.foo" ); }

};

var E = {

foo: C.prototype.foo

};

// Link E to D for delegation

Object.setPrototypeOf( E, D );

E.foo(); // "P.foo"

If you were thinking (quite reasonably!) that super would be bound dynamically at call-time, you might expect that super() would
automatically recognize that E delegates to D , so E.foo() using super() should call to D.foo() .

Not so. For performance pragmatism reasons, super is not late bound (aka, dynamically bound) like this is. Instead it's derived at call-
time from [[HomeObject]].[[Prototype]] , where [[HomeObject]] is statically bound at creation time.

In this particular case, super() is still resolving to P.foo() , since the method's [[HomeObject]] is still C and C.[[Prototype]] is P .

There will probably be ways to manually address such gotchas. Using toMethod(..) to bind/rebind a method's [[HomeObject]] (along with
setting the [[Prototype]] of that object!) appears to work in this scenario:

var D = {

foo: function() { console.log( "D.foo" ); }

};

// Link E to D for delegation

var E = Object.create( D );

// manually bind `foo`s `[[HomeObject]]` as

// `E`, and `E.[[Prototype]]` is `D`, so thus

// `super()` is `D.foo()`

E.foo = C.prototype.foo.toMethod( E, "foo" );

E.foo(); // "D.foo"

Note: toMethod(..) clones the method, and takes homeObject as its first parameter (which is why we pass E ), and the second parameter
(optionally) sets a name for the new method (which keep at "foo").

It remains to be seen if there are other corner case gotchas that devs will run into beyond this scenario. Regardless, you will have to be
diligent and stay aware of which places the engine automatically figures out super for you, and which places you have to manually take
care of it. Ugh!

Static > Dynamic?


But the biggest problem of all about ES6 class is that all these various gotchas mean class sorta opts you into a syntax which seems to
imply (like traditional classes) that once you declare a class , it's a static definition of a (future instantiated) thing. You completely lose sight
of the fact C is an object, a concrete thing, which you can directly interact with.

In traditional class-oriented languages, you never adjust the definition of a class later, so the class design pattern doesn't suggest such
capabilities. But one of the most powerful parts of JS is that it is dynamic, and the definition of any object is (unless you make it immutable)
a fluid and mutable thing.

class seems to imply you shouldn't do such things, by forcing you into the uglier .prototype syntax to do so, or forcing you think about
super gotchas, etc. It also offers very little support for any of the pitfalls that this dynamism can bring.

In other words, it's as if class is telling you: "dynamic is too hard, so it's probably not a good idea. Here's a static-looking syntax, so code
your stuff statically."

What a sad commentary on JavaScript: dynamic is too hard, let's pretend to be (but not actually be!) static.

These are the reasons why ES6 class is masquerading as a nice solution to syntactic headaches, but it's actually muddying the waters
further and making things worse for JS and for clear and concise understanding.
Note: If you use the .bind(..) utility to make a hard-bound function (see Chapter 2), the function created is not subclassable with ES6
extend like normal functions are.

Review (TL;DR)
class does a very good job of pretending to fix the problems with the class/inheritance design pattern in JS. But it actually does the
opposite: it hides many of the problems, and introduces other subtle but dangerous ones.

class contributes to the ongoing confusion of "class" in JavaScript which has plagued the language for nearly two decades. In some
respects, it asks more questions than it answers, and it feels in totality like a very unnatural fit on top of the elegant simplicity of the
[[Prototype]] mechanism.

Bottom line: if ES6 class makes it harder to robustly leverage [[Prototype]] , and hides the most important nature of the JS object
mechanism -- the live delegation links between objects -- shouldn't we see class as creating more troubles than it solves, and just
relegate it to an anti-pattern?

I can't really answer that question for you. But I hope this book has fully explored the issue at a deeper level than you've ever gone before,
and has given you the information you need to answer it yourself.
Apêndice B: Agradecimentos
Eu tenho que agradecer à muitas pessoas pela construção deste livro e por fazerem toda a série acontecer.

Primeiramente, eu devo agradecer à minha esposa Christen Simpson, e aos meus dois filhos Ethan e Emily, por aturar o papai que sempre
fica no computador. Mesmo quando não estou escrevendo livros, minha obsessão por JavaScript me faz grudar os olhos na tela, muito mais
do que eu deveria. Este tempo, que peguei emprestado da minha família, é a razão pela qual estes livros podem explicar JavaScript tão
profunda e completamente para você, o leitor. Eu devo tudo à minha família.

Eu gostaria de agradecer aos meus editores da O'Reilly, isto é, Simon St.Laurent e Brian MacDonald, assim como o resto do editorial e do
marketing pessoal. É fantástico trabalhar com eles, e foram sobretudo compreensivos durante esta experiência de escrever, editar e publicar
um livro "open source"

Obrigado a todos que participaram para fazer esta série de livros melhor, fornecendo sugestões e correções editoriais, incluindo Shelley
Powers, Tim Ferro, Evan Borden, Forrest L. Norvell, Jennifer Davis, Jesse Harlin, e muitos outros. Um grande obrigado a Nick Berardi para
escrever o prefácio para este título.

Obrigado às inúmeras pessoas na comunidade, incluindo os membros da comissão TC39, que partilharam tanto conhecimento com o resto
de nós, e especialmente tolerando minhas perguntas incessantes e explorações com paciência e detalhe. John-David Dalton, Juriy "kangax"
Zaytsev, Mathias Bynens, Axel Rauschmayer, Nicholas Zakas, Angus Croll, Reginald Braithwaite, Dave Herman, Brendan Eich, Allen Wirfs-
Brock, Bradley Meck, Domenic Denicola, David Walsh, Tim Disney, Peter van der Zee, Andrea Giammarchi, Kit Cambridge, Eric Elliott, e tantos
outros, não posso sequer arranhar a superfície.

A série de livros You Don't Know JS nasceu no Kickstarter, então eu também gostaria de agradecer a todos os meus (quase) 500 generosos
patrocinadores, sem os quais esta série de livros não teria acontecido:
Jan Szpila, nokiko, Murali Krishnamoorthy, Ryan Joy, Craig Patchett, pdqtrader, Dale Fukami, ray hatfield, R0drigo Perez [Mx], Dan Petitt,
Jack Franklin, Andrew Berry, Brian Grinstead, Rob Sutherland, Sergi Meseguer, Phillip Gourley, Mark Watson, Jeff Carouth, Alfredo
Sumaran, Martin Sachse, Marcio Barrios, Dan, AimelyneM, Matt Sullivan, Delnatte Pierre-Antoine, Jake Smith, Eugen Tudorancea, Iris,
David Trinh, simonstl, Ray Daly, Uros Gruber, Justin Myers, Shai Zonis, Mom & Dad, Devin Clark, Dennis Palmer, Brian Panahi Johnson,
Josh Marshall, Marshall, Dennis Kerr, Matt Steele, Erik Slagter, Sacah, Justin Rainbow, Christian Nilsson, Delapouite, D.Pereira, Nicolas
Hoizey, George V. Reilly, Dan Reeves, Bruno Laturner, Chad Jennings, Shane King, Jeremiah Lee Cohick, od3n, Stan Yamane, Marko
Vucinic, Jim B, Stephen Collins, Ægir Þorsteinsson, Eric Pederson, Owain, Nathan Smith, Jeanetteurphy, Alexandre ELISÉ, Chris Peterson,
Rik Watson, Luke Matthews, Justin Lowery, Morten Nielsen, Vernon Kesner, Chetan Shenoy, Paul Tregoing, Marc Grabanski, Dion
Almaer, Andrew Sullivan, Keith Elsass, Tom Burke, Brian Ashenfelter, David Stuart, Karl Swedberg, Graeme, Brandon Hays, John
Christopher, Gior, manoj reddy, Chad Smith, Jared Harbour, Minoru TODA, Chris Wigley, Daniel Mee, Mike, Handyface, Alex Jahraus,
Carl Furrow, Rob Foulkrod, Max Shishkin, Leigh Penny Jr., Robert Ferguson, Mike van Hoenselaar, Hasse Schougaard, rajan venkataguru,
Jeff Adams, Trae Robbins, Rolf Langenhuijzen, Jorge Antunes, Alex Koloskov, Hugh Greenish, Tim Jones, Jose Ochoa, Michael Brennan-
White, Naga Harish Muvva, Barkóczi Dávid, Kitt Hodsden, Paul McGraw, Sascha Goldhofer, Andrew Metcalf, Markus Krogh, Michael
Mathews, Matt Jared, Juanfran, Georgie Kirschner, Kenny Lee, Ted Zhang, Amit Pahwa, Inbal Sinai, Dan Raine, Schabse Laks, Michael
Tervoort, Alexandre Abreu, Alan Joseph Williams, NicolasD, Cindy Wong, Reg Braithwaite, LocalPCGuy, Jon Friskics, Chris Merriman,
John Pena, Jacob Katz, Sue Lockwood, Magnus Johansson, Jeremy Crapsey, Grzegorz Pawłowski, nico nuzzaci, Christine Wilks, Hans
Bergren, charles montgomery, Ariel ‫לבב‬-‫ בר‬Fogel, Ivan Kolev, Daniel Campos, Hugh Wood, Christian Bradford, Frédéric Harper, Ionuţ
Dan Popa, Jeff Trimble, Rupert Wood, Trey Carrico, Pancho Lopez, Joël kuijten, Tom A Marra, Jeff Jewiss, Jacob Rios, Paolo Di Stefano,
Soledad Penades, Chris Gerber, Andrey Dolganov, Wil Moore III, Thomas Martineau, Kareem, Ben Thouret, Udi Nir, Morgan Laupies, jory
carson-burson, Nathan L Smith, Eric Damon Walters, Derry Lozano-Hoyland, Geoffrey Wiseman, mkeehner, KatieK, Scott MacFarlane,
Brian LaShomb, Adrien Mas, christopher ross, Ian Littman, Dan Atkinson, Elliot Jobe, Nick Dozier, Peter Wooley, John Hoover, dan,
Martin A. Jackson, Héctor Fernando Hurtado, andy ennamorato, Paul Seltmann, Melissa Gore, Dave Pollard, Jack Smith, Philip Da Silva,
Guy Israeli, @megalithic, Damian Crawford, Felix Gliesche, April Carter Grant, Heidi, jim tierney, Andrea Giammarchi, Nico Vignola, Don
Jones, Chris Hartjes, Alex Howes, john gibbon, David J. Groom, BBox, Yu 'Dilys' Sun, Nate Steiner, Brandon Satrom, Brian Wyant, Wesley
Hales, Ian Pouncey, Timothy Kevin Oxley, George Terezakis, sanjay raj, Jordan Harband, Marko McLion, Wolfgang Kaufmann, Pascal
Peuckert, Dave Nugent, Markus Liebelt, Welling Guzman, Nick Cooley, Daniel Mesquita, Robert Syvarth, Chris Coyier, Rémy Bach, Adam
Dougal, Alistair Duggin, David Loidolt, Ed Richer, Brian Chenault, GoldFire Studios, Carles Andrés, Carlos Cabo, Yuya Saito, roberto
ricardo, Barnett Klane, Mike Moore, Kevin Marx, Justin Love, Joe Taylor, Paul Dijou, Michael Kohler, Rob Cassie, Mike Tierney, Cody Leroy
Lindley, tofuji, Shimon Schwartz, Raymond, Luc De Brouwer, David Hayes, Rhys Brett-Bowen, Dmitry, Aziz Khoury, Dean, Scott Tolinski -
Level Up, Clement Boirie, Djordje Lukic, Anton Kotenko, Rafael Corral, Philip Hurwitz, Jonathan Pidgeon, Jason Campbell, Joseph C.,
SwiftOne, Jan Hohner, Derick Bailey, getify, Daniel Cousineau, Chris Charlton, Eric Turner, David Turner, Joël Galeran, Dharma Vagabond,
adam, Dirk van Bergen, dave ♥♫★ furf, Vedran Zakanj, Ryan McAllen, Natalie Patrice Tucker, Eric J. Bivona, Adam Spooner, Aaron
Cavano, Kelly Packer, Eric J, Martin Drenovac, Emilis, Michael Pelikan, Scott F. Walter, Josh Freeman, Brandon Hudgeons, vijay
chennupati, Bill Glennon, Robin R., Troy Forster, otaku_coder, Brad, Scott, Frederick Ostrander, Adam Brill, Seb Flippence, Michael
Anderson, Jacob, Adam Randlett, Standard, Joshua Clanton, Sebastian Kouba, Chris Deck, SwordFire, Hannes Papenberg, Richard
Woeber, hnzz, Rob Crowther, Jedidiah Broadbent, Sergey Chernyshev, Jay-Ar Jamon, Ben Combee, luciano bonachela, Mark Tomlinson,
Kit Cambridge, Michael Melgares, Jacob Adams, Adrian Bruinhout, Bev Wieber, Scott Puleo, Thomas Herzog, April Leone, Daniel
Mizieliński, Kees van Ginkel, Jon Abrams, Erwin Heiser, Avi Laviad, David newell, Jean-Francois Turcot, Niko Roberts, Erik Dana, Charles
Neill, Aaron Holmes, Grzegorz Ziółkowski, Nathan Youngman, Timothy, Jacob Mather, Michael Allan, Mohit Seth, Ryan Ewing, Benjamin
Van Treese, Marcelo Santos, Denis Wolf, Phil Keys, Chris Yung, Timo Tijhof, Martin Lekvall, Agendine, Greg Whitworth, Helen Humphrey,
Dougal Campbell, Johannes Harth, Bruno Girin, Brian Hough, Darren Newton, Craig McPheat, Olivier Tille, Dennis Roethig, Mathias
Bynens, Brendan Stromberger, sundeep, John Meyer, Ron Male, John F Croston III, gigante, Carl Bergenhem, B.J. May, Rebekah Tyler,
Ted Foxberry, Jordan Reese, Terry Suitor, afeliz, Tom Kiefer, Darragh Duffy, Kevin Vanderbeken, Andy Pearson, Simon Mac Donald, Abid
Din, Chris Joel, Tomas Theunissen, David Dick, Paul Grock, Brandon Wood, John Weis, dgrebb, Nick Jenkins, Chuck Lane, Johnny
Megahan, marzsman, Tatu Tamminen, Geoffrey Knauth, Alexander Tarmolov, Jeremy Tymes, Chad Auld, Sean Parmelee, Rob Staenke,
Dan Bender, Yannick derwa, Joshua Jones, Geert Plaisier, Tom LeZotte, Christen Simpson, Stefan Bruvik, Justin Falcone, Carlos Santana,
Michael Weiss, Pablo Villoslada, Peter deHaan, Dimitris Iliopoulos, seyDoggy, Adam Jordens, Noah Kantrowitz, Amol M, Matthew
Winnard, Dirk Ginader, Phinam Bui, David Rapson, Andrew Baxter, Florian Bougel, Michael George, Alban Escalier, Daniel Sellers, Sasha
Rudan, John Green, Robert Kowalski, David I. Teixeira (@ditma, Charles Carpenter, Justin Yost, Sam S, Denis Ciccale, Kevin Sheurs,
Yannick Croissant, Pau Fracés, Stephen McGowan, Shawn Searcy, Chris Ruppel, Kevin Lamping, Jessica Campbell, Christopher Schmitt,
Sablons, Jonathan Reisdorf, Bunni Gek, Teddy Huff, Michael Mullany, Michael Fürstenberg, Carl Henderson, Rick Yoesting, Scott Nichols,
Hernán Ciudad, Andrew Maier, Mike Stapp, Jesse Shawl, Sérgio Lopes, jsulak, Shawn Price, Joel Clermont, Chris Ridmann, Sean Timm,
Jason Finch, Aiden Montgomery, Elijah Manor, Derek Gathright, Jesse Harlin, Dillon Curry, Courtney Myers, Diego Cadenas, Arne de
Bree, João Paulo Dubas, James Taylor, Philipp Kraeutli, Mihai Păun, Sam Gharegozlou, joshjs, Matt Murchison, Eric Windham, Timo
Behrmann, Andrew Hall, joshua price, Théophile Villard

Esta série de livros está sendo produzido num estilo código aberto, incluindo a edição e produção. Devemos ao GitHub a gratidão para fazer
esse tipo de coisa possível para a comunidade!
Mais uma vez, obrigado a todos os incontáveis que eu não sei o nome, mas pelos quais eu sou grato. Que esta série de livros seja
"propriedade" de todos nós e sirva para contribuir com o crescimento e a compreensão da linguagem JavaScript, para benefício de todos os
colaboradores da comunidade, atuais e futuros.

Você também pode gostar