İç içe JavaScript nesne anahtarının var olup olmadığını test edin


692

Bir nesneye başvurum varsa:

var test = {};

potansiyel olarak (ancak hemen değil) iç içe geçmiş nesneler olacaktır, örneğin:

{level1: {level2: {level3: "level3"}}};

Derin iç içe nesnelerde mülk olup olmadığını kontrol etmenin en iyi yolu nedir?

alert(test.level1);verir undefined, ancak alert(test.level1.level2.level3);başarısız olur.

Şu anda böyle bir şey yapıyorum:

if(test.level1 && test.level1.level2 && test.level1.level2.level3) {
    alert(test.level1.level2.level3);
}

ama daha iyi bir yol olup olmadığını merak ediyordum.


1
son zamanlarda sorulan teğetsel olarak alakalı bir soruyu kontrol etmek isteyebilirsiniz stackoverflow.com/questions/2525943/…
Anurag



Level3 özelliği yanlışsa, mevcut yaklaşımınızın potansiyel bir sorunu vardır, bu durumda, özellik mevcut olsa bile nfalse bu örneğe bir göz atın lütfen jsfiddle.net/maz9bLjx
GibboK

10
sadece try catch kullanabilirsiniz
Raghavendra

Yanıtlar:


489

Bunu istemiyorsanız adım adım yapmanız gerekir, TypeErrorçünkü üyelerden biri nullveyaundefined ise ve bir üyeye erişmeye çalıştığınızda bir istisna atılır.

İstisnayı ya da catchbirden fazla seviyenin varlığını test etmek için aşağıdaki gibi bir işlev yapabilirsiniz:

function checkNested(obj /*, level1, level2, ... levelN*/) {
  var args = Array.prototype.slice.call(arguments, 1);

  for (var i = 0; i < args.length; i++) {
    if (!obj || !obj.hasOwnProperty(args[i])) {
      return false;
    }
    obj = obj[args[i]];
  }
  return true;
}

var test = {level1:{level2:{level3:'level3'}} };

checkNested(test, 'level1', 'level2', 'level3'); // true
checkNested(test, 'level1', 'level2', 'foo'); // false

ES6 GÜNCELLEME:

ES6 özelliklerini ve özyinelemeyi kullanarak orijinal işlevin daha kısa bir sürümü: (ayrıca uygun kuyruk çağrı formundadır):

function checkNested(obj, level,  ...rest) {
  if (obj === undefined) return false
  if (rest.length == 0 && obj.hasOwnProperty(level)) return true
  return checkNested(obj[level], ...rest)
}

Ancak, iç içe bir özelliğin değerini almak ve yalnızca varlığını denetlemek istemiyorsanız, basit bir tek satırlık işlev şunlardır:

function getNested(obj, ...args) {
  return args.reduce((obj, level) => obj && obj[level], obj)
}

const test = { level1:{ level2:{ level3:'level3'} } };
console.log(getNested(test, 'level1', 'level2', 'level3')); // 'level3'
console.log(getNested(test, 'level1', 'level2', 'level3', 'length')); // 6
console.log(getNested(test, 'level1', 'level2', 'foo')); // undefined
console.log(getNested(test, 'a', 'b')); // undefined

Yukarıdaki fonksiyon, yuvalanmış özelliklerin değerini almanıza izin verir, aksi takdirde geri döner undefined.

GÜNCELLEME 2019-10-17:

İsteğe bağlı zincirleme önerisi üzerine Aşama 3 ulaşmış ECMAScript Komitesi süreci , bu izin verecektir size belirteci kullanarak güvenli bir şekilde erişim içe geçmiş özelliklere için ?., yeni opsiyonel zincirleme operatörü :

const value = obj?.level1?.level2?.level3 

Erişilen düzeylerden herhangi biri nullveya undefinedifadeundefined kendi başına çözülürse.

Teklif ayrıca yöntem çağrılarını güvenli bir şekilde yönetmenize olanak tanır:

obj?.level1?.method();

Yukarıda ifade üretecek undefinedhalinde obj, obj.level1ya da obj.level1.methodvardır nullya da undefinedaksi takdirde işlevini çağırır.

İsteğe bağlı zincirleme eklentisini kullanarak Babel ile bu özellik ile oynamaya başlayabilirsiniz .

Babel 7.8.0'dan beri , ES2020 varsayılan olarak desteklenmektedir

Kontrol Bu örneği Babel Repl üzerinde.

D GÜNCELLEME: Aralık 2019 🎉🎉

İsteğe bağlı zincirleme teklifi sonunda TC39 komitesinin Aralık 2019 toplantısında Aşama 4'e ulaştı . Bu, bu özelliğin ECMAScript 2020 Standardının bir parçası olacağı anlamına gelir .


4
argumentsaslında bir dizi değildir. Array.prototype.slice.call(arguments)resmi bir diziye dönüştürür. Öğrenmek
deefour

23
bu , nesneyi kopyalamak yerine yapmak ve başlamak için çok daha verimli olacaktırvar obj = arguments[0];var i = 1arguments
Claudiu

