ES6 / 2015'te sıfır güvenli mülk erişimi (ve koşullu atama)


159

Bir var nullES6 (ES2015 / JavaScript.next / Harmony) benzerlerinde -güvenli özelliği erişim (boş yayılma / varlığı) operatörü ?.olarak CoffeeScript örneğin? Yoksa ES7 için mi planlanıyor?

var aThing = getSomething()
...
aThing = possiblyNull?.thing

Bu kabaca şöyle olacaktır:

if (possiblyNull != null) aThing = possiblyNull.thing

İdeal çözüm (hatta atama olmamalı undefined) için aThingise possiblyNull, ISnull


4
@naomik Bu tür bir boş kontrol, derin bir şekilde iç içe geçmiş bir özelliği kontrol ettiğiniz ifadeler için çok yararlı olabilir, örneğin if( obj?.nested?.property?.value )yerineif( obj && obj.nested && obj.nested.property && obj.nested.property.value )
Sean Walsh

@SeanWalsh, nesneleriniz o kadar derinlemesine iç içe geçmişse veya işlevleriniz nesnelerinizde bu kadar derin kazıyorsa, muhtemelen uygulamanızla ilgili başka sorunlar da vardır.
Teşekkür ederim

1
Karşılaştırmak var appConfig = loadConfig(config, process.env); connect(appConfig.database);için connect(config). Sen bir daha basit bir nesne geçirebilirsiniz connectyerine bütün geçme confignesne kullanabilirsiniz conf.username, conf.passwordbunun yerine böyle bir şey çalışmakla config[process.env]?.database?.username, config[process.env]?.database?.password. Kaynak: Demeter Yasası .
teşekkür ederiz

Ayrıca, varsayılanları ayarlamak veya özellikleri sterilize etmek gibi bir şey yaparsanız (bu loadConfigyukarıdaki örnekte yapılabilir ), özelliklerin varlığı hakkında varsayımlar yapabilir ve uygulamanızın sayısız alanında sıfır denetimini atlayabilirsiniz.
Teşekkür ederim

4
@naomik Dil, nesnelerin iç içe geçmesini desteklediği sürece, uygulamanın mimarisi hakkında sizin veya benim ne düşündüğümden bağımsız olarak yine de yararlı bir özelliktir. Bir kenara, bunun gibi karmaşık nesne grafikleri, karmaşık bir veri modelini modelleyen ORM'lerde çok yaygındır.
Sean Walsh

Yanıtlar:


107

Güncelleme (2020-01-31): Görünüşe göre insanlar bunu hala buluyor, işte güncel hikaye:

Güncelleme (2017-08-01): Resmi bir eklenti kullanmak istiyorsanız, Babel 7'nin alfa yapısını yeni dönüşümle deneyebilirsiniz. Kilometreniz değişebilir

https://www.npmjs.com/package/babel-plugin-transform-optional-chaining

Orijinal :

Şu anda 1. aşamada olan bir özellik: İsteğe Bağlı Zincirleme.

https://github.com/tc39/proposal-optional-chaining

Bugün kullanmak istiyorsanız, bunu başaran bir Babel eklentisi var.

https://github.com/davidyaha/ecmascript-optionals-proposal


Bunun koşullu atamayı içermediğini doğru anlıyor muyum? her durumda street = user.address?.streetayarlanır street?
ᆼ ᆺ ᆼ

1
Aslında maalesef haklı olduğunu düşünüyorum. Street Sanırım atanacak undefined. Ama en azından undefined üzerindeki özelliklere erişmeye çalışmaktan vazgeçmez.
günler

2
Ayrıca sol eldeki operatörü siz yapabiliyorsunuz gibi görünüyor ve eğer tanımlanmamış bir şey varsa, sağ el değerlendirilmez. Babel eklentisi biraz değişiklik gösterebilir, henüz ben de pek test etmedim.
günler

1
Sayfanın sol tarafındaki koşullu atama ile ilgili olarak, =şu anda resmi şartnamede desteklenmiyor gibi görünüyor. github.com/tc39/proposal-optional-chaining#not-supported
basicdays

3
Mayıs 2020 itibariyle, mevcut tarayıcılar ve Typescript bunu uyguladı gibi görünüyor!
Josh Diehl

73

O kadar güzel değil mi? operatör, ancak benzer bir sonuç elde etmek için şunları yapabilirsiniz:

