Seleccionar página

métodos de acceso

Es posible definir dos métodos para responder a las acciones de lectura y escritura de una propiedad y de esta forma podemos tener el control sobre los datos que vamos a ofrecer y almacenar en las propiedades.

En el caso de class utilizamos la sintaxis get nombre_propiedad(){} y set nombre_propiedad(v){} para definir los dos métodos. El método set siempre debe recibir un parámetro con el nuevo valor que se quiere asignar.

Si utilizamos function podemos recurrir a Object.defineProperty() y Object.defineProperties() para definir una propiedad en el prototipo en cuyo descriptor definimos sus métodos get y set.

classfunction
let data = true;

class C {
  get x () {
    return data;
  }
  set x ( v ) {
    data = v;
  }
}

const c = new C();
console.assert( c.x === true );
c.x = false;
console.assert( c.x === false );
let data = true;

function C () {
}
Object.defineProperty( C.prototype, 'x', {
  get () {
    return data;
  },
  set ( v ) {
    data = v;
  }
} );

const c = new C();
console.assert( c.x === true );
c.x = false;
console.assert( c.x === false );

    En todos los casos es posible definir sólo un get para propiedades de sólo lectura. En ese caso, cuando se intente modificar la propiedad no se lanza un error, pero se ignora el intento de actualización. En la práctica también es posible definir sólo set, pero en general carece de mucho sentido sin un get.

    prototipo y enumeración

    Por defecto, las métodos de acceso, como el resto de métodos, se definen en el prototipo y por lo tanto no son enumerables en el objeto (recordemos que todos los elementos del prototipo, aunque sean enumerables en él, no son enumerables en el objeto). Si queremos que sean enumerables tenemos que definirlos en el objeto con Object.defineProperty() o Object.defineProperties().

    classfunction
    let data = true;
    
    class C {
      constructor() {
        Object.defineProperty(this, 'x', {
          get () {
            return data;
          },
          set ( v ) {
            data = v;
          },
          enumerable: true
        });
      }
    }
    
    const c = new C();
    console.assert( c.propertyIsEnumerable('x') );
    
    let data = true;
    
    function C () {
      Object.defineProperty( this, 'x', {
        get () {
          return data;
        },
        set ( v ) {
          data = v;
        },
        enumerable : true
      } );
    }
    
    const c = new C();
    console.assert( c.propertyIsEnumerable( 'x' ) );
    

    Esta es la estructura de un objeto con un método de acceso ubicado en el propio objeto, no en su prototipo:

      datos privados

      Cuando creamos unos métodos de acceso es que queremos gestionar la forma en la que se accede o se actualiza la información, por eso hemos incluido aquí este apartado sobre datos privados. Es posible que necesitemos datos privados que no tengan métodos de acceso, los mecanismos de ocultación serán los mismos.

      Aunque en una próxima especificación se permitirá definir miembros privados en las clases, de momento no disponemos de un mecanismo para ocultar contenido en un objeto y deberemos almacenarlos fuera del objeto, ya que si los guardamos en el objeto estos quedan expuestos, aunque fuera con un objeto Symbol().

      Uso de WeakMap

      Hay varias aproximaciones para dar solución a esta necesidad de protección de cierto datos, entre ellas la que ha mostrado ser eficiente y razonablemente fácil de utilizar es el uso de un WeakMap. Este objeto es un almacén clave-valor donde la clave debe ser un objeto. De esta forma podemos utilizar el objeto como clave y guardar ahí sus valores privados.

      classfunction
      const data = new WeakMap();
      
      class C {
        constructor () {
          data.set( this, {x : true} );
        }
        get x () {
          return data.get( this ).x;
        }
        set x ( v ) {
          data.get( this ).x = v;
        }
      }
      
      const c = new C();
      console.assert( c.x === true );
      c.x = false;
      console.assert( c.x === false );
      
        const data = new WeakMap();
        
        function C () {
          data.set( this, {x : true} );
        }
        Object.defineProperty( C.prototype, 'x', {
          get () {
            return data.get( this ).x;
          },
          set ( v ) {
            data.get( this ).x = v;
          }
        } );
        
        const c = new C();
        console.assert( c.x === true );
        c.x = false;
        console.assert( c.x === false );
      

      El objeto de tipo WeakMap deberemos mantenerlo dentro de un módulo o una clausura, sin que otros programas puedan acceder a él, o estaremos haciendo de nuevo públicos los datos privados de nuestro objeto.

      Puedes conocer un poco más sobre este tema en Encapsulación de las clases de ES6.

      métodos Índicemiembros estáticos