ES6 표준 시작 요약(Proxy)

13742 단어

Proxy


Proxy는 목표 대상에 앞서'차단'을 설치하고 외부에서 이 대상에 대한 방문을 반드시 이 층을 통해 차단해야 하기 때문에 외부의 방문을 필터하고 고칠 수 있는 메커니즘을 제공한 것으로 이해할 수 있다.Proxy라는 단어의 원래 뜻은 에이전트입니다. 에이전트가 어떤 조작을 에이전트로 하는지 표시하는 데 사용되며, 에이전트로 번역할 수 있습니다.
문법: var proxy = new Proxy(target, handler);Proxy 대상의 모든 사용법은 위와 같은 형식이고, 다른것은handler 매개 변수의 쓰기 방법일 뿐이다.그 중에서 new Proxy () 는 Proxy 실례를 생성하고 target 파라미터는 차단할 대상을 표시하며handler 파라미터도 하나의 대상으로 차단 행위를 맞춤형으로 설정합니다.
var proxy = new Proxy({}, {
  get: function(target, property) {
    return 35;
  }
});

proxy.time // 35 
proxy.name // 35
proxy.title // 35
//  . 
//  , get , 35

Proxy가 작동하려면 대상 대상 (상례는 빈 대상, target 매개 변수) 이 아니라 Proxy 실례 (상례는 proxy 대상) 에 대해 조작해야 합니다.
var target = {};
var handler = {};
var proxy = new Proxy(target, handler);
proxy.a = 'b';
console.log(target.a) // "b"
//  , proxy target。

Proxy 인스턴스는 다른 객체의 원형 객체가 될 수도 있습니다.
var proxy = new Proxy({}, {
  get: function(target, property) {
    return 35;
  }
});

let obj = Object.create(proxy);
//  obj.__proto__ === proxy
obj.time // 35

// proxy obj ,obj time ,
//  , proxy , 。

Proxy 지원 차단 작업


get(target, propKey, receiver)
대상 속성의 읽기를 차단하면 세 개의 매개 변수를 받아들일 수 있는데 그것이 바로 목표 대상, 속성 이름과proxy 실례 자체(엄밀히 말하면 조작 행위가 겨냥한 대상)이고 그 중 마지막 매개 변수는 선택할 수 있다.
Proxy를 이용하여 속성을 읽는 동작 (get) 을 함수를 실행하는 것으로 바꾸어 속성을 실현하는 체인 동작을 할 수 있습니다.
var pipe = (function () {
  return function (value) {
    var funcStack = [];
    var oproxy = new Proxy({} , {
      get : function (pipeObject, fnName) {
        if (fnName === 'get') {
          return funcStack.reduce(function (val, fn) {
            return fn(val);
          },value);
        }
        funcStack.push(window[fnName]);
        return oproxy;
      }
    });

    return oproxy;
  }
}());

var double = n => n * 2;
var pow    = n => n * n;
var reverseInt = n => n.toString().split("").reverse().join("") | 0;
//   window.double , window.pow, window.rereverseInt  push funcStack 
//  .get , 3 , , 。
pipe(3).double.pow.reverseInt.get; // 63

get 차단을 이용하여 각종 DOM 노드를 생성하는 공통 함수dom
const dom = new Proxy({}, {
  get(target, property) {
    return function(attrs = {}, ...children) {
      const el = document.createElement(property);
      for (let prop of Object.keys(attrs)) {
        el.setAttribute(prop, attrs[prop]);
      }
      for (let child of children) {
        if (typeof child === 'string') {
          child = document.createTextNode(child);
        }
        el.appendChild(child);
      }
      return el;
    }
  }
});

//  dom.xx   xx dom 
//  dom 
//  dom (     )
const el = dom.div({},
  'Hello, my name is ',
  dom.a({href: 'www.example.com'}, 'Mark'),
  '. I like:',
  dom.ul({},
    dom.li({}, 'The web'),
    dom.li({}, 'Food'),
    dom.li({}, '…actually that\'s it')
  )
);
document.body.appendChild(el);

get 방법의 세 번째 매개 변수의 예는 항상 원시적인 읽기 동작이 있는 대상을 가리키며 일반적인 상황에서는 Proxy 실례를 가리킨다.
const proxy = new Proxy({}, {
  get: function(target, property, receiver) {
    return receiver;
  }
});
// receiver proxy
proxy.getReceiver === proxy // true

// d.__proto__ = proxy
const d = Object.create(proxy);
// d   a  ,  proxy.a 
//  ,receiver d, 。
d.a === d // true

속성이 설정되지 않고 쓰기가 불가능한 경우 Proxy는 이 속성을 수정할 수 없습니다. 그렇지 않으면 Proxy 대상을 통해 이 속성에 접근하면 오류가 발생합니다.
set(target, propKey, value, receiver)
대상의 속성을 차단하는 설정은 목표 대상, 속성 이름, 속성 값과 Proxy 실례 자체 등 네 개의 매개 변수를 받아들일 수 있으며 그 중 마지막 매개 변수는 선택할 수 있습니다.부울 값을 반환합니다.
set 방법을 사용하면 데이터가 바인딩됩니다. 즉, 객체가 변경될 때마다 DOM이 자동으로 업데이트됩니다.
우리는 대상 위에 내부 속성을 설정할 것입니다. 속성 이름의 첫 번째 문자는 밑줄로 시작하여 외부에서 사용해서는 안 된다는 것을 나타냅니다.get과 set 방법을 결합하면 내부 속성이 외부에서 읽히지 않도록 할 수 있습니다.
const handler = {
  get (target, key) {
    invariant(key, 'get');
    return target[key];
  },
  set (target, key, value) {
    invariant(key, 'set');
    target[key] = value;
    //  ,set true, 。 false,undefined 
    return true;
  }
};
function invariant (key, action) {
  if (key[0] === '_') {
    throw new Error(`Invalid attempt to ${action} private "${key}" property`);
  }
}
const target = {};
const proxy = new Proxy(target, handler);
proxy._prop
// Error: Invalid attempt to get private "_prop" property
proxy._prop = 'c'
// Error: Invalid attempt to set private "_prop" property
//  , , 。

set 방법의 네 번째 매개 변수receiver는 get과 같이 원시적인 조작 행위가 있는 대상을 가리키며 일반적인 상황에서proxy 실례 자체이다
const handler = {
  set: function(obj, prop, value, receiver) {
    obj[prop] = receiver;
  }
};
const proxy = new Proxy({}, handler);

proxy.foo = 'bar';
proxy.foo === proxy // true

const myObj = {};
Object.setPrototypeOf(myObj, proxy);
//  set ,myObj.__proto__ = proxy

//  myObj.foo ,myObj foo , myObj foo 。
// myObj proxy  Proxy  , foo set 。
//  , receiver myObj。 
myObj.foo = 'bar';
//    myObj.foo  proxy.foo
myObj.foo === myObj // true

만약 목표 대상 자체의 어떤 속성이 쓸 수 없고 설정할 수 없다면 set 방법은 작용하지 않을 것입니다.
apply(target, object, args)
Proxy 실례를 함수로 호출하는 조작,call,apply 조작을 차단하고 목표 대상, 목표 대상의 상하문 대상 (this), 목표 대상의 매개 변수 그룹을 세 개 받아들인다.
var target = function () {};
var handler = {
  apply: function () {
    return 'I am the proxy';
  }
};
var p = new Proxy(target, handler);
//   target  , p()   p is not a function
// new Proxy   target,  
//  target  , handle 

//  apply  
console.log(p());

// "I am the proxy"

var twice = {
  apply (target, ctx, args) {
    return target(...args) * 2;
  }
};
function sum (left, right) {
  return left + right;
};
var proxy = new Proxy(sum, twice);
proxy(1, 2) // 6
proxy.call(null, 5, 6) // 22
proxy.apply(null, [7, 8]) // 30