user && user.address && user.address.postcode

Yana nullve undefinedher ikisi de falsy değerleri ( bu referansa bakınız ), ardından özellik &&operatöre sadece erişilen örnek null veya tanımlanmamış değilse.

Alternatif olarak, aşağıdaki gibi bir işlev yazabilirsiniz:

function _try(func, fallbackValue) {
    try {
        var value = func();
        return (value === null || value === undefined) ? fallbackValue : value;
    } catch (e) {
        return fallbackValue;
    }
}

Kullanım:

_try(() => user.address.postcode) // return postcode or undefined 

Veya bir yedek değerle:

_try(() => user.address.postcode, "none") // return postcode or a custom string

Muhtemelen soruyu açıklığa kavuşturmalıyım, peşinde olduğum en önemli şey
şartlı atamaydı

sadece bunun doğal bir sonucu; tersini güvenli bir şekilde bulmak için kullanabilirsiniz !(user && user.address && user.address.postcode) :)
rob2d

foo && foo.bar && foo.bar.quux ...büyük bir kod tabanında bu çirkin bir durumdur ve kaçınmanızın daha iyi olacağı çok fazla karmaşıklık ekler.
Skylar Saveland

2
Bu internette bulduğum en temiz çözüm. Bunu _get<T>(func: () => T, fallbackValue?: T) :T
typcript

3
Eğer user.address.postcodetanımlanmamış, _try(() => user.address.postcode, "")dönecektir undefinedyerine "". Yani kod _try(() => user.address.postcode, "").lengthbir istisna yaratacaktır.
Alexander Chen

35

Hayır . JavaScript'te bunun için lodash # get veya bunun gibi bir şey kullanabilirsiniz .


4
ES7'ye eklemek için öneriler var mı?
ᆼ ᆺ ᆼ

1
Hiç karşılaşmadım.
Girafa

3
@PeterVarga: Çok. Çok fazla tartışma. Dilbilgisi bu özellik için kolay bir şey değil
Bergi

1
lodash.getaçıkça koşullu atama yapamayacağı için biraz farklıdır.
ᆼ ᆺ ᆼ

17

Güvenli mülk erişimi için vanilya alternatifi

(((a.b || {}).c || {}).d || {}).e

En kısa koşullu atama muhtemelen şu olacaktır

try { b = a.b.c.d.e } catch(e) {}

Peki bu mekanizmayı kullanarak sorudaki ödevi nasıl yazarsınız? possiblyNull/ Tanımlı olmaması durumunda atamamak için hala bir şarta ihtiyacınız yok nullmu?
ᆼ ᆺ ᆼ

Elbette, bir görevin gerçekleşmesini isteyip istemediğinizi yine de kontrol etmeniz gerekiyor. '=' Operatörünü kullanırsanız, ister veri, ister tanımsız veya boş olsun, kaçınılmaz olarak bir şey atanacaktır. Yani yukarıdakiler sadece güvenli bir mülk erişimidir. Koşullu atama operatörü herhangi bir dilde var mı?
yagger

Elbette, örneğin CoffeeScript , aynı zamanda||=
ᆼ ᆺ ᆼ

+1 İlk düşüncem bile oldu (((a.b || {}).c || {}).d || {}).e. Ama daha kesin olur((((a || {}).b || {}).c || {}).d || {}).e
İsmailS

6

Hayır, ES6'da boş yayılma operatörü yok. Bilinen kalıplardan biriyle gitmeniz gerekecek .

Yine de yok etme özelliğini kullanabilirsiniz:

({thing: aThing} = possiblyNull);

ES7'de böyle bir operatör eklemek için pek çok tartışma (örn. Bu ) var, ancak hiçbiri gerçekten başarılı olmadı.


Bu umut verici görünüyordu, ancak en azından aThing = possiblyNull.thing
Babel'in onunla yaptığı şeyden

1
@PeterVarga: Oops, haklısın, yıkım özelliği var olmadığında işe yarıyor, ama nesne olmadığında değil null. Bu şablona çok benzeyen, ancak daha kafa karıştırıcı sözdizimiyle varsayılan bir değer sağlamanız gerekir .
Bergi

4

Buradaki listeye göre, Ecmascript'e güvenli geçiş eklemek için şu anda bir teklif yok. Yani bunu yapmanın sadece güzel bir yolu yok, aynı zamanda öngörülebilir gelecekte eklenmeyecek.


