Skip to content
Natuurondernemer
    Fevereiro 19, 2021 by admin

    Um principiante's Guia para JavaScript's Protótipo

    Um principiante's Guia para JavaScript's Protótipo
    Fevereiro 19, 2021 by admin

    p> Não se pode ir muito longe em JavaScript sem lidar com objectos. Eles são fundamentais para quase todos os aspectos da linguagem de programação JavaScript. De facto, aprender a criar objectos é provavelmente uma das primeiras coisas que se estudou quando se estava a começar. Dito isto, para aprender mais eficazmente sobre protótipos em JavaScript, vamos canalizar o nosso desenvolvedor Jr. interno e voltar ao básico.

    Objectos são pares chave/valor. A forma mais comum de criar um objecto é com chaves de caracóis {} e adiciona-se propriedades e métodos a um objecto usando notação de pontos.

    let animal = {}animal.name = 'Leo'animal.energy = 10animal.eat = function (amount) { console.log(`${this.name} is eating.`) this.energy += amount}animal.sleep = function (length) { console.log(`${this.name} is sleeping.`) this.energy += length}animal.play = function (length) { console.log(`${this.name} is playing.`) this.energy -= length}

    Simples. Agora, as probabilidades estão na nossa aplicação e precisaremos de criar mais do que um animal. Naturalmente, o passo seguinte para isso seria encapsular essa lógica dentro de uma função que podemos invocar sempre que precisemos de criar um novo animal. Chamaremos a este padrão Functional Instantiation e chamaremos à própria função uma “função construtora” uma vez que é responsável por “construir” um novo objecto.

    Functional Instantiation

    function Animal (name, energy) { let animal = {} animal.name = name animal.energy = energy animal.eat = function (amount) { console.log(`${this.name} is eating.`) this.energy += amount } animal.sleep = function (length) { console.log(`${this.name} is sleeping.`) this.energy += length } animal.play = function (length) { console.log(`${this.name} is playing.`) this.energy -= length } return animal}const leo = Animal('Leo', 7)const snoop = Animal('Snoop', 10)

    "I thought this was an Advanced JavaScript course...?" - Your brain É. Chegaremos lá.

    Agora sempre que quisermos criar um novo animal (ou, de uma forma mais geral, uma nova “instância”), tudo o que temos de fazer é invocar a nossa função Animal, passando-lhe a função name e energy nível. Isto funciona muito bem e é incrivelmente simples. No entanto, é possível detectar quaisquer fraquezas com este padrão? O maior e o que vamos tentar resolver tem a ver com os três métodos – eatsleep, e play. Cada um desses métodos não só são dinâmicos, como também são completamente genéricos. O que isso significa é que não há razão para recriar esses métodos como estamos actualmente a fazer sempre que criamos um novo animal. Estamos apenas a desperdiçar memória e a tornar cada objecto animal maior do que precisa de ser. Consegue pensar numa solução? E se em vez de recriarmos esses métodos cada vez que criamos um novo animal, os deslocarmos para o seu próprio objecto, então podemos ter cada animal de referência a esse objecto? Podemos chamar a este padrão Functional Instantiation with Shared Methods, verboso mas descritivo.

    Instanciação funcional com métodos partilhados

    const animalMethods = { eat(amount) { console.log(`${this.name} is eating.`) this.energy += amount }, sleep(length) { console.log(`${this.name} is sleeping.`) this.energy += length }, play(length) { console.log(`${this.name} is playing.`) this.energy -= length }}function Animal (name, energy) { let animal = {} animal.name = name animal.energy = energy animal.eat = animalMethods.eat animal.sleep = animalMethods.sleep animal.play = animalMethods.play return animal}const leo = Animal('Leo', 7)const snoop = Animal('Snoop', 10)

    movendo os métodos partilhados para o seu próprio objecto e referenciando esse objecto dentro da nossa função Animal, resolvemos agora o problema do desperdício de memória e dos objectos animais demasiado grandes.

    Object.create

    Vamos melhorar o nosso exemplo mais uma vez usando Object.create. Simplificando, Object.create permite criar um objecto que será delegado a outro objecto em pesquisas falhadas. Pondo de outra forma, Object.create permite-lhe criar um objecto e sempre que houver uma pesquisa de propriedade falhada nesse objecto, pode consultar outro objecto para ver se esse outro objecto tem a propriedade. Isso foi um monte de palavras. Vamos ver algum código.

    const parent = { name: 'Stacey', age: 35, heritage: 'Irish'}const child = Object.create(parent)child.name = 'Ryan'child.age = 7console.log(child.name) // Ryanconsole.log(child.age) // 7console.log(child.heritage) // Irish

    Então no exemplo acima, porque child foi criado com Object.create(parent), sempre que houver uma pesquisa de propriedade falhada em child, JavaScript delegará essa pesquisa até ao objecto parent. O que isso significa é que embora child não tenha um heritage propriedade, parent fá-lo quando regista child.heritage obterá o parent‘s heritage que era Irish.

    Agora com Object.create no nosso barracão de ferramentas, como podemos utilizá-lo para simplificar o nosso Animal código de antes? Bem, em vez de adicionar todos os métodos partilhados ao animal um a um, como estamos a fazer agora, podemos usar Object.create para delegar no animalMethods objecto em vez disso. Para parecer realmente inteligente, vamos chamar a este Functional Instantiation with Shared Methods and Object.create 🙃

    Functional Instantiation with Shared Methods and Object.create

    const animalMethods = { eat(amount) { console.log(`${this.name} is eating.`) this.energy += amount }, sleep(length) { console.log(`${this.name} is sleeping.`) this.energy += length }, play(length) { console.log(`${this.name} is playing.`) this.energy -= length }}function Animal (name, energy) { let animal = Object.create(animalMethods) animal.name = name animal.energy = energy return animal}const leo = Animal('Leo', 7)const snoop = Animal('Snoop', 10)leo.eat(10)snoop.play(5)

    📈 Então agora quando chamamos leo.eat, o JavaScript procurará o método eat no objecto leo. Essa pesquisa falhará, então, por causa do Object.create, delegará no animalMethods objecto que é onde encontrará eat.

    p> até agora, tudo bem. No entanto, ainda há algumas melhorias que podemos fazer. Parece apenas um pouco “hacky” ter de gerir um objecto separado (animalMethods) a fim de partilhar métodos através de instâncias. Isso parece ser uma característica comum que gostaria de implementar na própria linguagem. Acontece que é e é toda a razão pela qual está aqui –prototype.

    Então o que é exactamente prototype em JavaScript? Bem, pondo simplesmente, cada função em JavaScript tem uma propriedade prototype que faz referência a um objecto. Anticlimático, certo? Teste-o por si próprio.

    function doThing () {}console.log(doThing.prototype) // {}

    E se em vez de criarmos um objecto separado para gerir os nossos métodos (como estamos a fazer com animalMethods), apenas colocamos cada um desses métodos no Animal protótipo da função? Então tudo o que teríamos de fazer seria em vez de usar o Object.create para delegar a animalMethods, poderíamos usá-lo para delegar a Animal.prototype. Vamos chamar a este padrão Prototypal Instantiation.

    Instanciação Protótipo

    function Animal (name, energy) { let animal = Object.create(Animal.prototype) animal.name = name animal.energy = energy return animal}Animal.prototype.eat = function (amount) { console.log(`${this.name} is eating.`) this.energy += amount}Animal.prototype.sleep = function (length) { console.log(`${this.name} is sleeping.`) this.energy += length}Animal.prototype.play = function (length) { console.log(`${this.name} is playing.`) this.energy -= length}const leo = Animal('Leo', 7)const snoop = Animal('Snoop', 10)leo.eat(10)snoop.play(5)

    👏👏👏 Esperemos que tenha tido um grande momento “aha”. Mais uma vez, prototype é apenas uma propriedade que todas as funções em JavaScript têm e, como vimos acima, permite-nos partilhar métodos em todas as instâncias de uma função. Toda a nossa funcionalidade é ainda a mesma mas agora, em vez de termos de gerir um objecto separado para todos os métodos, podemos simplesmente utilizar outro objecto que vem incorporado no Animal própria função, Animal.prototype.

    Let’s. Vai. Deeper.

    Neste ponto sabemos três coisas:

    1. Como criar uma função construtora.
    2. Como adicionar métodos ao protótipo da função construtora.
    3. Como usar Object.create para delegar pesquisas falhadas ao protótipo da função.

    Tres tarefas parecem bastante fundacionais para qualquer linguagem de programação. O JavaScript é realmente assim tão mau que não existe uma forma mais fácil, “incorporada” de realizar a mesma coisa? Como provavelmente pode adivinhar neste ponto existe, e é usando o new palavra-chave.

    O que é bom na abordagem lenta e metódica que fizemos para chegar aqui é que agora terá uma compreensão profunda do que o new palavra-chave em JavaScript está a fazer debaixo do capô.

    Voltando ao nosso Animal construtor, as duas partes mais importantes eram a criação do objecto e a sua devolução. Sem criar o objecto com Object.create, não seríamos capazes de delegar no protótipo da função nas pesquisas falhadas. Sem a declaração return, nunca recuperaríamos o objecto criado.

    function Animal (name, energy) { let animal = Object.create(Animal.prototype) animal.name = name animal.energy = energy return animal}

    Aqui está a coisa fixe sobre new – quando se invoca uma função usando a palavra-chave new, essas duas linhas são feitas implicitamente (“debaixo da capota”) e o objecto que é criado chama-se this.

    Usando comentários para mostrar o que acontece debaixo da capota e assumindo o Animal construtor é chamado com a palavra-chave new, pode ser reescrita como isto.

    function Animal (name, energy) { // const this = Object.create(Animal.prototype) this.name = name this.energy = energy // return this}const leo = new Animal('Leo', 7)const snoop = new Animal('Snoop', 10)

    e sem os comentários “debaixo do capô”

    function Animal (name, energy) { this.name = name this.energy = energy}Animal.prototype.eat = function (amount) { console.log(`${this.name} is eating.`) this.energy += amount}Animal.prototype.sleep = function (length) { console.log(`${this.name} is sleeping.`) this.energy += length}Animal.prototype.play = function (length) { console.log(`${this.name} is playing.`) this.energy -= length}const leo = new Animal('Leo', 7)const snoop = new Animal('Snoop', 10)

    Apanhar a razão disto funciona e que o this objecto é criado para nós é porque chamamos a função construtor com o new palavra-chave. Se deixar de fora new quando invoca a função, esse this objecto nunca é criado nem é implicitamente devolvido. Podemos ver o problema com isto no exemplo abaixo.

    function Animal (name, energy) { this.name = name this.energy = energy}const leo = Animal('Leo', 7)console.log(leo) // undefined

    O nome para este padrão é Pseudoclassical Instantiation.

    Se o JavaScript não for a sua primeira linguagem de programação, poderá estar a ficar um pouco inquieto.

    “WTF este gajo acabou de recriar uma versão mais porcaria de uma Classe” – You

    Para os não familiarizados, uma Classe permite-lhe criar um plano para um objecto. Depois, sempre que cria uma instância dessa Classe, obtém um objecto com as propriedades e métodos definidos no projecto.

    Som familiar? Foi basicamente o que fizemos com a nossa Animal função construtora acima. Contudo, em vez de usarmos a palavra-chave class, utilizámos apenas uma antiga função JavaScript regular para recriar a mesma funcionalidade. É verdade que foi preciso um pouco de trabalho extra, bem como algum conhecimento sobre o que acontece “debaixo da capa” do JavaScript, mas os resultados são os mesmos.

    Aqui estão as boas notícias. O JavaScript não é uma linguagem morta. Está constantemente a ser melhorado e adicionado pelo comité TC-39. O que isso significa é que, embora a versão inicial do JavaScript não suportasse as aulas, não há razão para que não possam ser acrescentadas à especificação oficial. Na verdade, foi exactamente isso que o comité TC-39 fez. Em 2015, EcmaScript (a especificação oficial JavaScript) 6 foi lançado com suporte para Classes e a class palavra-chave. Vejamos como o nosso Animal função construtora acima ficaria com a nova sintaxe de classe.

    class Animal { constructor(name, energy) { this.name = name this.energy = energy } eat(amount) { console.log(`${this.name} is eating.`) this.energy += amount } sleep(length) { console.log(`${this.name} is sleeping.`) this.energy += length } play(length) { console.log(`${this.name} is playing.`) this.energy -= length }}const leo = new Animal('Leo', 7)const snoop = new Animal('Snoop', 10)

    P>Pretty clean, right?

    Então, se esta é a nova forma de criar classes, porque é que passámos tanto tempo a percorrer o caminho antigo? A razão para isso é porque a nova maneira (com o class palavra-chave) é principalmente apenas “açúcar sintáctico” sobre a maneira existente a que chamámos padrão pseudo-clássico. Para compreender completamente a sintaxe de conveniência das classes ES6, é preciso primeiro compreender o padrão pseudo-clássico.

    Neste ponto cobrimos os fundamentos do protótipo do JavaScript. O resto deste post será dedicado à compreensão de outros tópicos “bons de saber” relacionados com o mesmo. Noutro post, veremos como podemos pegar nestes fundamentos e usá-los para compreender como funciona a herança em JavaScript.

    Array Methods

    Falámos em profundidade acima sobre como se quiser partilhar métodos através de instâncias de uma classe, deve colar esses métodos no protótipo da classe’ (ou função). Podemos ver este mesmo padrão demonstrado se olharmos para o Array classe. Historicamente, é provável que tenha criado as suas arrays desta forma

    const friends = 

    Sai apenas açúcar ao criar um new instância do Array classe.

    const friendsWithSugar = const friendsWithoutSugar = new Array()

    Uma coisa em que talvez nunca tenha pensado é como é que cada instância de um array tem todos esses métodos incorporados (spliceslicepop, etc.)?

    bem como sabe agora, é porque esses métodos vivem em Array.prototype e quando cria uma nova instância de Array, usa a palavra-chave new que estabelece essa delegação a Array.prototype em pesquisas falhadas.

    Podemos ver todos os métodos da matriz através de simples registo Array.prototype.

    console.log(Array.prototype)/* concat: ƒn concat() constructor: ƒn Array() copyWithin: ƒn copyWithin() entries: ƒn entries() every: ƒn every() fill: ƒn fill() filter: ƒn filter() find: ƒn find() findIndex: ƒn findIndex() forEach: ƒn forEach() includes: ƒn includes() indexOf: ƒn indexOf() join: ƒn join() keys: ƒn keys() lastIndexOf: ƒn lastIndexOf() length: 0n map: ƒn map() pop: ƒn pop() push: ƒn push() reduce: ƒn reduce() reduceRight: ƒn reduceRight() reverse: ƒn reverse() shift: ƒn shift() slice: ƒn slice() some: ƒn some() sort: ƒn sort() splice: ƒn splice() toLocaleString: ƒn toLocaleString() toString: ƒn toString() unshift: ƒn unshift() values: ƒn values()*/

    A mesma lógica existe exactamente também para os Objectos. Todos os objectos serão delegados a Object.prototype em pesquisas falhadas, razão pela qual todos os objectos têm métodos como toString e hasOwnProperty.

    Métodos estáticos

    Up até este ponto, cobrimos o porquê e como partilhar métodos entre instâncias de uma Classe. Contudo, e se tivéssemos um método que fosse importante para a Aula, mas que não precisasse de ser partilhado entre instâncias? Por exemplo, e se tivéssemos uma função que incluía um conjunto de Animal instâncias e determinasse qual delas precisava de ser alimentada a seguir? Vamos chamar-lhe nextToEat.

    function nextToEat (animals) { const sortedByLeastEnergy = animals.sort((a,b) => { return a.energy - b.energy }) return sortedByLeastEnergy.name}

    Não faz sentido ter nextToEat viver em Animal.prototype uma vez que não queremos partilhá-lo entre todas as instâncias. Em vez disso, podemos pensar nisto como um método mais de ajuda. Portanto, se nextToEat não deveria viver em Animal.prototype, onde devemos colocá-lo? Bem, a resposta óbvia é que podíamos simplesmente colar nextToEat no mesmo âmbito que o nosso Animal classe e depois referenciá-la quando precisamos dela como normalmente faríamos.

    class Animal { constructor(name, energy) { this.name = name this.energy = energy } eat(amount) { console.log(`${this.name} is eating.`) this.energy += amount } sleep(length) { console.log(`${this.name} is sleeping.`) this.energy += length } play(length) { console.log(`${this.name} is playing.`) this.energy -= length }}function nextToEat (animals) { const sortedByLeastEnergy = animals.sort((a,b) => { return a.energy - b.energy }) return sortedByLeastEnergy.name}const leo = new Animal('Leo', 7)const snoop = new Animal('Snoop', 10)console.log(nextToEat()) // Leo

    Agora isto funciona, mas há uma maneira melhor.

    Quando se tem um método que é específico de uma classe em si, mas não precisa de ser partilhado entre instâncias dessa classe, pode-se adicioná-lo como um static propriedade da classe.

    class Animal { constructor(name, energy) { this.name = name this.energy = energy } eat(amount) { console.log(`${this.name} is eating.`) this.energy += amount } sleep(length) { console.log(`${this.name} is sleeping.`) this.energy += length } play(length) { console.log(`${this.name} is playing.`) this.energy -= length } static nextToEat(animals) { const sortedByLeastEnergy = animals.sort((a,b) => { return a.energy - b.energy }) return sortedByLeastEnergy.name }}

    Now, porque adicionámos nextToEat como uma static propriedade da classe, vive no Animal classe em si (não o seu protótipo) e pode ser acedido usando Animal.nextToEat.

    const leo = new Animal('Leo', 7)const snoop = new Animal('Snoop', 10)console.log(Animal.nextToEat()) // Leo

    Porque seguimos um padrão semelhante ao longo deste post, vejamos como realizaríamos esta mesma coisa usando ES5. No exemplo acima vimos como a utilização da palavra-chave static colocaria o método directamente na própria classe. Com ES5, este mesmo padrão é tão simples como adicionar manualmente o método ao objecto de função.

    function Animal (name, energy) { this.name = name this.energy = energy}Animal.prototype.eat = function (amount) { console.log(`${this.name} is eating.`) this.energy += amount}Animal.prototype.sleep = function (length) { console.log(`${this.name} is sleeping.`) this.energy += length}Animal.prototype.play = function (length) { console.log(`${this.name} is playing.`) this.energy -= length}Animal.nextToEat = function (animals) { const sortedByLeastEnergy = animals.sort((a,b) => { return a.energy - b.energy }) return sortedByLeastEnergy.name}const leo = new Animal('Leo', 7)const snoop = new Animal('Snoop', 10)console.log(Animal.nextToEat()) // Leo

    Conseguir o protótipo de um objecto

    Independentemente do padrão utilizado para criar um objecto, a obtenção do protótipo desse objecto pode ser conseguida utilizando o método Object.getPrototypeOf.

    function Animal (name, energy) { this.name = name this.energy = energy}Animal.prototype.eat = function (amount) { console.log(`${this.name} is eating.`) this.energy += amount}Animal.prototype.sleep = function (length) { console.log(`${this.name} is sleeping.`) this.energy += length}Animal.prototype.play = function (length) { console.log(`${this.name} is playing.`) this.energy -= length}const leo = new Animal('Leo', 7)const prototype = Object.getPrototypeOf(leo)console.log(prototype)// {constructor: ƒ, eat: ƒ, sleep: ƒ, play: ƒ}prototype === Animal.prototype // true

    Existem dois importantes takeaways do código acima.

    P>Primeiro, irá notar que proto é um objecto com 4 métodos, constructoreatsleep, e play. Isso faz sentido. Utilizámos getPrototypeOf passando na instância, leo recuperando o protótipo dessas instâncias, que é onde todos os nossos métodos estão a viver. Isto diz-nos mais uma coisa sobre prototype também que ainda não falámos. Por defeito, o objecto prototype terá uma propriedade constructor que aponta para a função original ou para a classe a partir da qual a instância foi criada. O que isto também significa é que porque o JavaScript coloca uma propriedade constructor no protótipo por defeito, qualquer instância poderá aceder ao seu construtor através de instance.constructor.

    A segunda importante retirada de cima é que Object.getPrototypeOf(leo) === Animal.prototype. Isso também faz sentido. O Animal função construtora tem uma propriedade protótipo onde podemos partilhar métodos em todas as instâncias e getPrototypeOf permite-nos ver o protótipo da própria instância.

    function Animal (name, energy) { this.name = name this.energy = energy}const leo = new Animal('Leo', 7)console.log(leo.constructor) // Logs the constructor function

    para ligar o que falámos anteriormente com Object.create, a razão porque isto funciona é porque quaisquer instâncias de Animal vão delegar em Animal.prototype em consultas falhadas. Assim, quando tentar aceder a leo.constructorleo não tem uma propriedade constructor pelo que irá delegar essa consulta a Animal.prototype que de facto tem uma propriedade constructor. Se este parágrafo não fazia sentido, voltar atrás e ler sobre Object.create acima.

    p>Talvez já tenha visto __proto__ usado antes para obter um protótipo de instâncias. Isso é uma relíquia do passado. Em vez disso, use Object.getPrototypeOf(instance) como vimos acima.

    Determinar se uma propriedade vive no protótipo

    Há certos casos em que é necessário saber se uma propriedade vive na própria instância ou se vive no protótipo a que o objecto delega. Podemos ver isto em acção, passando por cima do nosso leo objecto que temos vindo a criar. Digamos que o objectivo era o loop over leo e registar todas as suas chaves e valores. Usando um for in loop, que se pareceria provavelmente com isto.

    function Animal (name, energy) { this.name = name this.energy = energy}Animal.prototype.eat = function (amount) { console.log(`${this.name} is eating.`) this.energy += amount}Animal.prototype.sleep = function (length) { console.log(`${this.name} is sleeping.`) this.energy += length}Animal.prototype.play = function (length) { console.log(`${this.name} is playing.`) this.energy -= length}const leo = new Animal('Leo', 7)for(let key in leo) { console.log(`Key: ${key}. Value: ${leo}`)}

    O que esperaria ver? Muito provavelmente, era algo parecido com isto –

    Key: name. Value: LeoKey: energy. Value: 7

    No entanto, o que viu se correu o código foi isto –

    Key: name. Value: LeoKey: energy. Value: 7Key: eat. Value: function (amount) { console.log(`${this.name} is eating.`) this.energy += amount}Key: sleep. Value: function (length) { console.log(`${this.name} is sleeping.`) this.energy += length}Key: play. Value: function (length) { console.log(`${this.name} is playing.`) this.energy -= length}

    Porquê? Bem, um for in loop vai fazer loop sobre todas as propriedades enumeráveis tanto no próprio objecto como no protótipo que delega. Porque por defeito qualquer propriedade que se acrescenta ao protótipo da função é enumerável, vemos não só name e energy, mas também vemos todos os métodos no protótipo – eatsleep, e play. Para corrigir isto, ou precisamos de especificar que todos os métodos do protótipo são inumeráveis ou precisamos de uma forma de apenas consolar.log se a propriedade estiver no leo objecto em si e não o protótipo que leo delegados em consultas falhadas. É aqui que hasOwnProperty nos pode ajudar.

    hasOwnProperty é uma propriedade em cada objecto que devolve um booleano indicando se o objecto tem a propriedade especificada como sua própria propriedade e não no protótipo a que o objecto delega em caso de falha de pesquisa. É exactamente o que precisamos. Agora com este novo conhecimento, podemos modificar o nosso código para tirar partido de hasOwnProperty dentro do nosso for in loop.

    ...const leo = new Animal('Leo', 7)for(let key in leo) { if (leo.hasOwnProperty(key)) { console.log(`Key: ${key}. Value: ${leo}`) }}

    E agora o que vemos são apenas as propriedades que estão no leo objecto em si e não no protótipo leo delegados também.

    Key: name. Value: LeoKey: energy. Value: 7

    Se ainda estiver um pouco confuso sobre hasOwnProperty, aqui está algum código que pode clarificá-lo.

    function Animal (name, energy) { this.name = name this.energy = energy}Animal.prototype.eat = function (amount) { console.log(`${this.name} is eating.`) this.energy += amount}Animal.prototype.sleep = function (length) { console.log(`${this.name} is sleeping.`) this.energy += length}Animal.prototype.play = function (length) { console.log(`${this.name} is playing.`) this.energy -= length}const leo = new Animal('Leo', 7)leo.hasOwnProperty('name') // trueleo.hasOwnProperty('energy') // trueleo.hasOwnProperty('eat') // falseleo.hasOwnProperty('sleep') // falseleo.hasOwnProperty('play') // false

    Verifica se um objecto é uma instância de uma Classe

    Por vezes quer saber se um objecto é uma instância de uma classe específica. Para o fazer, pode usar o operador instanceof. O caso de utilização é bastante directo, mas a sintaxe real é um pouco estranha se nunca a tiver visto antes. Funciona assim

    object instanceof Class

    A afirmação acima retornará verdadeira se object for uma instância de Class e falsa se não for. Voltando ao nosso Animal exemplo, teríamos algo assim.

    function Animal (name, energy) { this.name = name this.energy = energy}function User () {}const leo = new Animal('Leo', 7)leo instanceof Animal // trueleo instanceof User // false

    A forma como instanceof funciona é verificar a presença de constructor.prototype na cadeia de protótipos do objecto. No exemplo acima, leo instanceof Animaltrue porque Object.getPrototypeOf(leo) === Animal.prototype. Além disso, leo instanceof Userfalse porque Object.getPrototypeOf(leo) !== User.prototype.

    Criar novas funções de construtor agnóstico

    P>Pode detectar o erro no código abaixo?

    function Animal (name, energy) { this.name = name this.energy = energy}const leo = Animal('Leo', 7)

    Even experientes programadores de JavaScript irão por vezes tropeçar no exemplo acima. Porque estamos a utilizar o pseudoclassical pattern de que tomámos conhecimento anteriormente, quando a Animal função construtora é invocada, temos de nos certificar de que a invocamos com a palavra-chave new. Se não o fizermos, então o this palavra-chave não será criada e também não será implicitamente devolvida.

    Como uma actualização, as linhas comentadas são o que acontece nos bastidores quando se usa o new palavra-chave sobre uma função.

    function Animal (name, energy) { // const this = Object.create(Animal.prototype) this.name = name this.energy = energy // return this}

    Isto parece demasiado importante de um detalhe para deixar para os outros programadores se lembrarem. Assumindo que estamos a trabalhar em equipa com outros programadores, há alguma forma de garantir que o nosso Animal construtor é sempre invocado com a palavra-chave new? Acontece que existe e é usando o instanceof operador de que tomámos conhecimento anteriormente.

    Se o construtor foi chamado com a palavra-chave new, então this dentro do corpo do construtor será uma instanceof a própria função do construtor. Isso foi um monte de palavras grandes. Aqui está algum código.

    function Animal (name, energy) { if (this instanceof Animal === false) { console.warn('Forgot to call Animal with the new keyword') } this.name = name this.energy = energy}

    agora em vez de apenas registar um aviso ao consumidor da função, e se voltarmos a invocar a função mas com o new palavra-chave desta vez?

    function Animal (name, energy) { if (this instanceof Animal === false) { return new Animal(name, energy) } this.name = name this.energy = energy}

    Agora independentemente de se Animal for invocado com a new palavra-chave, ainda assim funcionará correctamente.

    Re-creating Object.create

    Através deste post, confiamos fortemente em Object.create a fim de criar objectos que delegam no protótipo da função construtora. Neste ponto, deve saber como usar Object.create dentro do seu código, mas uma coisa que talvez não tenha pensado é como Object.create funciona realmente debaixo do capô. Para que possa realmente compreender como Object.create funciona, vamos recriá-lo nós próprios. Primeiro, o que sabemos sobre como Object.create funciona?

      1. Cria um argumento que é um objecto.
      2. Cria um objecto que delega no argumento objecto em pesquisas falhadas.
      3. Devolve o novo objecto criado.
      4. /ol>

        Vamos começar com #1.

        Object.create = function (objToDelegateTo) {}

        Simples o suficiente.

        Agora #2 – precisamos de criar um objecto que delegue no objecto argumento em pesquisas falhadas. Este é um pouco mais complicado. Para tal, usaremos o nosso conhecimento de como o new palavra-chave e protótipos funcionam em JavaScript. Primeiro, dentro do corpo do nosso Object.create implementação, vamos criar uma função vazia. Em seguida, vamos definir o protótipo dessa função vazia como sendo igual ao objecto de argumento. Depois, para criar um novo objecto, invocaremos a nossa função vazia usando a palavra-chave new. Se devolvermos esse objecto recém-criado, isso terminará também #3.

        Object.create = function (objToDelegateTo) { function Fn(){} Fn.prototype = objToDelegateTo return new Fn()}

        Wild. Vamos percorrê-lo.

        Quando criamos uma nova função, Fn no código acima, vem com uma propriedade prototype. Quando o invocamos com a palavra-chave new, sabemos que o que vamos receber de volta é um objecto que será delegado no protótipo da função em pesquisas falhadas. Se anularmos o protótipo da função, então podemos decidir qual o objecto a delegar em caso de pesquisa falhada. Assim, no nosso exemplo acima, anulamos Fn‘s prototype with the object that was passed in when Object.create‘s invoked which we call objToDelegateTo.

        Nota que só estamos a apoiar um único argumento ao Object.create. A implementação oficial também suporta um segundo argumento opcional que lhe permite adicionar mais propriedades ao objecto.criado.

        Funções de seta

        Funções de seta não têm as suas próprias this palavra-chave. Como resultado, as funções de seta não podem ser funções construtoras e se tentar invocar uma função de seta com a new palavra-chave, irá lançar um erro.

        const Animal = () => {}const leo = new Animal() // Error: Animal is not a constructor

        Também, porque demonstrámos acima que o padrão pseudo-clássico não pode ser usado com funções de seta, as funções de seta também não têm um prototype propriedade.

        const Animal = () => {}console.log(Animal.prototype) // undefined

    Previous articleDIRECTORYNext article Como ver os jogos New York Knicks ao vivo

    Deixe uma resposta Cancelar resposta

    O seu endereço de email não será publicado. Campos obrigatórios marcados com *

    Artigos recentes

    • Como montar um mineiro Bitcoin ASIC
    • Chris Martin tem aniversário na Disneylândia com Dakota Johnson
    • O que é um Site de Superfundo?
    • Echolalia: Os factos para além da “conversa de papagaio”, escrita, e eco
    • Lord of the Flies Quotes
    • Um Guia para Principiantes de Pegging
    • 42 Receitas de Sopa de Crockpot Saudável
    • 3 riscos surpreendentes de má postura
    • Tina Fey Biografia
    • O que são Correntes Oceânicas?

    Arquivo

    • Abril 2021
    • Março 2021
    • Fevereiro 2021
    • Janeiro 2021
    • Dezembro 2020
    • Novembro 2020
    • Outubro 2020
    • Setembro 2020
    • Agosto 2020
    • Julho 2020
    • Junho 2020
    • Maio 2020
    • Abril 2020
    • DeutschDeutsch
    • NederlandsNederlands
    • EspañolEspañol
    • FrançaisFrançais
    • PortuguêsPortuguês
    • ItalianoItaliano
    • PolskiPolski

    Meta

    • Iniciar sessão
    • Feed de entradas
    • Feed de comentários
    • WordPress.org
    Posterity WordPress Theme