0 [0]이 구문 적으로 유효한 이유는 무엇입니까?


119

이 줄이 자바 스크립트에서 유효한 이유는 무엇입니까?

var a = 0[0];

그 후, a이다 undefined.


4
같은 true[0]또는 ""[0]
Hacketo

24
@CodeAngry 공정하게 말하면 JavaScript는 HTML 내에서 태어 났고, "당신이 좋아하는 것을 내게 던져라. 그리고 나는 그것을 이해하려고 노력할 것이다"라는 전체를 시작한 것은 HTML이다.
Niet the Dark Absol 2015 년

8
@NiettheDarkAbsol 공평하게 말하면 구문 의미가 있기 때문에 (아직 많이는 아니지만) 잘못되었습니다 . 이것은 객체의 이름 "0"을 가진 속성을 얻는 것 new Number(0)입니다.
meandre

a는 항상 정의되지 않는다는 잘못된 가정입니다. 0[0]값 을 반환하는 것은 완벽하게 가능 합니다
Rune FS

@meandre 0["toString"]굉장합니다. 지적 해 주셔서 감사합니다.
Jonathan

답변:


169

당신이 할 때 0[0] 하면 JS 인터프리터는 첫 번째 0Number객체 로 변환 한 다음 [0]해당 객체 의 속성에 액세스하려고합니다 undefined.

0[0]이 컨텍스트의 언어 문법에서 속성 액세스 구문 을 허용 하므로 구문 오류가 없습니다 . 이 구조 (Javascript 문법의 용어 사용)는 NumericLiteral[NumericLiteral].

언어 문법의 관련 부분 ES5 ECMAScript 사양의 섹션 A.3 은 다음과 같습니다.

Literal ::
    NullLiteral
    BooleanLiteral
    NumericLiteral
    StringLiteral
    RegularExpressionLiteral

PrimaryExpression :
    this
    Identifier
    Literal
    ArrayLiteral
    ObjectLiteral
    ( Expression )

MemberExpression :
    PrimaryExpression
    FunctionExpression
    MemberExpression [ Expression ]
    MemberExpression . IdentifierName
    new MemberExpression Arguments    

따라서이 진행 과정을 통해 문법을 따를 수 있습니다.

MemberExpression [ Expression ]
PrimaryExpression [ Expression ]
Literal [ Expression ]
NumericLiteral [ Expression ]

그리고 비슷하게 ExpressionNumericLiteral 문법을 따른 후에도 결국 그렇게 될 수 있습니다 .

NumericLiteral [ NumericLiteral ]

이는 0[0]문법의 허용 된 부분이므로 SyntaxError가 없음을 의미합니다 .


그런 다음 런타임에 읽고있는 undefined소스가 객체이거나 객체에 대한 암시 적 변환이있는 한 존재하지 않는 속성을 읽을 수 있습니다 (단지로 읽음 ). 그리고 숫자 리터럴은 실제로 객체 (Number 객체)로 암시 적으로 변환됩니다.

이것은 자바 스크립트의 알려지지 않은 기능 중 하나입니다. 유형 Number, BooleanString 자바 스크립트는 일반적으로 프리미티브 (안 완전한 객체)로 내부적으로 저장됩니다. 이것은 컴팩트하고 변경 불가능한 스토리지 표현입니다 (아마도 구현 효율성을 위해이 방식으로 수행됨). 그러나 Javascript는 이러한 기본 요소를 속성 및 메서드가있는 객체처럼 처리 할 수 ​​있기를 바랍니다. 따라서 프리미티브에서 직접 지원되지 않는 속성이나 메서드에 액세스하려고하면 Javascript는 값이 프리미티브의 값으로 설정된 적절한 유형의 객체로 프리미티브를 일시적으로 강제합니다.

와 같은 기본 요소에서 객체와 유사한 구문을 사용하면 0[0]인터프리터는이를 기본 요소에 대한 속성 액세스로 인식합니다. 이에 대한 응답은 첫 번째 0숫자 프리미티브 를 가져 와서 속성에 Number액세스 할 수 있는 완전한 객체 로 강제 변환하는 것 [0]입니다. 이 특정 경우 [0]에는 Number 객체 의 속성 undefined이에서 얻은 값입니다 0[0].

다음은 속성을 처리하기 위해 기본 요소를 객체로 자동 변환하는 방법에 대한 기사입니다.

자바 스크립트 프리미티브의 비밀스런 삶


다음은 ECMAScript 5.1 사양의 관련 부분입니다.

9.10 CheckObjectCoercible

값이 undefined또는 이면 TypeError를 던지고 null, 그렇지 않으면를 반환합니다 true.

여기에 이미지 설명 입력