2
Kemer sıkma uğruna denemek / yakalamak için bir sürüm bir araya getirdim ve sürpriz yok - performans korkunç (bazı nedenlerden dolayı Safari hariç). Aşağıda, oldukça iyi performans gösteren bazı cevaplar ve Claudiu'nun seçilen cevaptan önemli ölçüde daha fazla performans gösteren modifikasyonu bulunmaktadır. Burada jsperf Bkz jsperf.com/check-if-deep-property-exists-with-willnotthrow
netpoetica

3
ES6'da argsdeğişken bildirimi kaldırılabilir ve yöntem ...args için ikinci argüman olarak kullanılabilir checkNested. developer.mozilla.org/tr/docs/Web/JavaScript/Reference/…
Vernon

7
Bu çok sürdürülemez. Herhangi bir özellik anahtarı değişirse (değişecekse), projedeki tüm geliştiricilerin tüm kod tabanını 'dize araması' gerekir. Bu çok daha büyük bir sorun
çıkardığı

357

İşte Oliver Steele'den aldığım bir model :

var level3 = (((test || {}).level1 || {}).level2 || {}).level3;
alert( level3 );

Aslında tüm makale bunu javascript'te nasıl yapabileceğinizin bir tartışmasıdır. Yukarıdaki sözdizimini (alıştıktan sonra okumak zor değil) bir deyim olarak kullanmaya karar verir.


8
@wared Bence çoğunlukla ne kadar özlü olduğu için ilginç. Bağlantılı gönderide performans özellikleri hakkında ayrıntılı bir tartışma var. Evet, her zaman tüm testleri yapar, ancak geçici değişkenler oluşturmayı önler ve her seferinde yeni bir boş nesne oluşturma yükünü önlemek istiyorsanız bir değişkene {} takma adı ekleyebilirsiniz. Vakaların% 99'unda hızın önemli olmasını beklemiyordum ve bunun olduğu durumlarda profillemenin yerini tutmaz.
Gabe Moothart

9
@MuhammadUmer Hayır, asıl nokta, (test || {})eğer test tanımsızsa, o zaman yapıyorsunuzdur ({}.level1 || {}). Tabii ki, {}.level1tanımsız, yani bunu yapıyorsunuz {}.level2, vb.
Joshua Taylor

3
@JoshuaTaylor: Bence eğer testbeyan edilmezse , bir ReferenceError olacak demektir , ama bu bir problem değildir, çünkü beyan edilmezse düzeltilecek bir hata vardır, bu yüzden hata iyi bir şeydir.

34
dediğin "olmadığı da zor okumak için bir kez sen alışması kendisine" . Eh, bunlar size işaretler vardır biliyorum zaten bu bir olduğunu karışıklık . Öyleyse neden bu çözümü öneriyorsunuz? Yazım hatalarına eğilimlidir ve okunabilirliğe kesinlikle hiçbir şey vermez . Sadece bak! İ Eğer zorunda çirkin satır yazmak, olması asswell gereken okunabilir ; yani ben sadece sopa ile gidiyorumif(test.level1 && test.level1.level2 && test.level1.level2.level3)
Sharky

8
Bir şeyi kaçırmadıkça, bu yanlış olabilecek mantıksal son özellikler için işe yaramaz ... ne yazık ki. Aksi takdirde bu deyimi seviyorum.
T3db0t

261

Güncelleme

Görünüşe göre lodash tüm iç içe mülk alma ihtiyaçlarınız için ekledi _.get .

_.get(countries, 'greece.sparta.playwright')

https://lodash.com/docs#get


Önceki cevap

Lodash kullanıcıları, bu sorunu hafifleten birkaç yöntemi olan lodash.contrib'in keyfini çıkarabilir .

getPath

İmza: _.getPath(obj:Object, ks:String|Array)

Yuvalanmış bir nesnede verilen tuşlarla tanımlanan yola göre değeri herhangi bir derinlikte alır. Anahtarlar bir dizi veya nokta ile ayrılmış bir dize olarak verilebilir. undefinedYola ulaşılamıyorsa döndürür .

var countries = {
        greece: {
            athens: {
                playwright:  "Sophocles"
            }
        }
    }
};

_.getPath(countries, "greece.athens.playwright");
// => "Sophocles"

_.getPath(countries, "greece.sparta.playwright");
// => undefined

_.getPath(countries, ["greece", "athens", "playwright"]);
// => "Sophocles"

_.getPath(countries, ["greece", "sparta", "playwright"]);
// => undefined

Lodash gerçekten bir _.isPathDefined (obj, pathString) yöntemine ihtiyaç duyar.
Matthew Payne

@MatthewPayne Belki iyi olurdu, ama gerçekten gerekli değil. Gerçekten kendiniz kolayca yapabilirsinizfunction isPathDefined(object, path) { return typeof _.getPath(object, path) !== 'undefined'; }
Thor84no

11
Lodash'ın kendisi de aynı işlevselliğe sahip:_.get(countries, 'greece.sparta.playwright', 'default'); // → 'default' _.has(countries, 'greece.spart.playwright') // → false
Tom

daha da iyisi _.result olur
Shishir Arora