4

2020 Çözümü ?.ve??

Artık ?.varlığını güvenli bir şekilde test etmek için doğrudan satır içi (İsteğe Bağlı Zincirleme) kullanabilirsiniz . Tüm modern tarayıcılar bunu destekler.

?? (Nullish Coalescing), undefined veya null ise varsayılan bir değer ayarlamak için kullanılabilir.

aThing = possiblyNull ?? aThing
aThing = a?.b?.c ?? possiblyNullFallback ?? aThing

Bir özellik mevcutsa, bir ?.sonraki kontrole geçer veya geçerli değeri döndürür. Herhangi bir arıza anında kısa devre yapacak ve geri dönecektir undefined.

const example = {a: ["first", {b:3}, false]}

example?.a  // ["first", {b:3}, false]
example?.b  // undefined

example?.a?.[0]     // "first"
example?.a?.[1]?.a  // undefined
example?.a?.[1]?.b  // 3

domElement?.parentElement?.children?.[3]?.nextElementSibling

null?.()                // undefined
validFunction?.()       // result
(() => {return 1})?.()  // 1

Varsayılan tanımlı bir değer sağlamak için kullanabilirsiniz ??. İlk gerçek değere ihtiyacınız varsa, kullanabilirsiniz ||.

example?.c ?? "c"  // "c"
example?.c || "c"  // "c"

example?.a?.[2] ?? 2  // false
example?.a?.[2] || 2  // 2

Bir vakayı kontrol etmezseniz, sol taraf özelliği mevcut olmalıdır. Aksi takdirde, bir istisna atacaktır.

example?.First         // undefined
example?.First.Second  // Uncaught TypeError: Cannot read property 'Second' of undefined

?.Tarayıcı Desteği -% 82, Ekim 2020

??Tarayıcı Desteği -% 82

Mozilla Belgeleri

-

Mantıksal sıfır atama, 2020+ çözümü

Yeni operatörleri şu anda tarayıcılar, ekleniyor ??=, ||=ve &&=. Tam olarak aradığınız şeyi yapmazlar, ancak kodunuzun amacına bağlı olarak aynı sonuca yol açabilir.

NOT: Bunlar henüz genel tarayıcı sürümlerinde yaygın değildir , ancak Babel iyi bir şekilde aktarılmalıdır. Kullanılabilirlik değiştikçe güncellenecek.

??=sol tarafın tanımsız veya boş olup olmadığını kontrol eder, önceden tanımlanmışsa kısa devre yapar. Değilse, sol tarafa sağ taraf değeri atanır. ||=ve &&=benzerdir, ancak ||ve &&operatörlerine dayalıdır .

Temel Örnekler

let a          // undefined
let b = null
let c = false

a ??= true  // true
b ??= true  // true
c ??= true  // false

Nesne / Dizi Örnekleri

let x = ["foo"]
let y = { foo: "fizz" }

x[0] ??= "bar"  // "foo"
x[1] ??= "bar"  // "bar"

y.foo ??= "buzz"  // "fizz"
y.bar ??= "buzz"  // "buzz"

x  // Array [ "foo", "bar" ]
y  // Object { foo: "fizz", bar: "buzz" }

Tarayıcı Desteği Ekim 2020 -% 70

Mozilla Belgeleri


1
Soru aynı zamanda koşullu atama ile ilgili
ᆼ ᆺ ᆼ

Ve ile birkaç örnek ?.ve ??durumunuz için işe yarayabilecek ayrıntılı bir gelecek çözüm eklendi. Mevcut en iyi çözüm muhtemelen sadece yapmaktıraThing = possiblyNull?.thing ?? aThing
Gibolt

1
// Typescript
static nullsafe<T, R>(instance: T, func: (T) => R): R {
    return func(instance)
}

// Javascript
function nullsafe(instance, func) {
    return func(instance);
};

// use like this
const instance = getSomething();
let thing = nullsafe(instance, t => t.thing0.thing1.thingx);

0

Güvenli bir derin alma yöntemi, undercore.js için doğal bir uyum gibi görünmektedir, ancak burada sorun, dize programlamasından kaçınmaktır. Dizi programlamasından kaçınmak için @ Felipe'nin yanıtını değiştirmek (veya en azından uç durumları arayana geri iter):

function safeGet(obj, props) {
   return (props.length==1) ? obj[keys[0]] :safeGet(obj[props[0]], props.slice(1))
}

