JavaScript: uso del espacio de cierre para crear miembros privados reales

JavaScript: uso del espacio de cierre para crear miembros privados reales

recientemente desarrollé Conector de datos de nube angularque permite a los desarrolladores de Angular usar datos en la nube, específicamente Servicio móvil Azure, utilizando estándares web como DB indexado. Estaba tratando de crear una forma para que los desarrolladores de JavaScript incrustaran miembros privados en un objeto. Mi técnica para este caso específico es usar lo que llamo “espacio cerrado”. En este tutorial, quiero compartir contigo cómo usar esto para tus propios proyectos y cómo se ven afectados el rendimiento y la memoria para los principales navegadores.
Pero antes de adentrarnos en esto, permítanme compartir por qué es posible que necesite miembros privados, así como una forma alternativa de «simular» miembros privados.

Por qué usar miembros privados?

Cuando crea un objeto con JavaScript, puede definir miembros de valor. Si desea controlar el acceso de lectura/escritura en ellos, necesita accesores que se puedan definir así:

var entity = {};

entity._property = "hello world";
Object.defineProperty(entity, "property", {
    get: function () { return this._property; },
    set: function (value) {
        this._property = value;
    },
    enumerable: true,
    configurable: true
});

Al hacer esto, tiene control total sobre las operaciones de lectura y escritura. El problema es que el _propiedad el miembro aún es accesible y se puede modificar directamente.

Esta es exactamente la razón por la que necesita una forma más robusta de definir miembros privados a los que solo las funciones del objeto pueden acceder.

Uso del espacio de cierre

La solución es utilizar el espacio de cierre. Este espacio de memoria lo crea el navegador cada vez que una función interna tiene acceso a variables desde el alcance de una función externa. Esto puede ser complicado a veces, pero para nuestro tema es una solución perfecta.

Así que modifiquemos el código anterior para usar esta función:

var createProperty = function (obj, prop, currentValue) {
    Object.defineProperty(obj, prop, {
        get: function () { return currentValue; },
        set: function (value) {
            currentValue = value;
        },
        enumerable: true,
        configurable: true
    });
}

var entity = {};

var myVar = "hello world";
createProperty(entity, "property", myVar);

En este ejemplo, el crearPropiedad función tiene un valor actual variable que pueden ver las funciones get y set. Esta variable se guardará en el espacio de cierre de las funciones get y set. Solo estas dos funciones ahora pueden ver y actualizar el valor actual ¡variable! ¡Misión cumplida!

La única advertencia que tenemos aquí es que el valor de origen (miVar) todavía es accesible. Así que aquí viene otra versión para una protección aún más robusta:

var createProperty = function (obj, prop) {
    var currentValue = obj[prop];
    Object.defineProperty(obj, prop, {
        get: function () { return currentValue; },
        set: function (value) {
            currentValue = value;
        },
        enumerable: true,
        configurable: true
    });
}

var entity = {
    property: "hello world"
};

createProperty(entity, "property");

Con este método, incluso el valor de origen se destruye. ¡Así que misión totalmente cumplida!

Consideración de rendimiento

Ahora echemos un vistazo al rendimiento.

Obviamente, los espacios de cierre o incluso las propiedades son más lentos y costosos que una simple variable. Es por eso que este artículo se centra más en la diferencia entre la forma regular y la técnica del espacio de cierre.

Para confirmar que el enfoque del espacio de cierre no es demasiado costoso en comparación con la forma estándar, escribí este pequeño punto de referencia:

<!DOCTYPE html>
  <html xmlns="http://www.w3.org/1999/xhtml">
  <head>
  <title></title>
  </head>
  <style>
  html {
  font-family: "Helvetica Neue", Helvetica;
  }
  </style>
  <body>
  <div id="results">Computing...</div>
  <script>
  var results = document.getElementById("results");
  var sampleSize = 1000000;
  var opCounts = 1000000;
  
 var entities = [];

 setTimeout(function () {
  // Creating entities
  for (var index = 0; index < sampleSize; index++) {
  entities.push({
  property: "hello world (" + index + ")"
  });
  }

 // Random reads
  var start = new Date().getTime();
  for (index = 0; index < opCounts; index++) {
  var position = Math.floor(Math.random() * entities.length);
  var temp = entities[position].property;
  }
  var end = new Date().getTime();

 results.innerHTML = "<strong>Results:</strong><br>Using member access: <strong>" + (end - start) + "</strong> ms";
  }, 0);

 setTimeout(function () {
  // Closure space =======================================
  var createProperty = function (obj, prop, currentValue) {
  Object.defineProperty(obj, prop, {
  get: function () { return currentValue; },
  set: function (value) {
  currentValue = value;
  },
  enumerable: true,
  configurable: true
  });
  }
  // Adding property and using closure space to save private value
  for (var index = 0; index < sampleSize; index++) {
  var entity = entities[index];

 var currentValue = entity.property;
  createProperty(entity, "property", currentValue);
  }

 // Random reads
  var start = new Date().getTime();
  for (index = 0; index < opCounts; index++) {
  var position = Math.floor(Math.random() * entities.length);
  var temp = entities[position].property;
  }
  var end = new Date().getTime();

 results.innerHTML += "<br>Using closure space: <strong>" + (end - start) + "</strong> ms";
  }, 0);

 setTimeout(function () {
  // Using local member =======================================
  // Adding property and using local member to save private value
  for (var index = 0; index < sampleSize; index++) {
  var entity = entities[index];

 entity._property = entity.property;
  Object.defineProperty(entity, "property", {
  get: function () { return this._property; },
  set: function (value) {
  this._property = value;
  },
  enumerable: true,
  configurable: true
  });
  }

 // Random reads
  var start = new Date().getTime();
  for (index = 0; index < opCounts; index++) {
  var position = Math.floor(Math.random() * entities.length);
  var temp = entities[position].property;
  }
  var end = new Date().getTime();

 results.innerHTML += "<br>Using local member: <strong>" + (end - start) + "</strong> ms";
  }, 0);

 </script>
  </body>
  </html>

