--- title: Proxy slug: Web/JavaScript/Reference/Global_Objects/Proxy tags: - ECMAScript 2015 - JavaScript - Proxy - Reference translation_of: Web/JavaScript/Reference/Global_Objects/Proxy --- <div>{{JSRef}}</div> <p><code><strong>Proxy</strong></code> 객체는 기본적인 동작(속성 접근, 할당, 순회, 열거, 함수 호출 등)의 새로운 행동을 정의할 때 사용합니다.</p> <h2 id="용어">용어</h2> <dl> <dt><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler">handler</a></dt> <dd>trap들을 가지고 있는 Placeholder 객체.</dd> <dt>traps</dt> <dd>프로퍼티에 접근할 수 있는 메소드. 운영체제에서 trap 이라는 컨셉과 유사하다.</dd> <dt>target</dt> <dd>proxy가 가상화하는 실제 객체. 이것은 proxy를 위한 backend 저장소로 사용된다. Invariants (semantics that remain unchanged) regarding object non-extensibility or non-configurable properties are verified against the target.</dd> </dl> <h2 id="구문">구문</h2> <pre class="syntaxbox">new Proxy(target, handler); </pre> <h3 id="매개변수">매개변수</h3> <dl> <dt><code>target</code></dt> <dd>proxy와 함께 감싸진 target 객체 (native array, function, 다른 proxy을 포함한 객체)</dd> <dt><code>handler</code></dt> <dd>프로퍼티들이 function 인 객체이다. 동작이 수행될 때, handler는 proxy의 행동을 정의한다.</dd> </dl> <h2 id="메서드">메서드</h2> <dl> <dt>{{jsxref("Proxy.revocable()")}}</dt> <dd>폐기할 수 있는(revocable) Proxy 객체를 생성.</dd> </dl> <h2 id="Methods_of_the_handler_object">Methods of the handler object</h2> <p>handler객체는 <code>Proxy를 위한 trap들을 포함하고 있는 </code>placeholder 객체이다.</p> <div>{{page('/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler', 'Methods') }}</div> <h2 id="예제">예제</h2> <h3 id="Basic_example">Basic example</h3> <p>프로퍼티 이름이 객체에 없을때, 기본값을 숫자 37로 리턴받는 간단한 예제이다. 이것은 <a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/get"><code>get</code></a> handler 를 사용하였다. </p> <pre class="brush: js">var handler = { get: function(target, name){ return name in target? target[name] : 37; } }; var p = new Proxy({}, handler); p.a = 1; p.b = undefined; console.log(p.a, p.b); // 1, undefined console.log('c' in p, p.c); // false, 37 </pre> <h3 id="No-op_forwarding_proxy">No-op forwarding proxy</h3> <p>이 예제에서는, native JavaScript를 사용하겠다. proxy는 적용된 모든 동작으로 보낼 것이다.</p> <pre class="brush: js">var target = {}; var p = new Proxy(target, {}); p.a = 37; // target으로 동작이 전달 console.log(target.a); // 37. 동작이 제대로 전달됨 </pre> <h3 id="Validation_(검증)">Validation (검증)</h3> <p>Proxy에서, 객체에 전달된 값을 쉽게 검증할 수 있다. 이 예제는 <a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/set"><code>set</code></a> handler 를 사용하였다.</p> <pre class="brush: js">let validator = { set: function(obj, prop, value) { if (prop === 'age') { if (!Number.isInteger(value)) { throw new TypeError('The age is not an integer'); } if (value > 200) { throw new RangeError('The age seems invalid'); } } // The default behavior to store the value obj[prop] = value; } }; let person = new Proxy({}, validator); person.age = 100; console.log(person.age); // 100 person.age = 'young'; // Throws an exception person.age = 300; // Throws an exception </pre> <h3 id="Extending_constructor_(생성자_확장)">Extending constructor (생성자 확장)</h3> <p>function proxy는 쉽게 새로운 생성자와 함께 생성자를 확장할 수 있다. 이 예제에서는 <code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/construct">construct</a> 와</code> <a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/apply"><code>apply</code></a> handlers 를 사용하였다.</p> <pre class="brush: js">function extend(sup,base) { var descriptor = Object.getOwnPropertyDescriptor( base.prototype,"constructor" ); base.prototype = Object.create(sup.prototype); var handler = { construct: function(target, args) { var obj = Object.create(base.prototype); this.apply(target,obj,args); return obj; }, apply: function(target, that, args) { sup.apply(that,args); base.apply(that,args); } }; var proxy = new Proxy(base,handler); descriptor.value = proxy; Object.defineProperty(base.prototype, "constructor", descriptor); return proxy; } var Person = function(name){ this.name = name; }; var Boy = extend(Person, function(name, age) { this.age = age; }); Boy.prototype.sex = "M"; var Peter = new Boy("Peter", 13); console.log(Peter.sex); // "M" console.log(Peter.name); // "Peter" console.log(Peter.age); // 13</pre> <h3 id="Manipulating_DOM_nodes_(DOM_nodes_조작)">Manipulating DOM nodes (DOM nodes 조작)</h3> <p>가끔씩, 두 개의 다른 element의 속성이나 클래스 이름을 바꾸고 싶을 것이다. 아래는 <a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/set"><code>set</code></a> handler 를 사용하였다.</p> <pre class="brush: js">let view = new Proxy({ selected: null }, { set: function(obj, prop, newval) { let oldval = obj[prop]; if (prop === 'selected') { if (oldval) { oldval.setAttribute('aria-selected', 'false'); } if (newval) { newval.setAttribute('aria-selected', 'true'); } } // The default behavior to store the value obj[prop] = newval; } }); let i1 = view.selected = document.getElementById('item-1'); console.log(i1.getAttribute('aria-selected')); // 'true' let i2 = view.selected = document.getElementById('item-2'); console.log(i1.getAttribute('aria-selected')); // 'false' console.log(i2.getAttribute('aria-selected')); // 'true' </pre> <h3 id="Value_correction_and_an_extra_property_(값_정정과_추가적인_property)">Value correction and an extra property (값 정정과 추가적인 property)</h3> <p><code>products</code> 라는 proxy 객체는 전달된 값을 평가하고, 필요할 때 배열로 변환한다. 이 객체는 <code>latestBrowser</code> 라는 추가적인 property를 지원하는데, getter와 setter 모두 지원한다. </p> <pre class="brush: js">let products = new Proxy({ browsers: ['Internet Explorer', 'Netscape'] }, { get: function(obj, prop) { // An extra property if (prop === 'latestBrowser') { return obj.browsers[obj.browsers.length - 1]; } // The default behavior to return the value return obj[prop]; }, set: function(obj, prop, value) { // An extra property if (prop === 'latestBrowser') { obj.browsers.push(value); return; } // Convert the value if it is not an array if (typeof value === 'string') { value = [value]; } // The default behavior to store the value obj[prop] = value; } }); console.log(products.browsers); // ['Internet Explorer', 'Netscape'] products.browsers = 'Firefox'; // pass a string (by mistake) console.log(products.browsers); // ['Firefox'] <- no problem, the value is an array products.latestBrowser = 'Chrome'; console.log(products.browsers); // ['Firefox', 'Chrome'] console.log(products.latestBrowser); // 'Chrome' </pre> <h3 id="Finding_an_array_item_object_by_its_property_(property로_배열의_객체를_찾기)">Finding an array item object by its property (property로 배열의 객체를 찾기)</h3> <p>proxy 는 유용한 특성을 가진 배열로 확장할 것이다. <code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties">Object.defineProperties</a>를 사용하지 않고, </code>유연하게 property들을 유연하게 "정의"할 수 있다. 이 예제는 테이블의 cell을 이용해서 row(열)을 찾는데 적용할 수 있다. 이 경우, target은 <code><a href="/en-US/docs/DOM/table.rows">table.rows</a>가 될 것이다.</code></p> <pre class="brush: js">let products = new Proxy([ { name: 'Firefox', type: 'browser' }, { name: 'SeaMonkey', type: 'browser' }, { name: 'Thunderbird', type: 'mailer' } ], { get: function(obj, prop) { // The default behavior to return the value; prop is usually an integer if (prop in obj) { return obj[prop]; } // Get the number of products; an alias of products.length if (prop === 'number') { return obj.length; } let result, types = {}; for (let product of obj) { if (product.name === prop) { result = product; } if (types[product.type]) { types[product.type].push(product); } else { types[product.type] = [product]; } } // Get a product by name if (result) { return result; } // Get products by type if (prop in types) { return types[prop]; } // Get product types if (prop === 'types') { return Object.keys(types); } return undefined; } }); console.log(products[0]); // { name: 'Firefox', type: 'browser' } console.log(products['Firefox']); // { name: 'Firefox', type: 'browser' } console.log(products['Chrome']); // undefined console.log(products.browser); // [{ name: 'Firefox', type: 'browser' }, { name: 'SeaMonkey', type: 'browser' }] console.log(products.types); // ['browser', 'mailer'] console.log(products.number); // 3 </pre> <h3 id="A_complete_traps_list_example_(완벽한_traps_리스트_예제)">A complete <code>traps</code> list example (완벽한 <code>traps</code> 리스트 예제)</h3> <p>이제 완벽한 traps 리스트를 생성하기 위해서, <em>non native</em> 객체를 프록시화 할 것이다. 이것은 특히, 다음과 같은 동작에 적합하다 : <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie/Simple_document.cookie_framework" title="https://developer.mozilla.org/en-US/docs/DOM/document.cookie#A_little_framework.3A_a_complete_cookies_reader.2Fwriter_with_full_unicode_support">the "little framework" published on the <code>document.cookie</code> page</a> 에 의해 생성된 <code>docCookies</code> 는 글로벌 객체</p> <pre class="brush: js">/* var docCookies = ... get the "docCookies" object here: https://developer.mozilla.org/en-US/docs/DOM/document.cookie#A_little_framework.3A_a_complete_cookies_reader.2Fwriter_with_full_unicode_support */ var docCookies = new Proxy(docCookies, { get: function (oTarget, sKey) { return oTarget[sKey] || oTarget.getItem(sKey) || undefined; }, set: function (oTarget, sKey, vValue) { if (sKey in oTarget) { return false; } return oTarget.setItem(sKey, vValue); }, deleteProperty: function (oTarget, sKey) { if (sKey in oTarget) { return false; } return oTarget.removeItem(sKey); }, enumerate: function (oTarget, sKey) { return oTarget.keys(); }, ownKeys: function (oTarget, sKey) { return oTarget.keys(); }, has: function (oTarget, sKey) { return sKey in oTarget || oTarget.hasItem(sKey); }, defineProperty: function (oTarget, sKey, oDesc) { if (oDesc && "value" in oDesc) { oTarget.setItem(sKey, oDesc.value); } return oTarget; }, getOwnPropertyDescriptor: function (oTarget, sKey) { var vValue = oTarget.getItem(sKey); return vValue ? { value: vValue, writable: true, enumerable: true, configurable: false } : undefined; }, }); /* Cookies test */ console.log(docCookies.my_cookie1 = "First value"); console.log(docCookies.getItem("my_cookie1")); docCookies.setItem("my_cookie1", "Changed value"); console.log(docCookies.my_cookie1);</pre> <h2 id="명세">명세</h2> <table class="standard-table"> <tbody> <tr> <th scope="col">Specification</th> <th scope="col">Status</th> <th scope="col">Comment</th> </tr> <tr> <td>{{SpecName('ES2015', '#sec-proxy-objects', 'Proxy')}}</td> <td>{{Spec2('ES2015')}}</td> <td>Initial definition.</td> </tr> <tr> <td>{{SpecName('ES2016', '#sec-proxy-objects', 'Proxy')}}</td> <td>{{Spec2('ES2016')}}</td> <td> </td> </tr> <tr> <td>{{SpecName('ES2017', '#sec-proxy-objects', 'Proxy')}}</td> <td>{{Spec2('ES2017')}}</td> <td> </td> </tr> <tr> <td>{{SpecName('ESDraft', '#sec-proxy-objects', 'Proxy')}}</td> <td>{{Spec2('ESDraft')}}</td> <td> </td> </tr> </tbody> </table> <h2 id="브라우저_호환성">브라우저 호환성</h2> <p>{{Compat("javascript.builtins.Proxy", 2)}}</p> <h2 id="같이_보기">같이 보기</h2> <ul> <li><a class="external" href="https://www.youtube.com/watch?v=sClk6aB_CPk">"Proxies are awesome" Brendan Eich presentation at JSConf</a> (<a class="external" href="http://www.slideshare.net/BrendanEich/metaprog-5303821">slides</a>)</li> <li><a class="external" href="http://wiki.ecmascript.org/doku.php?id=harmony:proxies">ECMAScript Harmony Proxy proposal page</a> and <a class="external" href="http://wiki.ecmascript.org/doku.php?id=harmony:proxies_semantics">ECMAScript Harmony proxy semantics page</a></li> <li><a class="external" href="http://soft.vub.ac.be/~tvcutsem/proxies/">Tutorial on proxies</a></li> <li><a href="/en-US/docs/JavaScript/Old_Proxy_API" title="/en-US/docs/JavaScript/Old_Proxy_API">SpiderMonkey specific Old Proxy API</a></li> <li>{{jsxref("Object.watch()")}} is a non-standard feature but has been supported in Gecko for a long time.</li> </ul> <h2 id="라이센스_참고사항">라이센스 참고사항</h2> <p>Some content (text, examples) in this page has been copied or adapted from the <a class="external" href="http://wiki.ecmascript.org/doku.php">ECMAScript wiki</a> which content is licensed <a class="external" href="http://creativecommons.org/licenses/by-nc-sa/2.0/">CC 2.0 BY-NC-SA</a>.</p>