JavaScript에서 Reflect 객체는 무엇을합니까?


87

얼마 전에 MDN Reflect에서 자바 스크립트 의 개체에 대한 빈 스텁을 보았지만 평생 Google에서 아무것도 찾을 수 없습니다. 오늘 나는이 http://people.mozilla.org/~jorendorff/es6-draft.html#sec-reflect-object를 찾았 으며 영역과 로더 기능을 제외하고 Proxy 객체와 비슷하게 들립니다.

기본적으로 내가 찾은이 페이지가 Reflect를 구현하는 방법 만 설명하는지 아니면 그 문구를 이해할 수 없는지 모르겠습니다. 누군가가 일반적으로 방법이 무엇인지 설명해 주 Reflect시겠습니까?

예를 들어, 내가 찾은 페이지에서 호출 Reflect.apply ( target, thisArgument, argumentsList ) 하면 "thisArgument 및 args 인수를 사용하여 target의 [[Call]] 내부 메서드를 호출 한 결과를 반환합니다." 라고 말합니다 . 하지만 그냥 전화하는 것과 어떻게 다른 target.apply(thisArgument, argumentsList)가요?

최신 정보:

@Blue 덕분에 나는 위키 http://wiki.ecmascript.org/doku.php?id=harmony:reflect_api&s=reflect 에서이 페이지를 찾았습니다. 내가 아는 한 반사 객체가 모든 메소드 버전을 제공한다고 말합니다. 전달을 더 쉽게하기 위해 프록시에 의해 트랩 될 수있는 작업. 그러나 그것이 어떻게 완전히 필요한지 모르겠 기 때문에 나에게는 조금 이상해 보입니다. 그러나 그것은 그보다 조금 더 많은 것을하는 것 같습니다. 특히 말하는 double-lifting파는 이전 프록시 사양을 가리 킵니다.


1
사양에는 "Reflect 객체는 하나의 일반 객체입니다."라고 나와 있습니다. 내 이해로 ReflectRealmLoader객체에 대한 컨테이너 일 뿐이지 만 후자가 무엇을하는지 모르겠습니다.
simonzack 2014-08-23

감사합니다 :) 링크 한 페이지에서 각 Realm이 자체 "자바 스크립트 컨텍스트"이고 로더가 reflect 사이의 유사점을 기반으로 모듈 등의 Realm을로드하는 것으로 보입니다. 프록시와 프록시 종류의 "오버로드"기능이 내장되어 Reflect.Loader있고 Reflect.Realm모듈 기능을 오버로드하는 것과 관련이 있을 수 있다는 사실 ?
Jim Jones

1
그것은과 같은 정적 방법과 JSON (같이) '정적 클래스'이다 : isExtensible, ownKeys등등에서 ES 6, 실제 클래스와,이 (더 클래스에 대해 알고하는 데 유용 target16.1.2 나는 생각한다).
Rudie 2014-08-23

답변:


129

업데이트 2015 : 7th답변 에서 지적했듯이 이제 ES6 (ECMAScript 2015)가 완성되었으므로 이제 더 적절한 문서를 사용할 수 있습니다.


원래 답변 ((역사적) 이해 및 추가 예) :

Reflection proposal받는 진행 한 것으로 보인다 초안 인 ECMAScript 6 규격 . 이 문서는 현재 Reflect-object의 메서드를 설명 하고 Reflect-object 자체 에 대해 다음 사항 만 설명합니다 .

Reflect 객체는 단일 일반 객체입니다.

Reflect 객체의 [[Prototype]] 내부 슬롯 값은 표준 내장 Object 프로토 타입 객체 (19.1.3)입니다.

Reflect 객체는 함수 객체가 아닙니다. [[Construct]] 내부 메서드가 없습니다. new 연산자 를 사용하여 Reflect 객체를 생성자로 사용할 수 없습니다 . Reflect 객체에는 [[Call]] 내부 메서드도 없습니다. Reflect 객체를 함수로 호출 할 수 없습니다.

그러나 ES Harmony 의 목적에 대한 간단한 설명이 있습니다 .