Creo 1 millón de objetos, todos con un miembro de propiedad. Luego hago tres pruebas:

  • Haz 1 millón de accesos aleatorios a la propiedad
  • Haz 1 millón de accesos aleatorios a la versión de «espacio cerrado»
  • Realice 1 millón de accesos aleatorios a la versión regular get/set

Aquí hay una tabla y un gráfico sobre el resultado:


Podemos ver que la versión de espacio de cierre siempre es más rápida que la versión normal y dependiendo del navegador, puede ser una optimización realmente impresionante.

El rendimiento de Chrome es menor de lo que esperaba. Puede haber un error, así que para estar seguro, me comuniqué con el equipo de Google para averiguar qué está sucediendo aquí. Además, si desea probar cómo funciona esto en Borde de Microsoft – El nuevo navegador de Microsoft que se enviará por defecto con Windows 10 – puedes descargarlo aquí.

Sin embargo, si miramos de cerca, podemos encontrar que usar un espacio de cierre o incluso una propiedad puede ser diez veces más lento que el acceso directo a un miembro. Así que ten cuidado y úsalo sabiamente.

Huella de memoria

También tenemos que comprobar si esta técnica no consume demasiada memoria. Para evaluar la memoria, escribí estos tres pequeños fragmentos de código:

Código de referencia

var sampleSize = 1000000;

var entities = [];

// Creating entities
for (var index = 0; index 

Regular Way

var sampleSize = 1000000;

var entities = [];

// Adding property and using local member to save private value
for (var index = 0; index 

Closure Space Version

var sampleSize = 1000000;

var entities = [];

var createProperty = function (obj, prop, currentValue) {
    Object.defineProperty(obj, prop, {
        get: function () { return currentValue; },
        set: function (value) {
            currentValue = value;
        },
        enumerable: true,
        configurable: true
    });
}

// Adding property and using closure space to save private value
for (var index = 0; index 

Then I ran all these three codes and I launched the embedded memory profiler (Example here using F12 tools):

Here are the results I got on my computer:

Between closure space and regular way, only Chrome has slightly better results for closure space version. IE11 and Firefox use a bit more memory but the browsers are relatively comparable – users probably won’t notice a difference across the modern browsers.

More Hands-On with JavaScript

It might surprise you a bit, but Microsoft has a bunch of free learning on many open source JavaScript topics and we’re on a mission to create a lot more with Microsoft Edge coming. Check out my own:

O la serie de aprendizaje de nuestro equipo:

Y algunas herramientas gratuitas: Comunidad de Visual Studio, Prueba de Azurey herramientas de prueba de navegador cruzado para Mac, Linux o Windows.

Conclusión

Como puede ver, las propiedades del espacio de cierre pueden ser un gran manera de crear datos realmente privados. Es posible que tenga que lidiar con un pequeño aumento en el consumo de memoria, pero desde mi punto de vista, esto es bastante razonable (y a ese precio puede tener una gran mejora en el rendimiento con respecto al uso normal).

Siéntete libre de enviarme un ping en Twitter si quieres discutir sobre este artículo: @deltakosh

Y por cierto, si quieres probarlo por ti mismo, encuentra todo el código utilizado aquí. Hay un buen «cómo hacer» en Azure Mobile Services aquí.

Este artículo es parte de la serie de tecnología de desarrollo web de Microsoft. Estamos emocionados de compartir Borde de Microsoft y es nuevo motor de renderizado contigo. Obtenga máquinas virtuales gratuitas o realice pruebas de forma remota en su dispositivo Mac, iOS, Android o Windows @ moderno.ES.

(dpe)


#JavaScript #uso #del #espacio #cierre #para #crear #miembros #privados #reales

Publicaciones Similares

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *