ES6 / 2015의 안전하지 않은 속성 액세스 및 조건부 할당


149

거기 nullES6 (ES2015 / JavaScript.next / 하모니) 등의 -safe 속성 액세스 (null의 전파 / 유) 연산자 ?.의 커피 스크립트 예는? 아니면 ES7을 계획하고 있습니까?

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

이것은 대략 다음과 같습니다.

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

이상적 솔루션 (심지어 할당해서는 안됩니다 undefined)로 aThing하면 possiblyNullISnull


3
@naomik 널 (null) 검사의이 종류는 매우 유용 할 수 있습니다에 대한 당신이 예를 들면, 중첩 속성에 대해 확인하고 if 문 if( obj?.nested?.property?.value )대신에if( obj && obj.nested && obj.nested.property && obj.nested.property.value )
— 숀 월시

@SeanWalsh 객체가 깊게 중첩되어 있거나 함수가 객체에 깊이 파고 있으면 앱에 몇 가지 다른 문제가있을 수 있습니다.
— 감사합니다

1
와 비교 var appConfig = loadConfig(config, process.env); connect(appConfig.database);하십시오 connect(config). 당신은에 훨씬 간단 개체를 전달할 수 있습니다 connect대신 전체 전달하는 config객체를 사용할 수 conf.username, conf.password대신 뭔가를 시도하는 config[process.env]?.database?.username, config[process.env]?.database?.password. 참조 : 데메테르의 법칙 .
— 감사합니다

또한 기본값 설정 또는 속성 삭제와 같은 작업을 수행 loadConfig하는 경우 (위의 예에서 수행 할 수 있음) 속성의 존재에 대해 가정하고 앱의 수많은 영역에서 null 검사를 건너 뛸 수 있습니다.
— 감사합니다

4
@naomik 언어가 중첩 객체를 지원하는 한, 앱 자체의 아키텍처에 대해 생각하거나 상관없이 여전히 유용한 기능입니다. 또한 이와 같은 복잡한 개체 그래프는 복잡한 데이터 모델을 모델링하는 ORM에서 매우 일반적입니다.
— Sean Walsh

답변:


98

업데이트 (2020-01-31) : 사람들이 여전히 이것을 찾는 것 같습니다. 현재 이야기는 다음과 같습니다.

업데이트 (2017-08-01) : 공식 플러그인을 사용하려면 새로운 변형으로 Babel 7의 알파 빌드를 시도 할 수 있습니다. 귀하의 마일리지가 다를 수 있습니다

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

실물 :

현재 1 단계에있는 기능 : 옵션 체인.

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

오늘 사용하고 싶다면 그것을 수행하는 Babel 플러그인이 있습니다.

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


조건부 할당이 포함되어 있지 않다는 것을 올바르게 이해하고 있습니까? 어떤 경우에도 street = user.address?.street설정 street하시겠습니까?
— ᆼ ᆺ ᆼ

1
실제로 불행히도 나는 당신이 옳다고 생각합니다. Street 나는 할당 될 것 같아요undefined . 그러나 적어도 정의되지 않은 속성에 액세스하려고하면 포기하지 않습니다.
— basicdays

2
또한 왼쪽에서 연산자를 사용할 수있는 것처럼 보이고 정의되지 않은 항목을 회피하면 오른쪽이 평가되지 않습니다. babel 플러그인은 약간 다를 수 있지만 아직 많이 테스트하지는 않았습니다.
— basicdays

1
의 왼쪽에있는 조건부 할당과 관련하여 =현재 공식 사양에서 지원되지 않는 것 같습니다. github.com/tc39/proposal-optional-chaining#not-supported
— 기본 요일

1
2020 년 5 월 현재, 현재 브라우저처럼 보이고 Typescript가 이것을 구현했습니다!
— Josh Diehl

65

?만큼 좋지 않습니다. 연산자이지만 비슷한 결과를 얻으려면 다음을 수행하십시오.

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

null및 undefined둘 다 잘못된 값 이므로 ( 이 참조 참조 ) &&선행 연산자가 null이거나 정의되지 않은 경우에만 연산자 뒤의 속성에 액세스합니다.

또는 다음과 같은 함수를 작성할 수 있습니다.

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

용법:

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

또는 대체 값으로 :

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

아마도 그 질문을 분명히해야 할 것입니다. 제가 겪은 가장 중요한 것은 조건부 할당이었습니다
— ᆼ ᆺ ᆼ

그것에 대한 단지 추론; 역을 안전하게 찾기 위해 다음을 사용할 수 있습니다 !(user && user.address && user.address.postcode) :)
— rob2d