Birden fazla farklı yol belirlemeniz gerekiyorsa: var url = _.get(e, 'currentTarget.myurl', null) || _.get(e, 'currentTarget.attributes.myurl.nodeValue', null) || null
Simon Hutchison

210

Aşağıda listelenen sonuçlarla birlikte bu soruya önerilen bazı öneriler üzerinde performans testleri yaptım ( lodash eklediğiniz için cdMinix'e teşekkür ederim ).

Feragat # 1 Dizeleri referanslara dönüştürmek gereksiz meta-programlamadır ve muhtemelen en iyi şekilde kaçınılmalıdır. Başlamak için referanslarınızı takip etmeyin. Bu sorudan benzer bir soruya daha fazla bilgi edinin .

Feragat # 2 Burada milisaniye başına milyonlarca işlemden bahsediyoruz. Bunların herhangi birinin çoğu kullanım durumunda çok fazla fark yaratması pek olası değildir. Her birinin sınırlarını bilerek en anlamlı olanı seçin. Benim reduceiçin kolaylık gibi bir şeyle giderdim .

Nesne Sarma (Oliver Steele tarafından) -% 34 - en hızlı

var r1 = (((test || {}).level1 || {}).level2 || {}).level3;
var r2 = (((test || {}).level1 || {}).level2 || {}).foo;

Orijinal çözüm (söz konusu önerilir) -% 45

var r1 = test.level1 && test.level1.level2 && test.level1.level2.level3;
var r2 = test.level1 && test.level1.level2 && test.level1.level2.foo;

checkNested -% 50

function checkNested(obj) {
  for (var i = 1; i < arguments.length; i++) {
    if (!obj.hasOwnProperty(arguments[i])) {
      return false;
    }
    obj = obj[arguments[i]];
  }
  return true;
}

get_if_exist -% 52

function get_if_exist(str) {
    try { return eval(str) }
    catch(e) { return undefined }
}

geçerli Zincir -% 54

function validChain( object, ...keys ) {
    return keys.reduce( ( a, b ) => ( a || { } )[ b ], object ) !== undefined;
}

objHasKeys -% 63

function objHasKeys(obj, keys) {
  var next = keys.shift();
  return obj[next] && (! keys.length || objHasKeys(obj[next], keys));
}

iç içe mülk var -% 69

function nestedPropertyExists(obj, props) {
    var prop = props.shift();
    return prop === undefined ? true : obj.hasOwnProperty(prop) ? nestedPropertyExists(obj[prop], props) : false;
}

_.get -% 72

en derin -% 86

function deeptest(target, s){
    s= s.split('.')
    var obj= target[s.shift()];
    while(obj && s.length) obj= obj[s.shift()];
    return obj;
}

üzgün palyaçolar -% 100 - en yavaş

var o = function(obj) { return obj || {} };

var r1 = o(o(o(o(test).level1).level2).level3);
var r2 = o(o(o(o(test).level1).level2).foo);

16
bir testin% 'si ne kadar fazla ise - ÇEVİRİCİ
çığ

2
peki ya peki _.get()? bu cevaplara kıyasla ne kadar başarılı?
beniutek

1
Bunların her yöntemi duruma bağlı olarak diğerlerinden daha yavaş veya daha hızlıdır. Tüm anahtarlar bulunursa "Object Wrap" en hızlı olabilir, ancak anahtarlardan biri bulunamazsa "Yerel çözüm / Orijinal çözüm" daha hızlı olabilir.
2017'de

1
@evilReiko Hiçbir anahtar bulunamazsa herhangi bir yöntem daha yavaş olacaktır, ancak birbirleriyle orantılı olarak hala aynıdır. Ancak haklısınız - bu her şeyden çok entelektüel bir egzersizdir. Burada milisaniye başına bir milyon yinelemeden bahsediyoruz. Çok fazla fark yaratacağı hiçbir kullanım durumu görmüyorum. Ben şahsen ben rahatlık için reduceya da gitmek istiyorum try/catch.
unitario

Performansı ne kadar yüksektry { test.level1.level2.level3 } catch (e) { // some logger e }
Lex

46

Bir dize gibi isim işlemek eğer, her derinlikte bir nesne özelliğini okuyabilir: 't.level1.level2.level3'.

window.t={level1:{level2:{level3: 'level3'}}};

function deeptest(s){
    s= s.split('.')
    var obj= window[s.shift()];
    while(obj && s.length) obj= obj[s.shift()];
    return obj;
}

alert(deeptest('t.level1.level2.level3') || 'Undefined');

undefinedSegmentlerden herhangi biri varsa döndürür undefined.


3
Bu yöntemin, en azından Chrome'da, bazı durumlarda seçilen cevabın @Claudiu değiştirilmiş versiyonundan daha iyi performans gösterdiğini belirtmek gerekir. Performans testine buradan bakın: jsperf.com/check-if-deep-property-exists-with-willnotthrow
netpoetica

28
var a;

a = {
    b: {
        c: 'd'
    }
};

function isset (fn) {
    var value;
    try {
        value = fn();
    } catch (e) {
        value = undefined;
    } finally {
        return value !== undefined;
    }
};

// ES5
console.log(
    isset(function () { return a.b.c; }),
    isset(function () { return a.b.c.d.e.f; })
);

ES6 ortamında kodluyorsanız (veya 6to5 kullanıyorsanız ) ok işlevi sözdiziminden yararlanabilirsiniz:

// ES6 using the arrow function
console.log(
    isset(() => a.b.c),
    isset(() => a.b.c.d.e.f)
);

Performansla ilgili olarak try..catch, mülk ayarlanmışsa blok kullanmanın performans cezası yoktur . Mülk ayarlanmamışsa performans etkisi vardır.

Sadece şunu kullanın _.has:

var object = { 'a': { 'b': { 'c': 3 } } };

_.has(object, 'a');
// → true

_.has(object, 'a.b.c');
// → true

_.has(object, ['a', 'b', 'c']);
// → true

2
Bence try-catchyaklaşım en iyi cevaptır. Bir nesneyi türü için sorgulamak ve API'nın var olduğunu varsaymak ve eğer yoksa buna göre başarısız olmak arasında felsefi bir fark vardır. İkincisi gevşek yazılan dillerde daha uygundur. Bkz. Stackoverflow.com/a/408305/2419669 . try-catchYaklaşım aynı zamanda çok daha nettir if (foo && foo.bar && foo.bar.baz && foo.bar.baz.qux) { ... }.
yangmillstheory

24

ne dersin

try {
   alert(test.level1.level2.level3)
} catch(e) {
 ...whatever

}

15
Deneme / yakalama, bir nesnenin varlığını test etmek için iyi bir yol olduğunu düşünmüyorum: Deneme / yakalama, burada test gibi normal koşulları değil, istisnaları ele almak içindir. Her adımda (typeof foo == "undefined") daha iyi olduğunu düşünüyorum - ve genel olarak, böyle derinden iç içe geçmiş özelliklerle çalışıyorsanız, muhtemelen bazı yeniden düzenleme gerekli. Ayrıca, istisna atılırsa try / catch, Firebug'da (ve hatanın açıldığı herhangi bir tarayıcıda) kesintiye neden olur.
Sam Dutton

Buna oy veriyorum, çünkü başka çözümler kullanırsanız tarayıcı varlığını iki kez kontrol edecektir. Diyelim ki ´acb = 2´'yi aramak istiyorsunuz. Tarayıcı zorundadır değeri değiştirmeden önce varlığını kontrol (aksi takdirde işletim sistemi tarafından yakalanan bir bellek hatası olacaktır).

4
Soru hala devam ediyor: bir cadı, tarayıcılar için bir deneme yakalama veya hasOwnProperty()n kez araması için daha hızlı mı?

14
Bu neden tekrar kötü? Bu bana en temiz görünüyor.
Austin Pray

Ben söyleyebilirim: özelliği daha deneyin var deneyin bir deneyin blok içine sarmak için iyidir. Eğer mevcut değilse bir hatadır. Ama sadece tembel ve mülkiyet var durumda için catch bloğuna düzenli kod koymak deneyin / catch yanlış kullanılır. Burada bir if / else veya benzeri bir şey gereklidir.
robsch

18

ES6 cevap, iyice test edildi :)

const propExists = (obj, path) => {
    return !!path.split('.').reduce((obj, prop) => {
        return obj && obj[prop] ? obj[prop] : undefined;
    }, obj)
}

Test kapsamının tamamını içeren Codepen'e bakın


Düz pervane değerini 0 olarak ayarlayarak testlerinizi başarısızlıkla sonuçlandırdım. Zorlama türü ile ilgilenmelisiniz.
germain

@germain Bu sizin için uygun mu? (Ben açıkça ===farklı falsys karşılaştırmak ve test ekledi. Eğer daha iyi bir fikriniz varsa, bana bildirin).
Frank Nocke

Ben düz pervane değerini ayarlayarak tekrar test başarısız yaptı false. Ve sonra nesnenizde bir değere sahip olmak isteyebilirsiniz undefined(garip ama JS olduğunu biliyorum). Olumlu bir yanlış değer olarak ayarladım '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))
germain

Kodlarımı (sağ üst, kolay) kesebilir, testleri düzeltebilir ve ekleyebilir ve bana URL'nizi gönderebilir misiniz? Teşekkürler =)
Frank Nocke

(Büyük) bir 3. parti kütüphanesine kaçmak ... mümkün, ama benim tercihim değil.
Frank Nocke

17

Ayrıca tc39 isteğe bağlı zincirleme önerisini babel 7 ile birlikte kullanabilirsiniz - tc39-teklif-isteğe bağlı-zincirleme

Kod şöyle görünür:

  const test = test?.level1?.level2?.level3;
  if (test) alert(test);

Bazı TC39 üyelerinin itirazları olduğu için bu sözdiziminin neredeyse kesinlikle değişeceğini unutmayın.
jhpratt GOFUNDME RELICENSING

Muhtemelen ama bu zaman içinde bir şekilde mevcut olacak ve önemli olan tek şey bu ... JS'de en çok özlediğim özelliklerden biri.
Goran.it

11

Özyinelemeli bir yaklaşım denedim:

function objHasKeys(obj, keys) {
  var next = keys.shift();
  return obj[next] && (! keys.length || objHasKeys(obj[next], keys));
}

! keys.length ||Özyineleme dışına başladı testin soldan hiçbir tuşlarıyla işlevini çalışmayacak şekilde. Testler:

obj = {
  path: {
    to: {
      the: {
        goodKey: "hello"
      }
    }
  }
}

console.log(objHasKeys(obj, ['path', 'to', 'the', 'goodKey'])); // true
console.log(objHasKeys(obj, ['path', 'to', 'the', 'badKey']));  // undefined

Örneğin bilinmeyen anahtar / değerleri olan nesnelerin bir grup dostu bir html görünümü yazdırmak için kullanıyorum, örneğin:

var biosName = objHasKeys(myObj, 'MachineInfo:BiosInfo:Name'.split(':'))
             ? myObj.MachineInfo.BiosInfo.Name
             : 'unknown';

9

Aşağıdaki komut dosyası daha okunabilir temsil verir düşünüyorum.

bir işlev bildir:

var o = function(obj) { return obj || {};};

sonra şu şekilde kullanın:

if (o(o(o(o(test).level1).level2).level3)
{

}

Ben buna "üzgün palyaço tekniği" diyorum çünkü o (


DÜZENLE:

İşte TypeScript için bir sürüm

derleme zamanında tür denetimleri verir (ayrıca Visual Studio gibi bir araç kullanıyorsanız intellisense)

export function o<T>(someObject: T, defaultValue: T = {} as T) : T {
    if (typeof someObject === 'undefined' || someObject === null)
        return defaultValue;
    else
        return someObject;
}

kullanım aynı:

o(o(o(o(test).level1).level2).level3

ama bu sefer intellisense çalışıyor!

artı, varsayılan bir değer ayarlayabilirsiniz:

o(o(o(o(o(test).level1).level2).level3, "none")

1
°0o <°(())))><
Daniel W.

1
Bunu beğendim, çünkü dürüsttür ve Objecttipinizi bilmediğinizde yüzünüze "tanımsız" atar . +1.


Teşekkürler Sventies. Yorumunu seviyorum. Bakmak oldukça güzel bir açı - bu koşullar çoğunlukla "ifs" de kullanılır ve her zaman dış parantezlerle çevrilidir. Yani, evet, çoğunlukla mutlu bir palyaço :)))
VeganHunter

Bunun için gitmek için gerçekten parantezlere aşık olmanız gerekiyor ...
Bastien7

7

küresel bir yarat function ve tüm projede kullanın

bunu dene

function isExist(arg){
   try{
      return arg();
   }catch(e){
      return false;
   }
}

let obj={a:5,b:{c:5}};

console.log(isExist(()=>obj.b.c))
console.log(isExist(()=>obj.b.foo))
console.log(isExist(()=>obj.test.foo))

eğer durum

if(isExist(()=>obj.test.foo)){
   ....
}

Harika çalışıyor. Basit ve verimli
gbland777

6

Bunun basit bir yolu şudur:

try {
    alert(test.level1.level2.level3);
} catch(e) {
    alert("undefined");    // this is optional to put any output here
}

try/catchYüksek seviyede bir test test.level1 nesneleri gibi zaman için durumlarda yakalar test.level1.level2 tanımlanmaz.


6

Proxies kullanan birine örnek görmedim

Ben de kendim geldim. Bununla ilgili en güzel şey, dizeleri enterpole etmek zorunda olmamanızdır. Aslında zincirli bir nesne işlevi geri döndürebilir ve onunla büyülü şeyler yapabilirsiniz. Derin nesneleri kontrol etmek için işlevleri çağırabilir ve dizi dizinleri alabilirsiniz