11.2.1 속성 접근 자

  1. baseReference를 MemberExpression을 평가 한 결과로 둡니다.
  2. baseValue를 GetValue (baseReference)로 둡니다.
  3. propertyNameReference를 Expression 평가의 결과로 둡니다.
  4. propertyNameValue를 GetValue (propertyNameReference)로 둡니다.
  5. CheckObjectCoercible (baseValue)를 호출합니다.
  6. propertyNameString을 ToString (propertyNameValue)로 둡니다.
  7. 평가되는 구문 생성이 Strict 모드 코드에 포함되어 있으면 strict를 true로, 그렇지 않으면 strict를 false로 둡니다.
  8. 기본 값이 baseValue이고 참조 된 이름이 propertyNameString이고 엄격 모드 플래그가 엄격한 Reference 유형의 값을 반환합니다.

이 질문에 대한 작동 부분은 위의 5 단계입니다.

8.7.1 GetValue (V)

액세스되는 값이 속성 참조 일 때 ToObject(base)모든 기본 요소의 객체 버전을 가져 오기 위해 호출하는 방법을 설명합니다 .

9.9 ToObject

이는 Boolean, NumberString프리미티브가 [[PrimitiveValue]] 내부 속성이 적절히 설정된 객체 형태로 변환되는 방법을 설명합니다 .


흥미로운 테스트로, 코드가 다음과 같으면 :

var x = null;
var a = x[0];

이 기술적으로 법적 구문으로 아직 구문 분석시에 구문 에러가 발생하지 것입니다,하지만 당신은 코드를 실행하면 속성 접근 자 로직 위의이 값에 적용될 때 때문에 런타임에서 형식 오류를 던질 것 x, 그것은 호출 CheckObjectCoercible(x)또는 호출 ToObject(x)하는 경우 모두 형식 오류가 발생합니다 x입니다 nullundefined.


0[1,2]또한 유효합니다. 무슨 뜻입니까? (I 질문을 업데이트)
마이클 M.에게

그리고 그것은 구문 오류를 일으키지 않습니다. 그 속성이 존재하지 않더라도 그렇지 null않거나 undefined완전히 괜찮은 속성에 액세스하기 때문 입니다.
user4642212

6
@Michael은 업데이트 할 필요가 없습니다. 그냥 그래서 그 쉼표 연산자입니다0[2]
아 미트 조기 납의에게

1
쉼표 연산자 : 모두 1, 2에서 평가 1,2하지만 반환 2.
user4642212

2
그것은 질문의 뉘앙스에 대한 멋진 대답입니다.
tbh__ 2015 년

20

대부분의 프로그래밍 언어와 마찬가지로 JS는 문법을 사용하여 코드를 구문 분석하고 실행 가능한 형식으로 변환합니다. 특정 코드 청크에 적용 할 수있는 문법 규칙이 없으면 SyntaxError가 발생합니다. 그렇지 않으면 코드가 의미가 있는지 여부에 관계없이 유효한 것으로 간주됩니다.

JS 문법 의 관련 부분은 다음 과 같습니다.

Literal :: 
   NumericLiteral
   ...

PrimaryExpression :
   Literal
   ...

MemberExpression :
   PrimaryExpression
   MemberExpression [ Expression ]
   ...

0[0]이러한 규칙을 준수 하므로 유효한 표현식으로 간주됩니다 . 그것이 올바른지 여부 (예 : 런타임에 오류가 발생하지 않음)는 또 다른 이야기이지만 그렇습니다. 이것은 JS가 someLiteral[someExpression]다음 과 같은 표현식을 평가하는 방법입니다 .

  1. 평가 someExpression(임의의 복잡 할 수 있음)
  2. 리터럴을 해당 객체 유형으로 변환 (숫자 리터럴 => Number, 문자열 => String등)
  3. get propertyresult (1) 속성 이름으로 result (2)에 대한 작업을 호출하십시오.
  4. 결과 버리기 (2)

그래서 다음 0[0]과 같이 해석됩니다.

index = 0
temp = Number(0)
result = getproperty(temp, index) // it's undefined, but JS doesn't care
delete temp
return result

다음은 유효 하지만 잘못된 표현식 의 예입니다 .

null[0]

파싱이 잘되었지만 런타임에 인터프리터가 2 단계에서 실패하고 ( null객체로 변환 할 수 없기 때문에 ) 런타임 오류가 발생합니다.


1
그것보다 더 많은 것이 있습니다. var x = null; var a = x[0];구문 오류를 생성하지 않지만 런타임에 TypeError를 발생시킵니다.
jfriend00

@ jfriend00 : 그게 질문의 전부는 아니지만 추가되었습니다.
게오르그

결과는 정의되지 않아도됩니다. 0[0]정의되지 않은 값 대신 값을 반환 할 수 있습니다
Rune FS

9

Javascript에서 유효하게 숫자를 아래 첨자 할 수있는 상황이 있습니다.

-> 0['toString']
function toString() { [native code] }

이 작업을 수행하려는 이유가 즉시 명확하지는 않지만 Javascript에서 첨자를 사용하는 것은 점 표기법을 사용하는 것과 동일합니다 (점 표기법이 식별자를 키로 사용하는 것을 제한하지만).