foo && foo.bar && foo.bar.quux ...큰 코드베이스에서는 이것이 추악하고 피해야 할 많은 복잡성을 추가합니다.
— Skylar Saveland

1
이것은 인터넷에서 찾은 가장 깨끗한 솔루션입니다. 나는 이것을 typescript와 함께 사용한다 :_get<T>(func: () => T, fallbackValue?: T) :T
— tomwassing

3
user.address.postcode가 정의되어 있지 않으면 대신에 _try(() => user.address.postcode, "")반환 undefined됩니다 "". 따라서 코드 _try(() => user.address.postcode, "").length에서 예외가 발생합니다.
— Alexander Chen

33

아니요 . JavaScript에서 lodash # get 또는 이와 유사한 것을 사용할 수 있습니다 .


4
ES7에 추가 할 제안이 있습니까?
— ᆼ ᆺ ᆼ

1
발생하지 않았습니다.
— Girafa

3
@ PeterVarga : 많은. 너무 많은 토론. 문법은이 기능에서 쉬운 일이 아닙니다
— Bergi

1
lodash.get조건부 할당을 수행 할 수 없다는 점에서 약간 다릅니다.
— ᆼ ᆺ ᆼ

17

안전한 자산 접근을위한 바닐라 대안

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

가장 간결한 조건부 할당은 아마도

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

그렇다면이 메커니즘을 사용하여 질문에 과제를 어떻게 쓰시겠습니까? possiblyNull정의되지 않은 경우에도 할당하지 않으려면 조건이 필요 하지 null않습니까?
— ᆼ ᆺ ᆼ

물론 과제를 수행 할 것인지 여전히 확인해야합니다. '='연산자를 사용하면 데이터, 정의되지 않았거나 null 인 것이 불가피하게 할당됩니다. 위의 내용은 안전한 자산 액세스입니다. 조건부 할당 연산자가 어떤 언어로도 존재합니까?
— yagger

물론, 예를 들어 커피 스크립트 , 또한이||=
— ᆼ ᆺ ᆼ

+1 나의 첫 생각조차도 (((a.b || {}).c || {}).d || {}).e. 그러나 더 정확할 것입니다((((a || {}).b || {}).c || {}).d || {}).e
— IsmailS

6

아니요, ES6에는 null 전파 연산자가 없습니다. 알려진 패턴 중 하나를 사용해야합니다. 합니다.

하지만 다음과 같은 구조 조정을 사용할 수 있습니다.

({thing: aThing} = possiblyNull);

ES7에서 그러한 연산자를 추가하기위한 많은 토론 (예 : this ) 이 있지만 실제로 이륙 한 사람은 없습니다.


그러나 유망한 것처럼 보였지만 적어도 바벨이 그것으로 무엇을하는지와는 전혀 다르지 않습니다aThing = possiblyNull.thing
— ᆼ ᆺ ᆼ

1
@PeterVarga : 그렇습니다. 속성이 존재하지 않을 때 객체가 아닌 경우에는 구조화가 작동합니다 null. 이 패턴 과 비슷 하지만 더 혼란스러운 구문 으로 기본값을 제공해야 합니다.
— Bergi

4

여기 의 목록으로 이동하면 현재 Ecmascript에 안전한 순회를 추가 할 제안이 없습니다. 따라서이를 수행 할 수있는 좋은 방법은 없을뿐만 아니라 미래에도 추가되지는 않을 것입니다.


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);

1

옵션 체인 ?. 및 Nullish Coalescing??

당신은 지금 직접 사용할 수 있습니다 ?.인라인을 하여 존재 여부를 안전하게 테스트 . 모든 최신 브라우저가이를 지원합니다.

?? undefined 또는 null 인 경우 기본값을 설정하는 데 사용할 수 있습니다.

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

속성이 존재 ?.하면 다음 확인으로 진행하거나 유효한 값을 반환합니다. 모든 고장은 즉시 단락되어 복귀 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

기본 정의 된 값을 보장하기 위해을 사용할 수 있습니다 ??. 첫 번째 진실한 값이 필요한 경우을 사용할 수 있습니다 ||.

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

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

사례를 확인하지 않으면 왼쪽 속성이 존재해야합니다. 그렇지 않으면 예외가 발생합니다.

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

?.브라우저 지원 -78 %, 2020 년 7 월

??브라우저 지원 -78 %

모질라 문서

-

논리적 인 무효 할당, 2020+ 솔루션

신규 사업자는 현재 브라우저에 추가되고 ??=, ||=하고 &&=. 그들은 당신이 찾고있는 것을하지 않지만 코드의 목적에 따라 동일한 결과를 초래할 수 있습니다.