Yukarıdaki kod, eşzamanlı şeyler için iyi çalışır. Ama bu ajax çağrısı gibi asenkron bir şeyi nasıl test edersiniz? Bunu nasıl test edersin? 500 http hatası döndürdüğünde yanıt json değilse ne olur?

window.fetch('https://httpbin.org/get')
.then(function(response) {
  return response.json()
})
.then(function(json) {
  console.log(json.headers['User-Agent'])
})

bazı geri aramalardan kurtulmak için zaman uyumsuz / beklemeyi kullanabileceğinizden emin olun. Ama ya daha sihirli bir şekilde yapabilirseniz? şuna benzer bir şey:

fetch('https://httpbin.org/get').json().headers['User-Agent']

Muhtemelen tüm vaatlerin ve .thenzincirlerin nerede olduğunu merak ediyorsunuz ... bu bildiğiniz her şey için engelliyor olabilir ... ama aynı Proxy tekniğini vaatle kullanarak, tek bir işlev yazmadan varlığını derinden iç içe geçmiş karmaşık yolu test edebilirsiniz.


Birisi ilgileniyorsa, zaman uyumsuz sürümünü npm'de yayınlıyorum
Endless

5

Bu cevaba dayanarak ES2015, sorunu çözecek olan bu genel fonksiyon ile geldim