@AmitJoki 이것은 (0).toString(함수를 호출하지 않고)와 같습니다. 숫자 유형의 속성입니다.
user4642212

@AmitJoki는 '이 줄이 유효한 이유'라는 질문에 답하기 때문입니다.
던컨

@ Duncan 그러나 이것은 "대괄호 표기법"에 더 가깝고 OP가 그것을 알고 있다고 생각합니다. 이 번호 개체로 해석있어 다음의 사실 0속성에 액세스하고 존재하지 않는 보낸 사람, undefined jfriend00에 설명 된대로 올바른.
아 미트 조기 납의

@AmitJoki 0[0]정의되지 않은 것을 반환 하는 잘못된 가정입니다 . 그럴 것 같지만 반드시 그럴 필요는 없습니다
Rune FS

9

이것이 유효한 구문 이 Javascript에 고유하지 않다는 점에 주목하고 싶습니다 . 대부분의 언어에는 런타임 오류 또는 유형 오류가 있지만 구문 오류와는 다릅니다. Javascript는 주어진 이름의 속성이없는 객체를 구독하는 경우를 포함하여 다른 언어가 예외를 발생시킬 수있는 많은 상황에서 undefined를 반환하도록 선택합니다.

이 구문은 표현식의 유형 (숫자 리터럴과 같은 간단한 표현식조차도)을 알지 못하므로 모든 연산자를 모든 표현식에 적용 할 수 있습니다. 예를 들어, 아래 첨자를 시도 undefined하거나 Javascript에서 nulla TypeError를 발생시킵니다 . 구문 오류가 아닙니다. 실행되지 않으면 (if- 문의 잘못된쪽에 있음) 문제가 발생하지 않는 반면 구문 오류는 정의상 항상 컴파일 타임 (eval, Function 등)에 포착됩니다. , 모두 컴파일로 계산).


8

유효한 구문이기 때문에 해석 할 유효한 코드도 있습니다. 모든 객체의 속성에 액세스하려고 시도 할 수 있으며 (이 경우 0은 Number 객체로 캐스팅 됨) 값이 있으면 값을 제공하고 그렇지 않으면 정의되지 않습니다. 그러나 undefined 속성에 액세스하려고하면 작동하지 않으므로 0 [0] [0]은 런타임 오류를 발생시킵니다. 그래도 여전히 유효한 구문으로 분류됩니다. 유효한 구문과 런타임 / 컴파일 시간 오류를 일으키지 않는 것에는 차이가 있습니다.


3

구문이 유효 할뿐만 아니라 undefined대부분의 경우 결과가 정상일 필요는 없습니다. JS는 가장 순수한 객체 지향 언어 중 하나입니다. 대부분의 소위 OO 언어는 일단 생성 된 객체의 형태 (클래스에 묶여 있음)를 변경할 수없고 객체의 상태 만 변경할 수 있다는 점에서 클래스 지향적입니다. JS에서는 상태와 객체의 형태를 변경할 수 있으며 이는 생각보다 자주 수행합니다. 이 기능은 오용하는 경우 다소 모호한 코드를 만듭니다. 숫자는 불변이므로 객체 자체를 변경할 수 없으며 상태도 형태도 아니므로 할 수 있습니다.

0[0] = 1;

1을 반환하지만 실제로 아무것도 할당하지 않는 유효한 할당 표현식입니다 0. 숫자 는 변경할 수 없습니다. 그 자체로는 다소 이상합니다. 아무것도 할당하지 않는 유효하고 올바른 (실행 가능한) 어싱먼트 표현식을 가질 수 있습니다 (*). 그러나 숫자의 유형은 변경 가능한 객체이므로 유형을 변경할 수 있으며 변경 사항은 프로토 타입 체인에 계단식으로 적용됩니다.

Number[0] = 1;
//print 1 to the console
console.log(0[0]);
//will also print 1 to the console because all integers have the same type
console.log(1[0]); 

물론 정상적인 사용 범주와는 거리가 멀지 만 다른 시나리오에서는 개체 기능을 확장하는 것이 실제로 의미가 있기 때문에이를 허용하도록 언어가 지정되어 있습니다. jQuery 플러그인이 jQuery 객체에 연결하여 예제를 제공하는 방법입니다.

(*) 실제로 객체의 속성에 값 1을 할당하지만 해당 (transcient) 객체를 참조 할 수있는 방법이 없으므로 nexx GC 패스에서 수집됩니다.


3

JavaScript에서는 모든 것이 객체이므로 인터프리터가 구문 분석 할 때 0을 객체로 취급하고 0을 속성으로 반환하려고합니다. true 또는 ""(빈 문자열)의 0 번째 요소에 액세스하려고 할 때도 같은 일이 발생합니다.

0 [0] = 1로 설정해도 속성과 그 값을 메모리에 설정하지만 0에 액세스하는 동안에는 숫자로 취급합니다 (여기서는 Object로 취급하는 것과 숫자로 취급하는 것 사이에 혼동하지 마십시오.)

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.