"@reflect"모듈은 여러 용도로 사용됩니다.
  • 이제 모듈이 있으므로 "@reflect"모듈은 이전에 Object에 정의 된 많은 리플렉션 메서드를위한보다 자연스러운 위치입니다. 이전 버전과의 호환성을 위해 Object의 정적 메서드가 사라질 가능성은 거의 없습니다. 그러나 새로운 메서드는 Object 생성자보다는 "@reflect"모듈에 추가되어야합니다.
  • 글로벌 프록시 바인딩이 필요하지 않은 자연스러운 프록시 홈입니다.
  • 이 모듈의 대부분의 메서드는 프록시 트랩에 일대일 매핑됩니다. 프록시 처리기는 아래와 같이 작업을 편리하게 전달하기 위해 이러한 메서드가 필요합니다.



따라서 Reflect객체는 여러 유틸리티 기능을 제공하며, 그 중 많은 부분이 전역 객체에 정의 된 ES5 메서드와 겹치는 것처럼 보입니다.

그러나 이것이 해결하려는 기존 문제 또는 추가 된 기능을 설명하지는 않습니다. 나는 이것이 shimmed 될 수 있다고 생각했고 실제로 위의 조화 사양은 '이 방법의 비 규범 적이며 대략적인 구현'에 연결 됩니다.

그 코드를 검사하는 것은 (더) 아이디어는 그것의 사용에 대해,하지만 다행히도 위키는 또한이 줄 수있는 윤곽 객체가 유용 왜이 반영 가지 이유 :
(내가 복사 (그리고 포맷 한)과는 향후 참조를 위해 다음과 같은 텍스트 소스는 내가 찾을 수 있는 유일한 예이므로 이해가되며 이미 좋은 설명이 있고 질문의 apply예를 터치합니다 .)


더 유용한 반환 값

대부분의 작업에서 Reflect정의 ES5 작업과 유사하다 Object등, Reflect.getOwnPropertyDescriptorReflect.defineProperty. 그러나 Object.defineProperty(obj, name, desc)will obj속성이 성공적으로 정의되었을 때 반환 되거나 TypeError그렇지 않으면 throw Reflect.defineProperty(obj, name, desc)는 속성이 성공적으로 정의되었는지 여부를 나타내는 부울을 반환하도록 지정됩니다. 이를 통해이 코드를 리팩토링 할 수 있습니다.

try {
  Object.defineProperty(obj, name, desc);
  // property defined successfully
} catch (e) {
  // possible failure (and might accidentally catch the wrong exception)
}

이에:

if (Reflect.defineProperty(obj, name, desc)) {
  // success
} else {
  // failure
}

이러한 부울 성공 상태를 반환하는 다른 메서드는 Reflect.set(속성 업데이트), Reflect.deleteProperty(속성 삭제), Reflect.preventExtensions(객체를 확장 불가능하게 만들기) 및 Reflect.setPrototypeOf(객체의 프로토 타입 링크 업데이트)입니다.


일류 운영

ES5에서 객체 obj가 특정 속성 이름을 정의하거나 상속 하는지 여부를 감지하는 방법 은 (name in obj). 마찬가지로 속성을 삭제하려면 delete obj[name]. 전용 구문이 훌륭하고 짧지 만, 이는 또한 작업을 일류 값으로 전달하려는 경우 이러한 작업을 함수에 명시 적으로 래핑해야 함을 의미합니다.

하여 Reflect이러한 작업이 용이 일류 함수로 정의된다
Reflect.has(obj, name)의 기능적 동등 (name in obj)Reflect.deleteProperty(obj, name)같은 작업을 수행하는 기능이다delete obj[name].


보다 안정적인 기능 적용

ES5에서 f배열로 압축 된 가변 개수의 인수를 사용 하여 함수를 호출 args하고 this값을에 바인딩하려면 다음과 같이 obj작성할 수 있습니다.

f.apply(obj, args)

그러나 f의도적으로 또는 의도하지 않게 자체 apply메서드를 정의하는 개체 일 수 있습니다 . 내장 apply함수가 호출 되었는지 확인하려면 일반적으로 다음과 같이 작성합니다.