function validChain( object, ...keys ) {
    return keys.reduce( ( a, b ) => ( a || { } )[ b ], object ) !== undefined;
}

var test = {
  first: {
    second: {
        third: "This is not the key your are looking for"
    }
  }
}

if ( validChain( test, "first", "second", "third" ) ) {
    console.log( test.first.second.third );
}

1
İşte benim son yaklaşımımfunction validChain (object, path) { return path.split('.').reduce((a, b) => (a || { })[b], object) !== undefined }
James Harrington

5

İç içe nesne özelliklerini güvenle almak için küçük bir işlev oluşturdum.

function getValue(object, path, fallback, fallbackOnFalsy) {
    if (!object || !path) {
        return fallback;
    }

    // Reduces object properties to the deepest property in the path argument.
    return path.split('.').reduce((object, property) => {
       if (object && typeof object !== 'string' && object.hasOwnProperty(property)) {
            // The property is found but it may be falsy.
            // If fallback is active for falsy values, the fallback is returned, otherwise the property value.
            return !object[property] && fallbackOnFalsy ? fallback : object[property];
        } else {
            // Returns the fallback if current chain link does not exist or it does not contain the property.
            return fallback;
        }
    }, object);
}

Veya daha basit ama okunamayan bir sürüm:

function getValue(o, path, fb, fbFalsy) {
   if(!o || !path) return fb;
   return path.split('.').reduce((o, p) => o && typeof o !== 'string' && o.hasOwnProperty(p) ? !o[p] && fbFalsy ? fb : o[p] : fb, o);
}

Ya da daha kısa ama sahte bayraklarda geri dönüş olmadan:

function getValue(o, path, fb) {
   if(!o || !path) return fb;
   return path.split('.').reduce((o, p) => o && typeof o !== 'string' && o.hasOwnProperty(p) ? o[p] : fb, o);
}

Şununla testim var:

const obj = {
    c: {
        a: 2,
        b: {
            c: [1, 2, 3, {a: 15, b: 10}, 15]
        },
        c: undefined,
        d: null
    },
    d: ''
}

İşte bazı testler:

// null
console.log(getValue(obj, 'c.d', 'fallback'));

// array
console.log(getValue(obj, 'c.b.c', 'fallback'));

// array index 2
console.log(getValue(obj, 'c.b.c.2', 'fallback'));

// no index => fallback
console.log(getValue(obj, 'c.b.c.10', 'fallback'));

Belgelere ve denediğim testlere sahip tüm kodu görmek için github özümü kontrol edebilirsiniz: https://gist.github.com/vsambor/3df9ad75ff3de489bbcb7b8c60beebf4#file-javascriptgetnestedvalues-js


4

@ CMS'nin mükemmel cevabının daha kısa, ES5 sürümü:

// Check the obj has the keys in the order mentioned. Used for checking JSON results.  
var checkObjHasKeys = function(obj, keys) {
  var success = true;
  keys.forEach( function(key) {
    if ( ! obj.hasOwnProperty(key)) {
      success = false;
    }
    obj = obj[key];
  })
  return success;
}

Benzer bir testle:

var test = { level1:{level2:{level3:'result'}}};
utils.checkObjHasKeys(test, ['level1', 'level2', 'level3']); // true
utils.checkObjHasKeys(test, ['level1', 'level2', 'foo']); // false

bununla ilgili tek sorun, tanımlanmamış anahtarların birden fazla seviyesi varsa, o zaman bir TypeError alırsınız, örneğincheckObjHasKeys(test, ['level1', 'level2', 'asdf', 'asdf']);
JKS

1
Daha uygun bir yöntem olup , her değeri direkt olarak geri döndürülebilir.
RobG

Belki değiştirmek success = false;için return false. Bir kez kırıldığını bildiğinizde kefalet etmelisiniz, null veya undefined olduğunda daha derin bir şey olamaz. Bu, daha derin yuvalanmış öğelerdeki hataları da önleyecektir, çünkü bunlar da yoktur.
Wade

4

Özellik varsa döndürülecek değeri arıyordu, bu yüzden yukarıdaki CMS tarafından cevap değiştirildi. İşte ben geldim:

function getNestedProperty(obj, key) {
  // Get property array from key string
  var properties = key.split(".");

  // Iterate through properties, returning undefined if object is null or property doesn't exist
  for (var i = 0; i < properties.length; i++) {
    if (!obj || !obj.hasOwnProperty(properties[i])) {
      return;
    }
    obj = obj[properties[i]];
  }

  // Nested property found, so return the value
  return obj;
}


Usage:

getNestedProperty(test, "level1.level2.level3") // "level3"
getNestedProperty(test, "level1.level2.foo") // undefined


3

CMS tarafından verilen cevap, null kontroller için aşağıdaki modifikasyonla da iyi çalışır

function checkNested(obj /*, level1, level2, ... levelN*/) 
      {
             var args = Array.prototype.slice.call(arguments),
             obj = args.shift();

            for (var i = 0; i < args.length; i++) 
            {
                if (obj == null || !obj.hasOwnProperty(args[i]) ) 
                {
                    return false;
                }
                obj = obj[args[i]];
            }
            return true;
    }

