Maison  >  Article  >  interface Web  >  Instructions d'utilisation du proxy dans ES6

Instructions d'utilisation du proxy dans ES6

亚连
亚连original
2018-06-13 10:57:351545parcourir

Cet article présente principalement les scénarios d'utilisation d'ES6 Proxy. Maintenant, je le partage avec vous et vous donne une référence.

Des fonctionnalités telles que les fonctions de flèche, la déstructuration de tableaux et les paramètres de repos dans ES6 ont été largement diffusées dès leur implémentation. Cependant, des fonctionnalités telles que Proxy sont rarement utilisées par les développeurs. à la compatibilité des navigateurs. D'autre part, afin de profiter de ces fonctionnalités, les développeurs doivent comprendre en profondeur leurs scénarios d'utilisation. Personnellement, j'aime beaucoup le proxy d'ES6 car il nous permet de contrôler l'accès externe aux objets de manière concise et facile à comprendre. Dans ce qui suit, je présenterai d'abord comment utiliser Proxy, puis expliquerai les scénarios d'utilisation de Proxy en citant des exemples spécifiques.

Proxy, comme son nom l'indique, sa fonction est très similaire au modèle de proxy dans les modèles de conception. Ce modèle est couramment utilisé sous trois aspects :

  1. Interception et surveillance. des transactions externes Accès aux objets

  2. Réduire la complexité des fonctions ou des classes

  3. Vérifier les opérations ou gérer les ressources requises avant les opérations complexes

Dans un environnement de navigateur prenant en charge Proxy, Proxy est un objet global et peut être utilisé directement. Proxy(target, handler) est un constructeur, target est l'objet proxy, handler est un objet qui déclare diverses opérations de proxy et renvoie finalement un objet proxy. Chaque fois que le monde extérieur accède aux propriétés de l'objet cible via l'objet proxy, il passera par l'objet gestionnaire. À partir de ce processus, l'objet proxy est très similaire au middleware. Alors, quelles opérations Proxy peut-il intercepter ? Les opérations les plus courantes sont obtenir (lire) et définir (modifier) ​​les propriétés des objets. Pour une liste complète des opérations interceptables, veuillez cliquer ici. De plus, l'objet Proxy fournit également une méthode de révocation pour déconnecter toutes les opérations proxy à tout moment. Avant de présenter officiellement Proxy, il est recommandé d'avoir une certaine compréhension de Reflect. Il s'agit également d'un nouvel objet global dans ES6. Pour des informations détaillées, veuillez vous référer à MDN Reflect.

Basique

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"

Dans le code ci-dessus, nous définissons d'abord un objet cible proxy, puis déclarons l'objet gestionnaire qui contient toutes les opérations proxy, puis utilisons Proxy (cible, gestionnaire) crée un proxy d'objet proxy. Après cela, tous les accès à l'attribut cible à l'aide d'un proxy seront traités par le gestionnaire.

1. Extrayez le module de vérification

Commençons par une simple vérification de type Cet exemple montre comment utiliser Proxy pour garantir l'exactitude des types de données :

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;

Si vous souhaitez développer directement un validateur pour toutes les propriétés d'un objet, la structure du code peut rapidement devenir gonflée. À l'aide de Proxy, vous pouvez séparer le validateur de la logique de base :

.
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;

En séparant le validateur et la logique principale, vous pouvez étendre à l'infini le contenu du validateur personValidators sans causer de dommages directs aux classes ou fonctions associées. Pour compliquer les choses, nous pouvons également utiliser Proxy pour simuler la vérification de type et vérifier si la fonction reçoit le type et le nombre de paramètres corrects :

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, {});

Propriétés privées

En JavaScript ou dans d'autres langages, il est d'usage d'ajouter un trait de soulignement _ avant le nom de la variable pour indiquer qu'il s'agit d'une propriété privée (pas vraiment privée), mais nous ne pouvons garantir que personne n'y accédera ou ne la modifiera. Dans le code suivant, nous déclarons une apiKey privée pour faciliter les appels de méthode au sein de l'objet api, mais nous ne voulons pas pouvoir accéder à l'api depuis l'extérieur._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;;
Évidemment, il y a pas de convention contraignante. En utilisant ES6 Proxy, nous pouvons implémenter de vraies variables privées. Ce qui suit montre deux méthodes de privatisation différentes pour différentes méthodes de lecture. La première méthode consiste à utiliser set/get pour intercepter les requêtes de lecture et d'écriture et renvoyer undefined :

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;;
La deuxième méthode consiste à utiliser has to intercept dans les opérations :

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 . Journal d'accès

Pour les propriétés ou interfaces qui sont fréquemment appelées, qui s'exécutent lentement ou qui occupent beaucoup de ressources de l'environnement d'exécution, les développeurs voudront enregistrer leur utilisation ou leurs performances à ce moment-là. , Le proxy peut être utilisé Agissant comme middleware, vous pouvez facilement implémenter la fonction de journalisation :

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. Alerte précoce et interception

Supposons que vous ne le vouliez pas. d'autres développeurs pour supprimer l'attribut noDelete, et vous souhaitez également faire savoir au développeur qui appelle oldMethod que cette méthode a été abandonnée, ou dire au développeur de ne pas modifier l'attribut doNotChange, vous pouvez alors utiliser Proxy pour y parvenir :

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. Opération de filtrage

Certaines opérations occuperont beaucoup de ressources, comme le transfert de fichiers volumineux à ce moment-là, si le fichier est déjà envoyé en morceaux. , il n'est pas nécessaire de répondre aux nouvelles demandes (non absolues). Utilisez Proxy pour détecter les caractéristiques de la demande et filtrer celles qui ne nécessitent pas de réponse et celles qui nécessitent une réponse en fonction des caractéristiques. Le code suivant montre simplement comment filtrer les fonctionnalités. Ce n'est pas un code complet, je pense que tout le monde en comprendra la beauté :

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 prend en charge l'annulation du proxy de la cible à tout moment. Cette opération est souvent utilisée pour fermer complètement l'accès aux données ou aux interfaces. Dans l'exemple suivant, nous utilisons la méthode Proxy.revocable pour créer un objet proxy pour un proxy révocable :

Decorator

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);

Le Decorator implémenté dans ES7 est équivalent au modèle de décorateur dans le modèle de conception. Si nous distinguons simplement les scénarios d'utilisation de Proxy et Decorator, cela peut être résumé comme suit : La fonction principale de Proxy est de contrôler l'accès externe à l'intérieur du proxy, et la fonction principale de Decorator est d'améliorer la fonction du proxy. décorateur. Tant que leurs scénarios d'utilisation principaux sont distingués, des fonctions telles que les journaux d'accès, bien que cet article utilise Proxy pour les implémenter, peuvent également être implémentées à l'aide de Decorator. Les développeurs peuvent les implémenter gratuitement en fonction des besoins du projet, des spécifications de l'équipe et de leurs propres préférences. choix.

J'ai compilé ce qui précède pour vous, j'espère que cela vous sera utile à l'avenir.

Articles connexes :

Comment introduire des fichiers CSS publics via Vue

Quelles sont les méthodes d'utilisation d'ajax dans Vue ?

Comment implémenter un slot de distribution de données dans vue.js

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn