>  기사  >  웹 프론트엔드  >  ES6에서 프록시를 사용하기 위한 지침

ES6에서 프록시를 사용하기 위한 지침

亚连
亚连원래의
2018-06-13 10:57:351564검색

이 글은 주로 ES6 Proxy의 사용 시나리오를 소개하고 참고 자료를 제공합니다.

ES6의 화살표 기능, 배열 분해, 나머지 매개변수와 같은 기능은 구현되자마자 널리 퍼졌습니다. 그러나 프록시와 같은 기능은 브라우저 호환성 때문에 거의 사용되지 않습니다. 또 다른 측면은 이러한 기능을 활용하려면 개발자가 사용 시나리오를 깊이 이해해야 한다는 것입니다. 개인적으로 저는 ES6의 Proxy를 매우 좋아합니다. 간결하고 이해하기 쉬운 방식으로 객체에 대한 외부 액세스를 제어할 수 있기 때문입니다. 다음에서는 먼저 Proxy 사용 방법을 소개한 후, 구체적인 예를 들어 Proxy 사용 시나리오를 설명하겠습니다.

프록시는 이름에서 알 수 있듯이 디자인 패턴의 프록시 패턴과 매우 유사합니다. 이 패턴은 다음 세 가지 측면에서 자주 사용됩니다.

  1. 객체에 대한 외부 액세스 차단 및 모니터링

  2. 효율성 감소 함수 또는 클래스의 복잡성

  3. 복잡한 작업 전에 작업을 확인하거나 필요한 리소스를 관리하세요

Proxy를 지원하는 브라우저 환경에서 Proxy는 전역 개체이므로 직접 사용할 수 있습니다. Proxy(target, handler)는 생성자이고, target은 프록시되는 객체이고, handler는 다양한 프록시 연산을 선언하는 객체이며, 궁극적으로 프록시 객체를 반환합니다. 외부 세계가 프록시 객체를 통해 대상 객체의 속성에 액세스할 때마다 핸들러 객체를 통과하게 됩니다. 이 프로세스에서 프록시 객체는 미들웨어와 매우 유사합니다. 그렇다면 프록시는 어떤 작업을 가로챌 수 있나요? 가장 일반적인 작업은 개체 속성 가져오기(읽기), 설정(수정) 및 기타 작업입니다. 차단할 수 있는 작업의 전체 목록을 보려면 여기를 클릭하세요. 또한 프록시 개체는 언제든지 모든 프록시 작업을 로그아웃할 수 있는 취소 메서드도 제공합니다. Proxy를 공식적으로 소개하기 전에 Reflect에 대해 어느 정도 이해하는 것이 좋습니다. 이는 ES6의 새로운 전역 개체이기도 하며 자세한 내용은 MDN Reflect를 참조하세요.

Basic

const target = { 
  name: 'Billy Bob',
  age: 15
};

const handler = { 
  get(target, key, proxy) {
    const today = new Date();
    console.log(`GET request made for ${key} at ${today}`);

    return Reflect.get(target, key, proxy);
  }
};

const proxy = new Proxy(target, handler);
proxy.name;
// => "GET request made for name at Thu Jul 21 2016 15:26:20 GMT+0800 (CST)"
// => "Billy Bob"

위 코드에서는 먼저 프록시 대상 객체 target을 정의한 다음 모든 프록시 작업을 포함하는 핸들러 객체를 선언한 다음 Proxy(대상, 핸들러)를 사용하여 프록시 객체 프록시를 생성합니다. 프록시를 사용하는 대상 속성은 핸들러에 의해 처리됩니다.

1. 검증 모듈 추출

간단한 유형 검증부터 시작해 보겠습니다. 이 예에서는 프록시를 사용하여 데이터 유형의 정확성을 확인하는 방법을 보여줍니다.

let numericDataStore = { 
  count: 0,
  amount: 1234,
  total: 14
};