3

Bu yanıttan başlayarak aşağıdaki seçenekler hazırlanmıştır . Her ikisi için de aynı ağaç:

var o = { a: { b: { c: 1 } } };

Tanımsız olduğunda aramayı durdur

var u = undefined;
o.a ? o.a.b ? o.a.b.c : u : u // 1
o.x ? o.x.y ? o.x.y.z : u : u // undefined
(o = o.a) ? (o = o.b) ? o.c : u : u // 1

Her seviyeyi birer birer sağlayın

var $ = function (empty) {
    return function (node) {
        return node || empty;
    };
}({});

$($(o.a).b).c // 1
$($(o.x).y).z // undefined

3

Bu sorunun eski olduğunu biliyorum, ama bunu tüm nesnelere ekleyerek bir uzantı sunmak istedim. İnsanların genişletilmiş nesne işlevselliği için Nesne prototipini kullanma eğiliminde olduklarını biliyorum, ancak bunu yapmaktan daha kolay bir şey bulamıyorum. Ayrıca, artık Object.defineProperty yöntemiyle kullanılabilir.

Object.defineProperty( Object.prototype, "has", { value: function( needle ) {
    var obj = this;
    var needles = needle.split( "." );
    for( var i = 0; i<needles.length; i++ ) {
        if( !obj.hasOwnProperty(needles[i])) {
            return false;
        }
        obj = obj[needles[i]];
    }
    return true;
}});

Şimdi, herhangi bir nesnede herhangi bir özelliği test etmek için şunları yapabilirsiniz:

if( obj.has("some.deep.nested.object.somewhere") )

İşte sınamak için bir jsfiddle ve özellikle Object numaralandırılabilir özelliği doğrudan numaralandırılması nedeniyle doğrudan değiştirirseniz, bazı jQuery içerir. Bu, üçüncü taraf kütüphaneleriyle iyi çalışmalıdır.


3

Bu hafif bir gelişme olduğunu düşünüyorum (1-astar olur):

   alert( test.level1 && test.level1.level2 && test.level1.level2.level3 )

Bu, && operatörünün değerlendirdiği son işleneni (ve kısa devreleri) döndürmesi nedeniyle çalışır.


Kelimenin tam anlamıyla söylediklerini kopyaladın ve kaçınmak istedin ...
Sean Kendle

3

Bu, tüm nesneler ve dizilerle çalışır :)

örn:

if( obj._has( "something.['deep']['under'][1][0].item" ) ) {
    //do something
}

Bu benim Brian'ın cevabının geliştirilmiş versiyonudur

_Has özelliğini özellik adı olarak kullandım çünkü mevcut has özelliği ile çakışabilir (ör: haritalar)

Object.defineProperty( Object.prototype, "_has", { value: function( needle ) {
var obj = this;
var needles = needle.split( "." );
var needles_full=[];
var needles_square;
for( var i = 0; i<needles.length; i++ ) {
    needles_square = needles[i].split( "[" );
    if(needles_square.length>1){
        for( var j = 0; j<needles_square.length; j++ ) {
            if(needles_square[j].length){
                needles_full.push(needles_square[j]);
            }
        }
    }else{
        needles_full.push(needles[i]);
    }
}
for( var i = 0; i<needles_full.length; i++ ) {
    var res = needles_full[i].match(/^((\d+)|"(.+)"|'(.+)')\]$/);
    if (res != null) {
        for (var j = 0; j < res.length; j++) {
            if (res[j] != undefined) {
                needles_full[i] = res[j];
            }
        }
    }

    if( typeof obj[needles_full[i]]=='undefined') {
        return false;
    }
    obj = obj[needles_full[i]];
}
return true;
}});

İşte keman


3

İşte benim almak - bu çözümlerin çoğu gibi yuvalanmış bir dizi durumunda görmezden:

    obj = {
        "l1":"something",
        "l2":[{k:0},{k:1}],
        "l3":{
            "subL":"hello"
        }
    }

Kontrol etmek isteyebilirim obj.l2[0].k

Aşağıdaki işlevle şunları yapabilirsiniz: deeptest('l2[0].k',obj)

Nesne varsa işlev true döndürür, aksi takdirde false olur

function deeptest(keyPath, testObj) {
    var obj;

    keyPath = keyPath.split('.')
    var cKey = keyPath.shift();

    function get(pObj, pKey) {
        var bracketStart, bracketEnd, o;

        bracketStart = pKey.indexOf("[");
        if (bracketStart > -1) { //check for nested arrays
            bracketEnd = pKey.indexOf("]");
            var arrIndex = pKey.substr(bracketStart + 1, bracketEnd - bracketStart - 1);
            pKey = pKey.substr(0, bracketStart);
			var n = pObj[pKey];
            o = n? n[arrIndex] : undefined;

        } else {
            o = pObj[pKey];
        }
        return o;
    }

    obj = get(testObj, cKey);
    while (obj && keyPath.length) {
        obj = get(obj, keyPath.shift());
    }
    return typeof(obj) !== 'undefined';
}