has(target, propKey)
이 메서드는 객체에 속성이 있는지 여부를 판단하는 HasProperty 작업을 차단하는 데 사용됩니다.전형적인 조작은 in 연산자로 부울 값을 되돌려 주는 것이다.
has 방법은 두 개의 매개 변수를 받아들일 수 있는데, 각각 목표 대상, 조회할 속성 이름이다.
var handler = {
  has (target, key) {
    if (key[0] === '_') {
      //  false  in 
      return false;
    }
    //  ,in   true。
    return key in target;
  }
};
var target = { _prop: 'foo', prop: 'foo' };
Object.setPrototypeOf(target, {
  aaa: 'aaa'
})
var proxy = new Proxy(target, handler);
console.log('_prop' in proxy); // false
console.log('prop' in proxy); // true
console.log('aaa' in proxy); // true

//  ( ), has 。
Object.preventExtensions(target);
console.log('_prop' in proxy); // / TypeError

주의해야 할 것은has 방법은 HasProperty 조작을 차단하는 것이지HasOwnProperty 조작이 아니다. 즉has 방법은 하나의 속성이 대상 자체의 속성인지 계승의 속성인지 판단하지 않는다.
그리고 비록 for...in 순환도 in 연산자를 사용하지만has는 for를 차단합니다.in 순환은 적용되지 않습니다.
construct(target, args)
Proxy 실례를 구조 함수로 호출하는 동작을 차단합니다. 예를 들어 new proxy (...args).
construct 방법은 두 개의 매개 변수를 받아들일 수 있습니다.target: 대상 대상,args: 구조 함수의 매개 변수 대상,newTarget: 실례 대상을 만들 때 new 명령 작용의 구조 함수
var p = new Proxy(function (a, b, c) {}, {
  construct: function(target, args, newTarget) {
    console.log('called: ' + args.join(', '));
    // newTarget   target ,  3 a,b,c
    //  newTarget.length 3
    console.log(newTarget.length) // 3

    //  , 。
    return { value: args[0] * 10 };
  }
});

(new p(2)).value
// "called: 2"
// 20

