[ Web Proxy ]
URL:
Viewing: https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Proxy [Back]  [Original]

Proxy - JavaScript | MDN

This page was translated from English by the community. Learn more and join the MDN Web Docs community.

View in English Always switch to English

Proxy

Baseline Widely available

This feature is well established and works across many devices and browser versions. Its been available across browsers since 2016 9.

Proxy .

In this article

Proxy Object , , . , , , .

Proxy .

  • target:
  • handler: .

.

js
const target = {
  message1: "hello",
  message2: "everyone",
};

const handler1 = {};

const proxy1 = new Proxy(target, handler1);

.

js
console.log(proxy1.message1); // hello
console.log(proxy1.message2); // everyone

.

js
const target = {
  message1: "hello",
  message2: "everyone",
};

const handler2 = {
  get(target, prop, receiver) {
    return "world";
  },
};

const proxy2 = new Proxy(target, handler2);

get() .

(traps) . handler2 .

js
console.log(proxy2.message1); // world
console.log(proxy2.message2); // world

Reflect .

js
const target = {
  message1: "hello",
  message2: "everyone",
};

const handler3 = {
  get(target, prop, receiver) {
    if (prop === "message2") {
      return "world";
    }
    return Reflect.get(...arguments);
  },
};

const proxy3 = new Proxy(target, handler3);

console.log(proxy3.message1); // hello
console.log(proxy3.message2); // world

Proxy()

Proxy .

Proxy.revocable()

Proxy .

37 . get() .

js
const handler = {
  get(obj, prop) {
    return prop in obj ? obj[prop] : 37;
  },
};

const 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

No-op

JavaScript .

js
const target = {};
const p = new Proxy(target, {});

p.a = 37;
//     

console.log(target.a);
//  37
//  (  !)

"no-op" JavaScript DOM , Map . .

ID , . .

js
class Secret {
  #secret;
  constructor(secret) {
    this.#secret = secret;
  }
  get secret() {
    return this.#secret.replace(/\d+/, "[REDACTED]");
  }
}

const aSecret = new Secret("123456");
console.log(aSecret.secret); // [REDACTED]
// no-op  ...
const proxy = new Proxy(aSecret, {});
console.log(proxy.secret); // TypeError: Cannot read private member #secret from an object whose class did not declare it

get this secret proxy #secret . secret this .

js
const proxy = new Proxy(aSecret, {
  get(target, prop, receiver) {
    //  'this' 
    // Reflect.get(target, prop, receiver) .
    return target[prop];
  },
});
console.log(proxy.secret);

this .

js
class Secret {
  #x = 1;
  x() {
    return this.#x;
  }
}

const aSecret = new Secret();
const proxy = new Proxy(aSecret, {
  get(target, prop, receiver) {
    const value = target[prop];
    if (value instanceof Function) {
      return function (...args) {
        return value.apply(this === receiver ? target : this, args);
      };
    }
    return value;
  },
});
console.log(proxy.x());

JavaScript JavaScript . , Map - [[MapData]] . .

js
const proxy = new Proxy(new Map(), {});
console.log(proxy.size); // TypeError: get size method called on incompatible Proxy

"this-recovering" .

. set() .

js
const validator = {
  set(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");
      }
    }

    //    
    obj[prop] = value;

    //  
    return true;
  },
};

const person = new Proxy({}, validator);

person.age = 100;
console.log(person.age); // 100
person.age = "young"; //  
person.age = 300; //  

. construct() apply() .

js
function extend(sup, base) {
  base.prototype = Object.create(sup.prototype);
  base.prototype.constructor = new Proxy(base, {
    construct(target, args) {
      const obj = Object.create(base.prototype);
      this.apply(target, obj, args);
      return obj;
    },
    apply(target, that, args) {
      sup.apply(that, args);
      base.apply(that, args);
    },
  });
  return base.prototype.constructor;
}

const Person = function (name) {
  this.name = name;
};

const Boy = extend(Person, function (name, age) {
  this.age = age;
});

Boy.prototype.gender = "M";

const Peter = new Boy("Peter", 13);

console.log(Peter.gender); // "M"
console.log(Peter.name); // "Peter"
console.log(Peter.age); // 13

