Underscore: Erro de métodos de cadeia de matriz em valores que não são de matriz

Criado em 20 mar. 2016  ·  6Comentários  ·  Fonte: jashkenas/underscore

Erro de métodos de cadeia de matriz em valores que não são de matriz, o que é diferente de outros métodos de categoria de matriz.

_().pop(); // error
_('').pop(); // error
_().first() // undefined

Comentários muito úteis

Isso traz outro problema, devemos converter array-likes para array?

_('hello').first() // 'h'
_('hello').pop() // ?

Todos 6 comentários

Isso traz outro problema, devemos converter array-likes para array?

_('hello').first() // 'h'
_('hello').pop() // ?

Isso parece ainda ser um problema - nós o encontramos depois de aumentar nossa versão de 1.8.3 para 1.9.0

Edit: Parece que isso foi corrigido em 1.9.1, em caso afirmativo, esse problema precisa permanecer em aberto?

Este comportamento ainda está presente em 1.10.2.

Um contraste um pouco mais convincente na minha opção, pois ambos envolvem a modificação de um valor no local:

Object.assign(undefined, {a: 1})  // error
_().extend({a: 1})  // undefined

Array.prototype.push.call(undefined, 1)  // error
_().push(1)  // error

Concordo um pouco que isso é inconsistente.

Vale a pena considerar em que tipo de situação do mundo real isso provavelmente seria encontrado e o que esperar do objeto encapsulado nesses casos.

No meio de uma cadeia, o objeto encapsulado é previsível. Por exemplo, na cadeia abaixo, o objeto encapsulado que é passado para push será Array ou undefined . Por esta razão, eu diria que os métodos wrapper Array devem implementar uma verificação nula. Isso é facil.

_.chain(something).map(f).push(x)  // hoping to push x to an array

Se o objeto encapsulado previsto for mais provável que seja algo diferente de uma matriz, então é sem dúvida um erro do programador. Eu não me importo de lançar uma exceção nesse caso.

_.chain(something).map(f).join('').push(x)  // hoping to push x to a string??

No início de uma cadeia, a história pode parecer um pouco diferente à primeira vista, mas vou argumentar que é a mesma e que devemos deixá-la em cheque nulo. Pelo menos deve ser o caso de o programador ter algum motivo para esperar que something seja um array mutável, pois, caso contrário, não haveria razão para chamar push :

function giveMeAMutableArraylike(something) {
    _.chain(something).push(x)
}

A expectativa de que something seja um array mutável do tipo pode não ser atendida por vários motivos:

  1. giveMeAMutableArraylike acabou sendo pago com null ou undefined por qualquer motivo. Isso é comparável ao primeiro exemplo de cadeia no meio e pode ser resolvido com a mesma verificação de nulo.
  2. O chamador está tentando fazer giveMeAMutableArraylike fazer algo que obviamente não pode fazer, ou seja, quebrar o contrato. Isso é comparável ao segundo exemplo da cadeia no meio. Novamente, acho que não há problema em lançar um erro neste caso.
  3. something é um valor simples em vez de um array com um único elemento, ou seja, v em vez de [v] . Essa é uma situação comum em muitas APIs JavaScript. Se o contrato de giveMeAMutableArraylike permitir isso, obviamente é errado lançar um erro, independentemente do que v seja.

Neste último caso, o Underscore não pode saber se giveMeAMutableArraylike permite elementos simples simples ou não, então cabe ao programador de giveMeAMutableArraylike implementar seu contrato específico. Não há nada que uma biblioteca como Underscore possa fazer que faça a coisa certa para todos os contratos possíveis:

| Comportamento atual | Envolver em array de elemento único
---|---|---
Contrato permite elemento único nu | _Programador precisa intervir_ | O sublinhado faz a coisa certa automaticamente
Contrato não permite elemento único nu | O sublinhado faz a coisa certa automaticamente | _O programador precisa intervir_

Além disso, como você decide se um valor deve ser encapsulado? Alguns contratos podem querer encapsular qualquer coisa que não seja Array , enquanto outros podem querer passar arguments e objetos simples como estão. Novamente, isso é algo que uma biblioteca como Underscore não pode adivinhar em nome do usuário.

Situações mais exóticas também são concebíveis, por exemplo, um contrato que permite objetos semelhantes a arrays imutáveis , como strings, e que os converterá em Array primeiro. É impossível cobrir todas as variações possíveis. Pelo princípio da menor mudança, simplesmente não há argumento convincente para apoiar alguns contratos em detrimento de contratos que já foram suportados. Há também um forte argumento para manter a implementação tão mínima quanto possível.

Então, acho que a solução certa é inserir apenas uma verificação nula e deixar por isso mesmo. Esta é a única mudança no comportamento de Underscore que parece defensável em todas as situações concebíveis e também é consistente com o comportamento de _.extend . Uma verificação nula ainda pode ser considerada uma correção de bug, enquanto fazer qualquer coisa além disso rapidamente a transformaria em uma alteração arbitrária.

@jashkenas você poderia iluminar isso? Se você concordar, prepararei um pull request que implementa a verificação nula.

@jgonggrijp — Você pode elaborar em uma frase ou duas o comportamento que deseja implementar com a verificação nula?

É que os métodos de matriz lançarão explicitamente uma exceção quando chamados em um valor nulo? Ou que eles vão noop quando chamados em um valor nulo?

@jashkenas Sim. O comportamento que eu implementaria é um no-op, seguindo o estilo das funções do array Underscore:

https://github.com/jashkenas/underscore/blob/4cf715f593805ba8d7c5685cd06c82b3cd9b55ae/modules/index.js#L495

Exceto que a verificação length não seria necessária, então eu apenas inseriria

if (obj == null) return chainresult(this, obj);

entre estas duas linhas:

https://github.com/jashkenas/underscore/blob/4cf715f593805ba8d7c5685cd06c82b3cd9b55ae/modules/index.js#L1654 -L1655

Além de testes, é claro, e se esses testes revelarem uma necessidade, talvez também uma verificação nula que transforme o método em no-op antes desta linha:

https://github.com/jashkenas/underscore/blob/4cf715f593805ba8d7c5685cd06c82b3cd9b55ae/modules/index.js#L1665

Soa bem para mim!

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

dmaicher picture dmaicher  ·  9Comentários

umarfarooq125 picture umarfarooq125  ·  8Comentários

zackschuster picture zackschuster  ·  5Comentários

arypbatista picture arypbatista  ·  3Comentários

xiaoliwang picture xiaoliwang  ·  3Comentários