Se eu tiver uma referência a um objeto:
var test = {};
que potencialmente (mas não imediatamente) terão objetos aninhados, algo como:
{level1: {level2: {level3: "level3"}}};
Qual é a melhor maneira de verificar a existência de propriedade em objetos profundamente aninhados?
alert(test.level1);
produz undefined
, mas alert(test.level1.level2.level3);
falha.
Atualmente, estou fazendo algo parecido com isto:
if(test.level1 && test.level1.level2 && test.level1.level2.level3) {
alert(test.level1.level2.level3);
}
mas eu queria saber se existe uma maneira melhor.
javascript
object
properties
nested
user113716
fonte
fonte
Respostas:
Você deve fazer isso passo a passo se não quiser um,
TypeError
porque se um dos membros fornull
orundefined
e tentar acessar um membro, uma exceção será lançada.Você pode simplesmente fazer
catch
a exceção ou criar uma função para testar a existência de vários níveis, algo como isto:ATUALIZAÇÃO ES6:
Aqui está uma versão mais curta da função original, usando os recursos e a recursão do ES6 (também está no formulário de chamada de cauda apropriado ):
No entanto, se você deseja obter o valor de uma propriedade aninhada e não apenas verificar sua existência, aqui está uma função simples de uma linha:
A função acima permite que você obtenha o valor das propriedades aninhadas, caso contrário, retornará
undefined
.ATUALIZAÇÃO 2019-10-17:
A proposta de encadeamento opcional chegou ao Estágio 3 no processo do comitê ECMAScript , permitindo acessar com segurança propriedades profundamente aninhadas, usando o token
?.
, o novo operador de encadeamento opcional :Se algum dos níveis acessados for
null
ouundefined
a expressão será resolvidaundefined
por si mesma.A proposta também permite lidar com chamadas de método com segurança:
A expressão acima produzirá
undefined
seobj
,obj.level1
ouobj.level1.method
sãonull
ouundefined
, caso contrário irá chamar a função.Você pode começar a jogar com esse recurso com Babel usando o plug-in de encadeamento opcional .Desde o Babel 7.8.0 , o ES2020 é suportado por padrão
Veja este exemplo no Babel REPL.
🎉🎉UPDATE: dezembro de 2019 🎉🎉
A proposta de encadeamento opcional finalmente chegou ao Estágio 4 na reunião de dezembro de 2019 do comitê do TC39. Isso significa que esse recurso fará parte do Padrão ECMAScript 2020 .
fonte
arguments
não é realmente uma matriz.Array.prototype.slice.call(arguments)
converte-o em uma matriz formal. Aprendervar obj = arguments[0];
e começar a partirvar i = 1
em vez de copiar oarguments
objetoargs
declaração da variável pode ser removida e...args
pode ser usada como o segundo argumento para ocheckNested
método. developer.mozilla.org/en/docs/Web/JavaScript/Reference/…Aqui está um padrão que peguei de Oliver Steele :
De fato, todo o artigo é uma discussão de como você pode fazer isso em javascript. Ele decide usar a sintaxe acima (que não é tão difícil de ler quando você se acostuma) como um idioma.
fonte
(test || {})
é que, se o teste for indefinido, você está fazendo({}.level1 || {})
. Obviamente,{}.level1
é indefinido, o que significa que você está fazendo{}.level2
e assim por diante.test
não for declarado, haverá um ReferenceError , mas isso não é um problema, porque se não for declarado, há um bug a ser corrigido, portanto o erro é uma coisa boa.if(test.level1 && test.level1.level2 && test.level1.level2.level3)
Atualizar
Parece que o lodash foi adicionado
_.get
para que todas as suas propriedades aninhadas obtenham necessidades.https://lodash.com/docs#get
Resposta anterior
Os usuários do lodash podem aproveitar o lodash.contrib, que possui alguns métodos que atenuam esse problema .
getPath
Assinatura:
_.getPath(obj:Object, ks:String|Array)
Obtém o valor em qualquer profundidade em um objeto aninhado com base no caminho descrito pelas chaves fornecidas. As chaves podem ser fornecidas como uma matriz ou como uma sequência separada por pontos. Retorna
undefined
se o caminho não puder ser alcançado.fonte
function isPathDefined(object, path) { return typeof _.getPath(object, path) !== 'undefined'; }
_.get(countries, 'greece.sparta.playwright', 'default'); // → 'default' _.has(countries, 'greece.spart.playwright') // → false
var url = _.get(e, 'currentTarget.myurl', null) || _.get(e, 'currentTarget.attributes.myurl.nodeValue', null) || null
Fiz testes de desempenho (obrigado cdMinix por adicionar o lodash) em algumas das sugestões propostas para esta pergunta com os resultados listados abaixo.
Quebra de Objetos (de Oliver Steele) - 34% - mais rápido
Solução original (sugerida em questão) - 45%
checkNested - 50%
get_if_exist - 52%
validChain - 54%
objHasKeys - 63%
nestedPropertyExists - 69%
_.get - 72%
mais profundo - 86%
palhaços tristes - 100% - mais lento
fonte
_.get()
? qual é o desempenho em comparação com essas respostas?reduce
outry/catch
por conveniência.try { test.level1.level2.level3 } catch (e) { // some logger e }
Você pode ler uma propriedade do objeto em qualquer profundidade, se você lida com o nome como uma string:
't.level1.level2.level3'
.Retorna
undefined
se algum dos segmentos forundefined
.fonte
Se você estiver codificando no ambiente ES6 (ou usando 6to5 ), poderá aproveitar a sintaxe da função de seta :
Em relação ao desempenho, não há penalidade de desempenho por usar o
try..catch
bloco se a propriedade estiver configurada. Há um impacto no desempenho se a propriedade estiver desmarcada.Considere simplesmente usar
_.has
:fonte
try-catch
abordagem é a melhor resposta. Há uma diferença filosófica entre consultar um objeto para seu tipo e supor que a API exista e falhar de acordo, se não existir. O último é mais apropriado em idiomas de baixa digitação. Consulte stackoverflow.com/a/408305/2419669 . Atry-catch
abordagem também é muito mais clara do queif (foo && foo.bar && foo.bar.baz && foo.bar.baz.qux) { ... }
.e quanto a
fonte
hasOwnProperty()
n vezes?Resposta ES6, exaustivamente testada :)
→ veja Codepen com cobertura completa de teste
fonte
===
para os diferentes falsys e adicionei o teste. Se você tiver uma idéia melhor, me avise).false
. E então você pode querer ter um valor no seu objeto definido comoundefined
(eu sei que é estranho, mas é JS). Eu fiz um valor falso positivo definido como'Prop not Found'
:const hasTruthyProp = prop => prop === 'Prop not found' ? false : true const path = obj => path => path.reduce((obj, prop) => { return obj && obj.hasOwnProperty(prop) ? obj[prop] : 'Prop not found' }, obj) const myFunc = compose(hasTruthyProp, path(obj))
Você também pode usar a proposta de encadeamento opcional do tc39 junto com o babel 7 - tc39-proposta-encadeamento opcional
O código ficaria assim:
fonte
Eu tentei uma abordagem recursiva:
Ele
! keys.length ||
inicia a recursão para que não execute a função sem as teclas restantes para testar. Testes:Estou usando-o para imprimir uma visualização html amigável de um monte de objetos com valores / chaves desconhecidos, por exemplo:
fonte
Eu acho que o script a seguir fornece uma representação mais legível.
declare uma função:
use-o assim:
Eu chamo isso de "técnica do palhaço triste" porque está usando o sinal o (
EDITAR:
aqui está uma versão para TypeScript
fornece verificações de tipo em tempo de compilação (assim como o intellisense, se você usar uma ferramenta como o Visual Studio)
o uso é o mesmo:
mas desta vez o intellisense funciona!
Além disso, você pode definir um valor padrão:
fonte
°0o <°(())))><
Object
tipo. +1.criar um global
function
e usar em todo o projetotente isso
se condição
fonte
Uma maneira simples é esta:
Ele
try/catch
captura os casos em que qualquer um dos objetos de nível superior, como test, test.level1, test.level1.level2 não está definido.fonte
Não vi nenhum exemplo de alguém usando Proxies
Então eu criei o meu. O melhor de tudo é que você não precisa interpolar strings. Você pode realmente retornar uma função de
objetocapaz de cadeia e fazer algumas coisas mágicas com ela. Você pode até chamar funções e obter índices de matriz para verificar objetos profundosMostrar snippet de código
O código acima funciona bem para coisas síncronas. Mas como você testaria algo assíncrono como esta chamada ajax? Como você testa isso? e se a resposta não for json quando retornar um erro de 500 http?
Certifique-se de que você pode usar async / waitit para se livrar de alguns retornos de chamada. Mas e se você pudesse fazê-lo ainda mais magicamente? algo parecido com isto:
Você provavelmente se pergunta onde estão todas as promessas e
.then
cadeias ... isso pode estar bloqueando tudo o que você sabe ... mas, usando a mesma técnica Proxy com promessa, você pode realmente testar um caminho complexo profundamente aninhado para sua existência sem nunca escrever uma única funçãoMostrar snippet de código
fonte
Com base nesta resposta , criei esta função genérica usando a
ES2015
qual resolveria o problemafonte
function validChain (object, path) { return path.split('.').reduce((a, b) => (a || { })[b], object) !== undefined }
Eu criei uma pequena função para obter propriedades de objetos aninhados com segurança.
Ou uma versão mais simples, mas um pouco ilegível:
Ou ainda mais curto, mas sem fallback na bandeira do falso:
Eu tenho teste com:
E aqui estão alguns testes:
Para ver todo o código com a documentação e os testes que eu tentei, você pode verificar a minha lista do github: https://gist.github.com/vsambor/3df9ad75ff3de489bbcb7b8c60beebf4#file-javascriptgetnestedvalues-js
fonte
Uma versão ES5 mais curta da excelente resposta do @ CMS:
Com um teste semelhante:
fonte
checkObjHasKeys(test, ['level1', 'level2', 'asdf', 'asdf']);
success = false;
parareturn false
. Você deve sair quando souber que quebra, nada mais profundo pode existir quando for nulo ou indefinido. Isso impediria os erros nos itens aninhados mais profundos, pois eles obviamente também não existem.Eu acho que isso é uma ligeira melhora (se torna um liner):
Isso funciona porque o operador && retorna o operando final avaliado (e causa um curto-circuito).
fonte
Eu estava procurando o valor a ser retornado se a propriedade existir, então modifiquei a resposta do CMS acima. Aqui está o que eu vim com:
fonte
A resposta dada pelo CMS funciona bem com a seguinte modificação para verificações nulas também
fonte
As opções a seguir foram elaboradas a partir desta resposta . Mesma árvore para ambos:
Parar de pesquisar quando indefinido
Garanta que cada nível, um por um
fonte
Eu sei que essa pergunta é antiga, mas eu queria oferecer uma extensão adicionando isso a todos os objetos. Eu sei que as pessoas tendem a desaprovar o uso do protótipo de objeto para a funcionalidade estendida de objetos, mas não acho nada mais fácil do que fazer isso. Além disso, agora é permitido com o método Object.defineProperty .
Agora, para testar qualquer propriedade em qualquer objeto, você pode simplesmente fazer:
Aqui está um jsfiddle para testá-lo e, em particular, inclui alguns jQuery que são quebrados se você modificar o Object.prototype diretamente devido à propriedade tornar-se enumerável. Isso deve funcionar bem com bibliotecas de terceiros.
fonte
Isso funciona com todos os objetos e matrizes :)
ex:
esta é minha versão aprimorada da resposta de Brian
Eu usei _has como o nome da propriedade, pois pode entrar em conflito com a propriedade has existente (ex: mapas)
Aqui está o violino
fonte
Aqui está minha opinião - a maioria dessas soluções ignora o caso de uma matriz aninhada, como em:
Eu posso querer procurar
obj.l2[0].k
Com a função abaixo, você pode fazer
deeptest('l2[0].k',obj)
A função retornará true se o objeto existir, false caso contrário
fonte
Agora também podemos usar o
reduce
loop através de chaves aninhadas:fonte
Você pode fazer isso usando a função recursiva. Isso funcionará mesmo se você não souber o nome de todas as chaves de objetos aninhadas.
fonte
existe uma função aqui no codecode (safeRead) que fará isso de maneira segura ...
se alguma propriedade for nula ou indefinida, uma sequência vazia será retornada
fonte
Com base em um comentário anterior , aqui está outra versão em que o objeto principal também não pôde ser definido:
fonte
Eu escrevi minha própria função que segue o caminho desejado e tem uma função de retorno de chamada boa e ruim.
Uso:
fonte
fonte