DOM

Proxy . .

selected view . set() .

view.selected HTML 'aria-selected' true . view.selected 'aria-selected' true 'aria-selected' false .

js
const view = new Proxy(
  {
    selected: null,
  },
  {
    set(obj, prop, newval) {
      const oldval = obj[prop];

      if (prop === "selected") {
        if (oldval) {
          oldval.setAttribute("aria-selected", "false");
        }
        if (newval) {
          newval.setAttribute("aria-selected", "true");
        }
      }

      //   
      obj[prop] = newval;

      //  
      return true;
    },
  },
);

const item1 = document.getElementById("item-1");
const item2 = document.getElementById("item-2");

// item1 
view.selected = item1;

console.log(`item1: ${item1.getAttribute("aria-selected")}`);
// item1: true

// item2  item1  
view.selected = item2;

console.log(`item1: ${item1.getAttribute("aria-selected")}`);
// item1: false

console.log(`item2: ${item2.getAttribute("aria-selected")}`);
// item2: true

products . getter setter latestBrowser .

js
const products = new Proxy(
  {
    browsers: ["Internet Explorer", "Netscape"],
  },
  {
    get(obj, prop) {
      //  
      if (prop === "latestBrowser") {
        return obj.browsers[obj.browsers.length - 1];
      }

      //    
      return obj[prop];
    },
    set(obj, prop, value) {
      //  
      if (prop === "latestBrowser") {
        obj.browsers.push(value);
        return true;
      }

      //      
      if (typeof value === "string") {
        value = [value];
      }

      //    
      obj[prop] = value;

      //  
      return true;
    },
  },
);

console.log(products.browsers);
//  ['Internet Explorer', 'Netscape']

products.browsers = "Firefox";
// ()  

console.log(products.browsers);
//  ['Firefox'] <-    

products.latestBrowser = "Chrome";

console.log(products.browsers);
//  ['Firefox', 'Chrome']

console.log(products.latestBrowser);
//  'Chrome'

. Object.defineProperties() "" . . table.rows .

js
const products = new Proxy(
  [
    { name: "Firefox", type: "browser" },
    { name: "SeaMonkey", type: "browser" },
    { name: "Thunderbird", type: "mailer" },
  ],
  {
    get(obj, prop) {
      //    . prop  int
      if (prop in obj) {
        return obj[prop];
      }

      // product  . products.length  
      if (prop === "number") {
        return obj.length;
      }

      let result;
      const types = {};

      for (const product of obj) {
        if (product.name === prop) {
          result = product;
        }
        if (types[product.type]) {
          types[product.type].push(product);
        } else {
          types[product.type] = [product];
        }
      }

      //  product 
      if (result) {
        return result;
      }

      // type product 
      if (prop in types) {
        return types[prop];
      }

      // types product 
      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

trap

traps docCookies .

js
/*
  const docCookies = ...  "docCookies" .
  https://reference.codeproject.com/dom/document/cookie/simple_document.cookie_framework
*/

const docCookies = new Proxy(docCookies, {
  get(target, key) {
    return target[key] || target.getItem(key) || undefined;
  },
  set(target, key, value) {
    if (key in target) {
      return false;
    }
    return target.setItem(key, value);
  },
  deleteProperty(target, key) {
    if (!(key in target)) {
      return false;
    }
    return target.removeItem(key);
  },
  ownKeys(target) {
    return target.keys();
  },
  has(target, key) {
    return key in target || target.hasItem(key);
  },
  defineProperty(target, key, descriptor) {
    if (descriptor && "value" in descriptor) {
      target.setItem(key, descriptor.value);
    }
    return target;
  },
  getOwnPropertyDescriptor(target, key) {
    const value = target.getItem(key);
    return value
      ? {
          value,
          writable: true,
          enumerable: true,
          configurable: false,
        }
      : undefined;
  },
});

/*   */

console.log((docCookies.myCookie1 = "First value"));
console.log(docCookies.getItem("myCookie1"));

docCookies.setItem("myCookie1", "Changed value");
console.log(docCookies.myCookie1);

Specification
ECMAScript 2027 LanguageSpecification
# sec-proxy-objects


Web Proxy Viewer  |  New URL  |  Original Page