Function.prototype.apply.call(f, obj, args)

이것은 장황 할뿐만 아니라 금방 이해하기 어려워집니다. 를 사용하면 Reflect이제 더 짧고 이해하기 쉬운 방식으로 안정적인 함수 호출을 수행 할 수 있습니다.

Reflect.apply(f, obj, args)


가변 인수 생성자

가변 개수의 인수를 사용하여 생성자 함수를 호출한다고 가정 해보십시오. ES6에서는 새로운 확산 구문 덕분에 다음과 같은 코드를 작성할 수 있습니다.

var obj = new F(...args)

ES5에서, 하나는 사용할 수 있기 때문에 이것은 더 열심히 작성하는 것입니다 F.apply또는 F.call가변 인자를 가진 함수를 호출 할 수 있지만이 없습니다 F.construct에 기능 new가변 인자를 가진 함수는. 를 사용하면 Reflect이제 ES5로 작성할 수 있습니다.

var obj = Reflect.construct(F, args)


프록시 트랩에 대한 기본 전달 동작

Proxy개체를 사용하여 기존 개체를 래핑 할 때 작업을 가로 채고 작업을 수행 한 다음 일반적으로 가로 챈 작업을 래핑 된 개체에 적용하는 "기본 작업을 수행"하는 것이 매우 일반적입니다. 예를 들어 객체에 대한 모든 속성 액세스를 간단히 기록하고 싶다고 가정 해 보겠습니다 obj.

var loggedObj = new Proxy(obj, {
  get: function(target, name) {
    console.log("get", target, name);
    // now do the default thing
  }
});

ReflectProxyAPI는 협력하여 설계된 각각 있도록, Proxy함정, 해당 방법이 존재하는 Reflect그 "기본 일을한다". 따라서 Proxy 핸들러 내에서 "기본"작업을 수행하려는 경우 항상 해당 Reflect객체 에서 해당 메서드를 호출하는 것이 올바른 방법입니다 .

var loggedObj = new Proxy(obj, {
  get: function(target, name) {
    console.log("get", target, name);
    return Reflect.get(target, name);
  }
});

의 반환 형식 Reflect방법의 반환 형식과 호환되도록 보장 Proxy트랩.


접근 자의이 바인딩 제어

ES5에서는 일반 속성 액세스 또는 속성 업데이트를 수행하는 것이 매우 쉽습니다. 예를 들면 :

var name = ... // get property name as a string
obj[name] // generic property lookup
obj[name] = value // generic property update

Reflect.getReflect.set방법은 같은 일을하지만, 또한 마지막 옵션 인수로 받아 들일 수 있도록 receiver명시 적으로 설정하는 방법을 수 있도록 매개 변수 this속성이 당신이 얻을 때 - 결합을 / 세트는 접근이다 :

var name = ... // get property name as a string
Reflect.get(obj, name, wrapper) // if obj[name] is an accessor, it gets run with `this === wrapper`
Reflect.set(obj, name, value, wrapper)

이것은 래핑 할 때 obj접근 자 내에서 자체 전송이 래퍼로 다시 라우팅되기를 원할 때 유용합니다 ( 예 obj: 다음과 같이 정의 된 경우 ).

var obj = {
  get foo() { return this.bar(); },
  bar: function() { ... }
}

전화 Reflect.get(obj, "foo", wrapper)this.bar()걸면 전화가로 다시 라우팅됩니다 wrapper.


유산 피하기 __proto__

일부 브라우저에서는 __proto__개체의 프로토 타입에 대한 액세스를 제공하는 특수 속성으로 정의됩니다. ES5 Object.getPrototypeOf(obj)는 프로토 타입을 쿼리 하는 새로운 방법 을 표준화했습니다 . 객체의 프로토 타입을 설정 하는 데 해당하는를 정의 Reflect.getPrototypeOf(obj)한다는 점을 제외하면 정확히 동일 Reflect합니다 Reflect.setPrototypeOf(obj, newProto). 이것은 객체의 프로토 타입을 업데이트하는 새로운 ES6 호환 방법입니다.
참고 : setPrototypeOf 또한에 존재합니다Object ( Knu의견에서 올바르게 지적한대로 )!