var obj = {
    "l1":"level1",
    "arr1":[
        {"k":0},
        {"k":1},
        {"k":2}
    ],
    "sub": {
       	"a":"letter A",
        "b":"letter B"
    }
};
console.log("l1: " + deeptest("l1",obj));
console.log("arr1[0]: " + deeptest("arr1[0]",obj));
console.log("arr1[1].k: " + deeptest("arr1[1].k",obj));
console.log("arr1[1].j: " + deeptest("arr1[1].j",obj));
console.log("arr1[3]: " + deeptest("arr1[3]",obj));
console.log("arr2: " + deeptest("arr2",obj));


3

Artık reduceiç içe tuşlar arasında geçiş yapmak için de kullanabiliriz :

// @params o<object>
// @params path<string> expects 'obj.prop1.prop2.prop3'
// returns: obj[path] value or 'false' if prop doesn't exist

const objPropIfExists = o => path => {
  const levels = path.split('.');
  const res = (levels.length > 0) 
    ? levels.reduce((a, c) => a[c] || 0, o)
    : o[path];
  return (!!res) ? res : false
}

const obj = {
  name: 'Name',
  sys: { country: 'AU' },
  main: { temp: '34', temp_min: '13' },
  visibility: '35%'
}

const exists = objPropIfExists(obj)('main.temp')
const doesntExist = objPropIfExists(obj)('main.temp.foo.bar.baz')

console.log(exists, doesntExist)


3

Bunu, özyinelemeli işlevi kullanarak yapabilirsiniz. İç içe geçmiş tüm Nesne anahtarlarının adını bilmeseniz bile bu işe yarar.

function FetchKeys(obj) {
    let objKeys = [];
    let keyValues = Object.entries(obj);
    for (let i in keyValues) {
        objKeys.push(keyValues[i][0]);
        if (typeof keyValues[i][1] == "object") {
            var keys = FetchKeys(keyValues[i][1])
            objKeys = objKeys.concat(keys);
        }
    }
    return objKeys;
}

let test = { level1: { level2: { level3: "level3" } } };
let keyToCheck = "level2";
let keys = FetchKeys(test); //Will return an array of Keys

if (keys.indexOf(keyToCheck) != -1) {
    //Key Exists logic;
}
else {
    //Key Not Found logic;
}

2

Burada kod çözücüde (safeRead) bunu güvenli bir şekilde yapacak bir işlev var ...

safeRead(test, 'level1', 'level2', 'level3');

herhangi bir özellik null veya undefined ise, boş bir dize döndürülür


Ben ayarlanmamışsa boş bir dize döndürür çünkü
templating

2

Önceki bir yoruma dayanarak , ana nesnenin de tanımlanamadığı başka bir sürüm:

// Supposing that our property is at first.second.third.property:
var property = (((typeof first !== 'undefined' ? first : {}).second || {}).third || {}).property;

2

İstediğim yolu izleyen ve iyi ve kötü bir geri arama işlevi olan kendi işlevimi yazdım.

function checkForPathInObject(object, path, callbackGood, callbackBad){
    var pathParts = path.split(".");
    var currentObjectPath = object;

    // Test every step to see if it exists in object
    for(var i=0; i<(pathParts.length); i++){
        var currentPathPart = pathParts[i];
        if(!currentObjectPath.hasOwnProperty(pathParts[i])){
            if(callbackBad){
                callbackBad();
            }
            return false;
        } else {
            currentObjectPath = currentObjectPath[pathParts[i]];
        }
    }

    // call full path in callback
    callbackGood();
}

Kullanımı:

var testObject = {
    level1:{
        level2:{
            level3:{
            }
        }
    }
};


checkForPathInObject(testObject, "level1.level2.level3", function(){alert("good!")}, function(){alert("bad!")}); // good

checkForPathInObject(testObject, "level1.level2.level3.levelNotThere", function(){alert("good!")}, function(){alert("bad!")}); //bad

Adil için kodunuzu uyum ilham için size kredi vermek olsa benim cevap
davewoodhall

2
//Just in case is not supported or not included by your framework
//***************************************************
Array.prototype.some = function(fn, thisObj) {
  var scope = thisObj || window;
  for ( var i=0, j=this.length; i < j; ++i ) {
    if ( fn.call(scope, this[i], i, this) ) {
      return true;
    }
  }
  return false;
};
//****************************************************

function isSet (object, string) {
  if (!object) return false;
  var childs = string.split('.');
  if (childs.length > 0 ) {
    return !childs.some(function (item) {
      if (item in object) {
        object = object[item]; 
        return false;
      } else return true;
    });
  } else if (string in object) { 
    return true;
  } else return false;
}

var object = {
  data: {
    item: {
      sub_item: {
        bla: {
          here : {
            iam: true
          }
        }
      }
    }
  }
};

console.log(isSet(object,'data.item')); // true
console.log(isSet(object,'x')); // false
console.log(isSet(object,'data.sub_item')); // false
console.log(isSet(object,'data.item')); // true
console.log(isSet(object,'data.item.sub_item.bla.here.iam')); // true
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.