numericDataStore = new Proxy(numericDataStore, { 
  set(target, key, value, proxy) {
    if (typeof value !== 'number') {
      throw Error("Properties in numericDataStore can only be numbers");
    }
    return Reflect.set(target, key, value, proxy);
  }
});

// 抛出错误,因为 "foo" 不是数值
numericDataStore.count = "foo";

// 赋值成功
numericDataStore.count = 333;

모든 객체를 직접 확인하려는 경우 개발 중입니다. 속성에 대한 유효성 검사기는 코드 구조를 빠르게 비대하게 만들 수 있습니다. 프록시를 사용하면 유효성 검사기를 핵심 논리에서 분리하여 자체 포함되게 만들 수 있습니다.

function createValidator(target, validator) { 
  return new Proxy(target, {
    _validator: validator,
    set(target, key, value, proxy) {
      if (target.hasOwnProperty(key)) {
        let validator = this._validator[key];
        if (!!validator(value)) {
          return Reflect.set(target, key, value, proxy);
        } else {
          throw Error(`Cannot set ${key} to ${value}. Invalid.`);
        }
      } else {
        throw Error(`${key} is not a valid property`)
      }
    }
  });
}

const personValidators = { 
  name(val) {
    return typeof val === 'string';
  },
  age(val) {
    return typeof age === 'number' && age > 18;
  }
}
class Person { 
  constructor(name, age) {
    this.name = name;
    this.age = age;
    return createValidator(this, personValidators);
  }
}

const bill = new Person('Bill', 25);

// 以下操作都会报错
bill.name = 0; 
bill.age = 'Bill'; 
bill.age = 15;

유효성 검사기와 기본 논리를 분리하면 내용을 무한히 확장할 수 있습니다. personValidators 관련 클래스나 기능에 직접적인 손상을 주지 않고 유효성 검사기를 사용합니다. 더 복잡하게 만들기 위해 프록시를 사용하여 함수가 올바른 유형과 숫자의 매개변수를 받는지 확인하기 위해 유형 검사를 시뮬레이션할 수도 있습니다.

let obj = { 
  pickyMethodOne: function(obj, str, num) { /* ... */ },
  pickyMethodTwo: function(num, obj) { /*... */ }
};

const argTypes = { 
  pickyMethodOne: ["object", "string", "number"],
  pickyMethodTwo: ["number", "object"]
};

obj = new Proxy(obj, { 
  get: function(target, key, proxy) {
    var value = target[key];
    return function(...args) {
      var checkArgs = argChecker(key, args, argTypes[key]);
      return Reflect.apply(value, target, args);
    };
  }
});

function argChecker(name, args, checkers) { 
  for (var idx = 0; idx < args.length; idx++) {
    var arg = args[idx];
    var type = checkers[idx];
    if (!arg || typeof arg !== type) {
      console.warn(`You are incorrectly implementing the signature of ${name}. Check param ${idx + 1}`);
    }
  }
}

obj.pickyMethodOne(); 
// > You are incorrectly implementing the signature of pickyMethodOne. Check param 1
// > You are incorrectly implementing the signature of pickyMethodOne. Check param 2
// > You are incorrectly implementing the signature of pickyMethodOne. Check param 3

obj.pickyMethodTwo("wopdopadoo", {}); 
// > You are incorrectly implementing the signature of pickyMethodTwo. Check param 1

// No warnings logged
obj.pickyMethodOne({}, "a little string", 123); 
obj.pickyMethodOne(123, {});

2. 개인 속성

JavaScript나 다른 언어에서는 일반적으로 모든 사람이 사용합니다. 변수 이름 앞에 밑줄 _을 추가하여 이것이 개인 속성(실제로는 개인이 아님)임을 나타내지만, 누구도 실제로 이 속성에 액세스하거나 수정하지 않을 것이라고 보장할 수는 없습니다. 아래 코드에서는 API 개체 내에서 메서드 호출을 용이하게 하기 위해 비공개 apiKey를 선언하지만 외부에서 API에 액세스할 수 있기를 원하지 않습니다._apiKey:

var api = { 
  _apiKey: &#39;123abc456def&#39;,
  /* mock methods that use this._apiKey */
  getUsers: function(){}, 
  getUser: function(userId){}, 
  setUser: function(userId, config){}
};

// logs &#39;123abc456def&#39;;
console.log("An apiKey we want to keep private", api._apiKey);

// get and mutate _apiKeys as desired
var apiKey = api._apiKey; 
api._apiKey = &#39;987654321&#39;;

분명히 규칙은 구속력이 없습니다. ES6 프록시를 사용하면 실제 개인 변수를 구현할 수 있습니다. 다음은 서로 다른 읽기 방법에 대한 두 가지 다른 민영화 방법을 보여줍니다. 첫 번째 방법은 set/get을 사용하여 읽기 및 쓰기 요청을 가로채고 정의되지 않은 상태를 반환하는 것입니다:

let api = { 
  _apiKey: &#39;123abc456def&#39;,
  getUsers: function(){ }, 
  getUser: function(userId){ }, 
  setUser: function(userId, config){ }
};

const RESTRICTED = [&#39;_apiKey&#39;];
api = new Proxy(api, { 
  get(target, key, proxy) {
    if(RESTRICTED.indexOf(key) > -1) {
      throw Error(`${key} is restricted. Please see api documentation for further info.`);
    }
    return Reflect.get(target, key, proxy);
  },
  set(target, key, value, proxy) {
    if(RESTRICTED.indexOf(key) > -1) {
      throw Error(`${key} is restricted. Please see api documentation for further info.`);
    }
    return Reflect.get(target, key, value, proxy);
  }
});

// 以下操作都会抛出错误
console.log(api._apiKey);
api._apiKey = &#39;987654321&#39;;

두 번째 방법은 작업에서 가로채기를 사용하는 것입니다:

var api = { 
  _apiKey: &#39;123abc456def&#39;,
  getUsers: function(){ }, 
  getUser: function(userId){ }, 
  setUser: function(userId, config){ }
};

const RESTRICTED = [&#39;_apiKey&#39;];
api = new Proxy(api, { 
  has(target, key) {
    return (RESTRICTED.indexOf(key) > -1) ?
      false :
      Reflect.has(target, key);
  }
});

// these log false, and `for in` iterators will ignore _apiKey
console.log("_apiKey" in api);

for (var key in api) { 
  if (api.hasOwnProperty(key) && key === "_apiKey") {
    console.log("This will never be logged because the proxy obscures _apiKey...")
  }
}

3. 액세스 로그

자주 통화하는 경우 , 천천히 실행하거나 실행 환경 리소스를 많이 차지하는 속성이나 인터페이스의 경우 개발자는 사용량이나 성능을 기록하기를 원할 것입니다. 이때 프록시를 사용하여 미들웨어 역할을 하고 로그 기능을 쉽게 구현할 수 있습니다.

let api = { 
  _apiKey: &#39;123abc456def&#39;,
  getUsers: function() { /* ... */ },
  getUser: function(userId) { /* ... */ },
  setUser: function(userId, config) { /* ... */ }
};

function logMethodAsync(timestamp, method) { 
  setTimeout(function() {
    console.log(`${timestamp} - Logging ${method} request asynchronously.`);
  }, 0)
}

api = new Proxy(api, { 
  get: function(target, key, proxy) {
    var value = target[key];
    return function(...arguments) {
      logMethodAsync(new Date(), key);
      return Reflect.apply(value, target, arguments);
    };
  }
});

api.getUsers();

4. 조기 경고 및 차단

다른 개발자가 noDelete 속성을 삭제하는 것을 원하지 않고, oldMethod를 호출하는 개발자에게 이 메서드가 더 이상 사용되지 않는다는 사실을 알리거나 개발자에게 doNotChange 속성을 수정하지 말라고 알리기를 원한다고 가정해 보세요.

let dataStore = { 
  noDelete: 1235,
  oldMethod: function() {/*...*/ },
  doNotChange: "tried and true"
};

const NODELETE = [&#39;noDelete&#39;]; 
const NOCHANGE = [&#39;doNotChange&#39;];
const DEPRECATED = [&#39;oldMethod&#39;]; 

dataStore = new Proxy(dataStore, { 
  set(target, key, value, proxy) {
    if (NOCHANGE.includes(key)) {
      throw Error(`Error! ${key} is immutable.`);
    }
    return Reflect.set(target, key, value, proxy);
  },
  deleteProperty(target, key) {
    if (NODELETE.includes(key)) {
      throw Error(`Error! ${key} cannot be deleted.`);
    }
    return Reflect.deleteProperty(target, key);

  },
  get(target, key, proxy) {
    if (DEPRECATED.includes(key)) {
      console.warn(`Warning! ${key} is deprecated.`);
    }
    var val = target[key];

    return typeof val === &#39;function&#39; ?
      function(...args) {
        Reflect.apply(target[key], target, args);
      } :
      val;
  }
});

// these will throw errors or log warnings, respectively
dataStore.doNotChange = "foo"; 
delete dataStore.noDelete; 
dataStore.oldMethod();

5. 필터링 작업

이 때 대용량 파일을 전송하는 등 일부 작업은 많은 리소스를 차지합니다. 새로운 요청에 응답할 필요는 없습니다(절대적인 것은 아님). 이때 Proxy를 사용하여 요청의 특성을 감지하고 응답이 필요하지 않은 요청과 특성에 따라 응답이 필요한 요청을 필터링할 수 있습니다. 다음 코드는 단순히 기능 필터링 방법을 보여줍니다. 완전한 코드는 아닙니다.

let obj = { 
  getGiantFile: function(fileId) {/*...*/ }
};

obj = new Proxy(obj, { 
  get(target, key, proxy) {
    return function(...args) {
      const id = args[0];
      let isEnroute = checkEnroute(id);
      let isDownloading = checkStatus(id);   
      let cached = getCached(id);

      if (isEnroute || isDownloading) {
        return false;
      }
      if (cached) {
        return cached;
      }
      return Reflect.apply(target[key], target, args);
    }
  }
});

6 프록시 중단은 언제든지 대상에 대한 프록시 취소를 지원합니다. 이 작업은 데이터나 인터페이스에 대한 액세스를 완전히 차단하는 데 자주 사용됩니다. 다음 예에서는 Proxy.revocable 메서드를 사용하여 취소 가능한 프록시가 있는 프록시 개체를 만듭니다.

let sensitiveData = { username: &#39;devbryce&#39; };
const {sensitiveData, revokeAccess} = Proxy.revocable(sensitiveData, handler);
function handleSuspectedHack(){ 
  revokeAccess();
}

// logs &#39;devbryce&#39;
console.log(sensitiveData.username);
handleSuspectedHack();
// TypeError: Revoked
console.log(sensitiveData.username);

Decorator

ES7에 구현된 데코레이터는 디자인 패턴의 데코레이터 패턴과 동일합니다. Proxy와 Decorator의 사용 시나리오를 간단히 구분하면 다음과 같이 요약할 수 있습니다. Proxy의 핵심 기능은 Proxy 내부에 대한 외부 접근을 제어하는 ​​것이고, Decorator의 핵심 기능은 Proxy의 기능을 강화하는 것입니다. 데코레이터. 핵심 사용 시나리오가 구별되는 한, 이 기사에서는 Proxy를 사용하여 구현하지만 개발자는 프로젝트 요구 사항, 팀 사양 및 자신의 선호도에 따라 무료로 구현할 수 있습니다. 선택.

위 내용은 모두를 위해 제가 정리한 내용입니다. 앞으로 모든 사람에게 도움이 되기를 바랍니다.

관련 기사:

Vue를 통해 공개 CSS 파일을 소개하는 방법

Vue에서 ajax를 사용하는 방법은 무엇인가요?

vue.js에서 데이터 분배 슬롯을 구현하는 방법

위 내용은 ES6에서 프록시를 사용하기 위한 지침의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.