참고 : 공용 브라우저 버전에서는 아직 일반적이지 않지만 Babel은 잘 변환됩니다. 가용성이 변경되면 업데이트됩니다.

??=왼쪽이 정의되지 않았는지 또는 null인지 확인하고 이미 정의 된 경우 단락합니다. 그렇지 않으면 왼쪽에 오른쪽 값이 할당됩니다. ||=그리고 &&=온 유사하지만 기반 ||및 &&운영.

기본 예

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

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

객체 / 배열 예

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" }

브라우저 지원 2020 년 7 월-.03 %

모질라 문서


1
질문은 또한 조건부 할당에 관한 것입니다
— ᆼ ᆺ ᆼ

몇 가지 예에 추가 ?.하고 ??, 그리고 상황에 맞는 일을 할 수있는 상세한 곧 솔루션입니다. 현재 가장 좋은 솔루션은 아마도 할 것입니다aThing = possiblyNull?.thing ?? aThing
— Gibolt

0

안전한 deep get 메소드는 underscore.js에 자연스럽게 맞는 것처럼 보이지만 문제는 문자열 프로그래밍을 피하는 것입니다. 문자열 프로그래밍을 피하기 위해 @Felipe의 답변을 수정하십시오 (또는 적어도 사례를 호출자에게 푸시합니다).

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

예:

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

Rick을 말하면, 이것은 추가 단계가있는 문자열 프로그래밍처럼 들립니다.
— tocqueville

1
lodash는 이제 심오한 get / set을 구현합니다. _.get(obj, array_or_dotstring)
— 프로토 타입

1
그러나 공정 심지어 자바 스크립트 점 및 문자열 접근 표기 할 것은, 기본적으로 문자열 프로그램입니다 obj.a.b.c대obj['a']['b']['c']
— 프로토 타입

1
어디에서 keys오나요?
— Levi Roberts

-2

나는 이것이 JavaScript 질문이라는 것을 알고 있지만 Ruby는 요청 된 모든 방식으로 이것을 처리한다고 생각하므로 관련 참조 지점이라고 생각합니다.

.&, try및 &&에는 강점과 잠재적 인 함정이 있습니다. 이러한 옵션에 대한 자세한 설명은 다음과 같습니다. http://mitrev.net/ruby/2015/11/13/the-operator-in-ruby/

TLDR; Rubyists의 결론은 dig눈에 더 쉽고 값 이나 가치null 가 부여 될 것이라는 더 강한 보증입니다 .

TypeScript의 간단한 구현은 다음과 같습니다.

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
}

이것은 모든 깊이의 중첩 및 핸들 기능에 사용할 수 있습니다.

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

이 try접근법은 이전 답변에서 볼 수 있듯이 JS에서도 읽는 것이 좋습니다. 또한이 구현의 한 가지 단점 인 루핑이 필요하지 않습니다.


나는이 답변을 재미 있고 완전하며 대안적인 해결책으로 여기에 넣었다. 루비가 구문 설탕으로 이것을하는 방식을 좋아한다. 커뮤니티를 위해이 기능을 제공하기 만하면됩니다. 다시 한 번 투표하면이 게시물을 기꺼이 삭제할 것입니다. 건배!
— theUtherSide

정말 ECMAScript2019 루비 같은 "옵션 체인"에 대한 좋은 제안을 가지고 볼 수 냉각 : github.com/tc39/proposal-optional-chaining 그것은 바벨 플러그인을 통해 오늘을 사용할 수, 그리고 너무 객체의 메소드를 호출하기위한 작동합니다 babeljs.io를 / docs / ko / babel-plugin-proposal-optional-chaining
— theUtherSide

-5

이 질문은 2018 년에 약간의 새로 고침이 필요하다고 생각했습니다.이 라이브러리는 라이브러리를 사용하지 않고도 훌륭하게 수행 Object.defineProperty()할 수 있으며 다음과 같이 사용할 수 있습니다.

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

나는 MDN에 문서화 된 것처럼 의 방법에 사용할 수 있는 writeable및 enumerable정의 때문에이 안전하고 js-ethical이라고 생각합니다.definePropertyObject

아래 함수 정의 :

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);
    }
});

콘솔 출력으로 jsBin 을 결합하여 이것을 보여줍니다. jsBin 버전에서는 빈 값에 대한 사용자 정의 예외도 추가했습니다. 이것은 선택 사항이므로 위의 최소 정의에서 제외했습니다.

개선을 환영합니다


2
이를 통해 실제로 코드를 문자열로 작성합니다. 그렇게하는 것은 정말 나쁜 생각입니다. IDE 친화적이지 않은 코드 리팩토링이 불가능합니다.
— tocqueville
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.