편집 :
사이드 노트 (Q에 대한 주석 처리) : 'Q : ES6 모듈 대 HTML 가져 오기' 에 대해 설명 Realms하고 Loader개체 를 설명 하는 짧고 간단한 답변이 있습니다.

이 링크에서 다른 설명이 제공됩니다 .

영역 개체는 고유 한 전역 개체, 표준 라이브러리의 복사본 및 "내장"(Object.prototype의 초기 값과 같이 전역 변수에 바인딩되지 않은 표준 개체)을 사용하여 고유 한 전역 환경의 개념을 추상화합니다.

확장 가능한 웹 : 이것은 <iframe>DOM이없는 동일 출처의 동적 동등 물입니다 .

그래도 언급 할 가치 가 있습니다 .이 모든 것은 아직 초안이며 돌에 새겨 져있는 사양이 아닙니다! ES6이므로 브라우저 호환성을 염두에 두십시오!

도움이 되었기를 바랍니다!


@Spencer Killen : 글쎄 ..이 대답은 당신의 생각을 올바른 방향으로 가리키고 이것이와의 차이점 Reflect.applytarget.apply어떻게 관련되는지 설명했 습니까? 아니면 현상금이 끝나기 전에 무엇을 추가해야합니까?
GitaarLAB

2
setPrototypeOfObject.
KNU

1
Reflect.get프로토 타입 속성이있는 객체를 프록시하는 경우 프록시 가져 오기의 기본 구현으로 사용 하면 제대로 작동하지 않습니다. 작동하지 않는다고 불평합니다. Reflect.get(target, property)그러나을 전달하지 않고 대신 사용하면 receiver작동합니다.
CMCDragonkai

프록시를 통해 항상 속성에 액세스하는 내 테스트 target는 프록시가 래핑하는 원래 대상인 반면 receiver프록시 자체는 인 상황을 초래 합니다. 그러나 다시 속성에 다르게 액세스 할 수 있다면 이것은 다를 수 있습니다.
CMCDragonkai

5

위키에서 찾은 초안 문서를 살펴보면

http://wiki.ecmascript.org/doku.php?id=harmony:specification_drafts

우리는 초안에서 명확히하는 "단일 일반 객체"에 대한 라인을 얻습니다. 또한 함수 정의가 있습니다.

위키는 emcascript 웹 사이트에서 링크를 찾을 수 있으므로 신뢰할 수 있어야합니다.

http://www.ecmascript.org/dev.php

그래도 Google의 첫 번째 링크를 찾았고 위키를 직접 검색하여 찾을 운이 없었습니다.


감사합니다. 공식 사양에 나열된 각 메서드가 호출 될 때 취한 단계는 내 링크의 단계와 거의 동일 해 보이지만 예를 들어 Reflect 아래에서 호출 될 때 각 메서드가 수행하는 작업을 파악할 수 없습니다. 적용 단계가 나열됩니다. 4. PrepareForTailCall 추상 작업을 수행합니다. 5. thisArgument 및 args 인수를 사용하여 target의 [[Call]] 내부 메서드를 호출 한 결과를 반환합니다. -------이게 무슨 뜻입니까?
Jim Jones

그것을 보면서 가장 좋은 추측은 4 단계에서 꼬리 재귀를 참조하고 5 단계는 프로토 타입 함수에 대한 참조입니다. 일반적인 아이디어는 apply 메소드가 적용 대상 (1-2 단계), 오류 핸들 (3 단계)에서 실행될 수 있는지 확인한 다음 apply 함수를 호출하는 것입니다 (4-5 단계). 문서를 훑어 보면서 제가 추측 할 수있는 가장 좋은 점은 Reflect 모듈의 요점은 객체에 대한 어떤 형태의 검사가 필요한 기능을 수행 할 때라는 것입니다. call의 사용은 아마도 "[[Call]] 내부 메소드가없는"이유 일 것입니다.
Blue
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.