deleteProperty(target, propKey)
delete proxy [propKey]의 동작을 차단하고 브리 값을 되돌려줍니다. 이 방법이 오류를 던지거나false를 되돌려주면 현재 속성은 delete 명령에 의해 삭제될 수 없습니다.대상 대상 자체의 설정 불가 (configurable) 속성은 deleteProperty 방법으로 삭제할 수 없습니다. 그렇지 않으면 오류가 발생합니다.
defineProperty(target, propKey, propDesc)
Object 차단.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy,propDescs), 부울 값을 되돌려줍니다.
또한 대상 대상을 확장할 수 없음 (non-extensible), defineProperty는 대상 대상에 존재하지 않는 속성을 추가할 수 없습니다. 그렇지 않으면 오류가 발생합니다.또한 대상 대상의 어떤 속성을 쓸 수 없거나 설정할 수 없거나 (configurable)할 경우 defineProperty 방법은 이 두 설정을 바꿀 수 없습니다.
getOwnPropertyDescriptor()
getOwnPropertyDescriptor 메서드가 Object를 가로막습니다.getOwnPropertyDescriptor (), 속성 설명 대상이나undefined를 되돌려줍니다.
getPrototypeOf()
getPrototypeOf 방법은 주로 대상의 원형을 가져오는 것을 차단하는 데 사용됩니다.구체적으로 말하면 아래의 조작을 가로막아라.
  • Object.prototype.proto
  • Object.prototype.isPrototypeOf()
  • Object.getPrototypeOf()
  • Reflect.getPrototypeOf()
  • instanceof

  • getPrototypeOf 방법의 반환 값은 대상이나null이어야 합니다. 그렇지 않으면 오류가 발생합니다.또한 대상 대상이 확장되지 않으면 getPrototypeOf 방법은 대상 대상의 원형 대상을 되돌려야 합니다.
    isExtensible()
    Object 차단.isExtensible 작업Object.isExtensible는 객체가 확장 가능한지 여부를 판단합니다(이 위에 새 속성을 추가할 수 있는지).
    이 방법은 부울 값만 되돌릴 수 있습니다. 그렇지 않으면 되돌림 값이 자동으로 부울 값으로 바뀝니다.
    이 방법은 대상 대상의 isExtensible 속성과 일치해야 하며, 그렇지 않으면 오류가 발생합니다.
    Object.isExtensible(proxy) === Object.isExtensible(target)
    

    ownKeys()
    ownKeys 메서드는 객체 자체의 속성에 대한 읽기 작업을 차단합니다.구체적으로 말하면 다음과 같은 조작을 차단한다.
  • Object.getOwnPropertyNames()
  • Object.getOwnPropertySymbols()
  • Object.keys()
  • for...in 루프
  • const obj = { hello: 'world' };
    const proxy = new Proxy(obj, {
      ownKeys: function () {
        return ['a', 'b'];
      }
    });
    
    for (let key in proxy) {
      console.log(key); //  
    }
    // ownkeys a b 
    //  obj , for...in 。
    

    ownKeys 메서드가 반환하는 배열 멤버는 문자열 또는 Symbol 값만 사용할 수 있습니다.만약 다른 종류의 값이 있거나, 되돌아오는 것이 전혀 수조가 아니라면, 오류를 보고할 것이다.
    Object를 사용합니다.keys 메서드는 ownKeys 메서드에 의해 자동으로 필터링되며 반환되지 않습니다.
  • 대상에 존재하지 않는 속성
  • 속성 이름은 Symbol 값
  • 범람 불가(enumerable)의 속성
  • 아래와 같다
    let target = {
      a: 1,
      b: 2,
      c: 3,
      [Symbol.for('secret')]: '4',
    };
    
    Object.defineProperty(target, 'key', {
      enumerable: false,
      configurable: true,
      writable: true,
      value: 'static'
    });
    
    let handler = {
      ownKeys(target) {
        return ['a', 'd', Symbol.for('secret'), 'key'];
      }
    };
    
    let proxy = new Proxy(target, handler);
    
    Object.keys(proxy)
    //  ['a']
    // 'd' ,Symbol.for('secret') Symbol  ,'key' (enumerable) 。
    

    대상 객체 자체에 구성할 수 없는 속성이 있으면 ownKeys 메소드에 의해 해당 속성이 반환되어야 합니다. 그렇지 않으면 오류가 보고됩니다.
    대상 대상이 확장할 수 없음 (non-extensible) 이면 ownKeys 방법이 되돌아오는 그룹에 원래 대상의 모든 속성을 포함해야 하며, 여분의 속성을 포함할 수 없습니다. 그렇지 않으면 오류가 발생합니다.
    preventExtensions()
    preventExtensions 메서드가 Object를 가로막습니다.preventExtensions().이 방법은 반드시 부울 값을 되돌려야 한다. 그렇지 않으면 자동으로 부울 값으로 바뀔 것이다.
    Object.preventExtensions () 방법은 대상을 확장할 수 없게 합니다. 즉, 새로운 속성을 영원히 추가할 수 없습니다.
    이 방법은 대상 대상이 확장 가능한proxy라면 제한이 있습니다.preventExtensions가true로 돌아가면 Object입니다.isExtensible(proxy)는false(확장불가),proxy.true로 돌아가려면 preventExtensions 를 사용하십시오.
    setPrototypeOf()
    setPrototypeOf 방법은 주로 Object를 차단하는 데 사용됩니다.setPrototypeOf 메서드
    이 방법은 부울 값만 되돌릴 수 있고, 그렇지 않으면 자동으로 부울 값으로 바뀔 수 있으니 주의하십시오.또한 목표 대상을 확장할 수 없음(non-extensible) 경우 setPrototypeOf 방법은 목표 대상의 원형을 바꿀 수 없습니다.

    Proxy.revocable()


    Proxy.revocable 메서드는 취소 가능한 Proxy 인스턴스를 반환합니다.
    let target = {};
    let handler = {};
    
    let {proxy, revoke} = Proxy.revocable(target, handler);
    
    proxy.foo = 123;
    proxy.foo // 123
    
    revoke();
    proxy.foo // TypeError: Revoked
    

    Proxy.revocable 방법은 대상을 되돌려줍니다. 이 대상의 proxy 속성은 Proxy 실례이고,revoke 속성은 함수입니다. Proxy 실례를 취소할 수 있습니다.위 코드에서revoke 함수를 실행한 후에Proxy 실례에 접근하면 오류가 발생합니다.장면을 사용하는 것은 목표 대상이 직접 접근을 허용하지 않기 때문에 에이전트를 통해 접근해야 하며 방문이 끝나면 에이전트를 회수하고 다시 접근을 허용하지 않는다는 것이다.

    질문


    Proxy는 대상 객체에 대한 액세스를 에이전트할 수 있지만 대상 객체에 대한 투명 에이전트가 아니므로 아무런 차단 없이 대상 객체의 동작과 일치하도록 보장할 수 없습니다.주요 원인은 Proxy 에이전트의 경우 대상 내부의 this 키워드가 Proxy 에이전트를 가리킨다는 것이다.
    const target = {
      m: function () {
        console.log(this === proxy);
      }
    };
    const handler = {};
    
    const proxy = new Proxy(target, handler);
    
    target.m() // false
    proxy.m()  // true
    

    다음은this가 가리키는 변화로 인해 Proxy가 목표 대상을 에이전트할 수 없습니다.
    const _name = new WeakMap();
    
    class Person {
      constructor(name) {
        _name.set(this, name);
      }
      get name() {
        return _name.get(this);
      }
    }
    
    const jane = new Person('Jane');
    jane.name // 'Jane'
    
    const proxy = new Proxy(jane, {});
    proxy.name // undefined
    //   return _name.get(proxy)
    //  set   undefined
    

    위 코드에서 대상 객체 제인의 name 속성은 실제 외부 WeakMap 객체에 저장됩니다.name 위,this 키를 통해 구분합니다.proxy를 통과했기 때문에.name에 접근할 때,this는proxy를 가리키며, 값을 찾을 수 없기 때문에undefined를 되돌려줍니다.
    일부 원생 대상의 내부 속성은 정확한this를 통해서만 얻을 수 있기 때문에Proxy도 이러한 원생 대상의 속성을 대리할 수 없습니다.
    const target = new Date();
    const handler = {};
    const proxy = new Proxy(target, handler);
    
    proxy.getDate();
    // TypeError: this is not a Date object.
    

    this를 원시 대상에 귀속시키면 이 문제를 해결할 수 있습니다.
    const target = new Date('2015-01-01');
    const handler = {
      get(target, prop) {
        if (prop === 'getDate') {
          return target.getDate.bind(target);
        }
        return Reflect.get(target, prop);
      }
    };
    const proxy = new Proxy(target, handler);
    
    proxy.getDate() // 1
    

    예: 웹 서비스의 클라이언트


    Proxy 대상은 웹 서비스를 쓰는 클라이언트에게 적합하도록 대상 대상의 모든 속성을 차단할 수 있습니다.
    따라서 모든 데이터에 적합한 방법을 쓰지 않고 Proxy 차단기만 쓰면 된다.
    const service = createWebService('http://example.com/data');
    
    // service.employees =  () => httpGet('http://example.com/data/employees');
    // service.employees()  httpGet  employees  
    
    service.employees().then(json => {
      const employees = JSON.parse(json);
      // ···
    });
    
    function createWebService(baseUrl) {
      return new Proxy({}, {
        get(target, propKey, receiver) {
          return () => httpGet(baseUrl+'/' + propKey);
        }
      });
    }
    

    좋은 웹페이지 즐겨찾기