Misal:

var test = { 
  a: { 
    b: 'b property value',
    c: { }
  } 
}
safeGet(test, ['a', 'b']) 
safeGet(test, "a.b".split('.'))  

Rick'ten söz etmek gerekirse: bu kulağa fazladan adımlarla dizi programlama gibi geliyor.
tocqueville

1
Lodash artık deep get / set uyguluyor _.get(obj, array_or_dotstring)
prototip

1
Ancak adil olmak gerekirse, Javascript nokta ve dize erişimci gösterimi bile temelde dize programlamadır, obj.a.b.cvsobj['a']['b']['c']
prototip

1
Nereden keysgeliyor?
Levi Roberts

-3

Bunun bir JavaScript sorusu olduğunu biliyorum, ancak bence Ruby bunu istenen tüm yollarla ele alıyor, bu yüzden bunun alakalı bir referans noktası olduğunu düşünüyorum.

.&, tryve && güçlü yanlarına ve potansiyel tuzaklara sahiptir. Buradaki seçeneklerin harika bir özeti: http://mitrev.net/ruby/2015/11/13/the-operator-in-ruby/

TLDR; Ruby'cilerle vardığı sonuca göre diggöze daha kolay ve daha güçlü bir garanti hem bir değer veya nullatanacaktır.

İşte TypeScript'te basit bir imeplementasyon:

export function dig(target: any, ...keys: Array<string>): any {
  let digged = target
  for (const key of keys) {
    if (typeof digged === 'undefined') {
      return undefined // can also return null or a default value
    }
    if (typeof key === 'function') {
      digged = key(digged)
    } else {
      digged = digged[key]
    }
  }
  return digged
}

Bu, herhangi bir yuvalama derinliği için kullanılabilir ve işlevleri kullanır.

a = dig(b, 'c', 'd', 'e');
foo = () => ({});
bar = dig(a, foo, 'b', 'c')

tryÖnceki cevapları gösterildiği gibi bir yaklaşım, JS okumak eşit derecede güzel. Ayrıca bu uygulamanın bir dezavantajı olan döngü gerektirmez.


Bu yanıtı buraya eğlenceli / tam / alternatif bir çözüm olarak koyuyorum, z / c Ruby'nin bunu sözdizimsel şekerle yapmasını seviyorum. Bunu topluluk için oraya koy - Bir kez daha oy verin ve bu gönderiyi memnuniyetle sileceğim. Şerefe!
theUtherSide

ECMAScript2019'un Ruby benzeri "İsteğe Bağlı Zincirleme" için harika bir önerisi olduğunu görmek gerçekten harika: github.com/tc39/proposal-optional-chaining Bugün Babel Eklentisi aracılığıyla kullanılabilir ve aynı zamanda bir nesnede yöntemleri çağırmak için de çalışır: babeljs.io / docs / en / babel-plugin-proposal-optional-chaining
theUtherSide

-5

Bu sorunun 2018 için biraz yenilenmesi gerektiğini düşündüm. Bu, herhangi bir kitaplık kullanmadan güzelce yapılabilir Object.defineProperty()ve aşağıdaki gibi kullanılabilir:

myVariable.safeGet('propA.propB.propC');

MDN'de belgelendiği gibi yöntemi için artık mevcut olan writeableve enumerabletanımları nedeniyle bunun güvenli (ve js-etik) olduğunu düşünüyorum.definePropertyObject

aşağıdaki fonksiyon tanımı:

Object.defineProperty(Object.prototype, 'safeGet', { 
    enumerable: false,
    writable: false,
    value: function(p) {
        return p.split('.').reduce((acc, k) => {
            if (acc && k in acc) return acc[k];
            return undefined;
        }, this);
    }
});

Bunu göstermek için konsol çıktılı bir jsBin oluşturdum. JsBin sürümünde boş değerler için de özel bir istisna eklediğimi unutmayın. Bu isteğe bağlıdır ve bu yüzden onu yukarıdaki minimum tanımın dışında bıraktım.

İyileştirmeler memnuniyetle karşılanmaktadır


2
Bununla, fiili olarak dizelere kod yazarsınız. Bunu yapmak gerçekten kötü bir fikir. Kodunuzun yeniden düzenlenmesini imkansız hale getirir ve IDE dostu değildir.
tocqueville
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.