Частные объекты в JavaScript ES6 классы

Можно ли создавать частные свойства в классах ES6?

Вот пример. Как я могу предотвратить доступ к instance.property?

class Something {
  constructor(){
    this.property = "test";
  }
}

var instance = new Something();
console.log(instance.property); //=> "test"

Ответ 1

Частные поля внедряются в стандарте ECMA. Вы можете начать использовать их сегодня с babel 7 и предустановкой стадии 3. См. пример Babel REPL.

class Something {
  #property;

  constructor(){
    this.#property = "test";
  }
}

const instance = new Something();
console.log(instance.property); //=> undefined

Ответ 2

Короткий ответ, нет, нет встроенной поддержки частных свойств с ES6-классами.

Но вы могли бы подражать этому поведению, не добавляя новые свойства к объекту, но сохраняя их внутри конструктора классов и используя геттеры и сеттеры для достижения скрытых свойств. Обратите внимание, что геттеры и сеттеры получают переопределение в каждом новом экземпляре класса.

ES6

class Person {
    constructor(name) {
        var _name = name
        this.setName = function(name) { _name = name; }
        this.getName = function() { return _name; }
    }
}

ES5

function Person(name) {
    var _name = name
    this.setName = function(name) { _name = name; }
    this.getName = function() { return _name; }
}

Ответ 3

Чтобы расширить ответ на @loganfsmyth:

Единственные действительно частные данные в JavaScript все еще ограничены переменными. Вы не можете иметь приватные свойства в смысле свойств, которые доступны внутренне, так же, как и общедоступные свойства, но вы можете использовать скоординированные переменные для хранения частных данных.

Скользящие переменные

Подходом здесь является использование области функции конструктора, которая является частной, для хранения частных данных. Чтобы методы имели доступ к этим личным данным, они также должны быть созданы внутри конструктора, что означает, что вы воссоздаете их с каждым экземпляром. Это штраф за производительность и память, но некоторые считают, что штраф является приемлемым. Штраф можно избежать для методов, которым не нужен доступ к личным данным, добавив их в прототип, как обычно.

Пример:

function Person(name) {
  let age = 20; // this is private
  this.name = name; // this is public

  this.greet = function () {
    // here we can access both name and age
    console.log(`name: ${this.name}, age: ${age}`);
  };
}

let joe = new Person('Joe');
joe.greet();

// here we can access name but not age

Scope WeakMap

A WeakMap можно использовать, чтобы избежать предыдущей производительности подхода и штрафа за память. WeakMaps связывает данные с объектами (здесь, экземпляры) таким образом, что к ним можно получить доступ только с помощью этой WeakMap. Таким образом, мы используем метод переменных области для создания частной WeakMap, а затем используем эту WeakMap для извлечения частных данных, связанных с this. Это быстрее, чем метод переменных с областью, потому что все ваши экземпляры могут совместно использовать одну WeakMap, поэтому вам не нужно воссоздавать методы, чтобы они могли получить доступ к своим собственным WeakMaps.

Пример:

let Person = (function () {
  let privateProps = new WeakMap();

  class Person {
    constructor(name) {
      this.name = name; // this is public
      privateProps.set(this, {age: 20}); // this is private
    }

    greet() {
      // Here we can access both name and age
      console.log(`name: ${this.name}, age: ${privateProps.get(this).age}`);
    }
  }

  return Person;
})();

let joe = new Person('Joe');
joe.greet();

// here we can access joe name but not age

В этом примере используется Object для использования одного WeakMap для нескольких частных свойств; вы также можете использовать несколько WeakMaps и использовать их как age.set(this, 20) или написать небольшую обертку и использовать ее по-другому, например privateProps.set(this, 'age', 0).

Конфиденциальность этого подхода теоретически может быть нарушена путем вмешательства в глобальный объект WeakMap. Тем не менее, все JavaScript можно сломать искаженными глобалами. Наш код уже построен на предположении, что этого не происходит.

(Этот метод также можно выполнить с помощью Map, но WeakMap лучше, потому что Map создаст утечки памяти, если вы не будете очень осторожны, и для этих целей они не отличаются друг от друга.)

Полуответ: Обозначенные символы

Символ - это тип примитивного значения, который может служить именем свойства. Вы можете использовать метод переменной scoped для создания частного символа, а затем хранить личные данные в this[mySymbol].

Конфиденциальность этого метода может быть нарушена с помощью Object.getOwnPropertySymbols, но это несколько неудобно делать.

Пример:

let Person = (function () {
  let ageKey = Symbol();

  class Person {
    constructor(name) {
      this.name = name; // this is public
      this[ageKey] = 20; // this is intended to be private
    }

    greet() {
      // Here we can access both name and age
      console.log(`name: ${this.name}, age: ${this[ageKey]}`);
    }
  }

  return Person;
})();

let joe = new Person('Joe');
joe.greet();

// Here we can access joe name and, with a little effort, age. ageKey is
// not in scope, but we can obtain it by listing all Symbol properties on
// joe with `Object.getOwnPropertySymbols(joe)`.

Полу-ответ: подчеркивание

Старый по умолчанию, просто используйте общедоступное свойство с префиксом подчеркивания. Хотя это и не является частной собственностью, эта конвенция достаточно распространена, что она хорошо говорит о том, что читатели должны относиться к собственности как к частной, которая часто выполняет свою работу. В обмен на этот провал мы получаем подход, который легче читать, проще печатать и быстрее.

Пример:

class Person {
  constructor(name) {
    this.name = name; // this is public
    this._age = 20; // this is intended to be private
  }

  greet() {
    // Here we can access both name and age
    console.log(`name: ${this.name}, age: ${this._age}`);
  }
}

let joe = new Person('Joe');
joe.greet();

// Here we can access both joe name and age. But we know we aren't
// supposed to access his age, which just might stop us.

Заключение

В ES2017 по-прежнему нет идеального способа делать частные свойства. Различные подходы имеют плюсы и минусы. Скользящие переменные являются действительно частными; Scope WeakMaps являются очень частными и более практичными, чем облачные переменные; Обозначенные символы являются достаточно частными и разумно практичными; подчеркивания часто достаточно приватны и очень практичны.

Ответ 4

Обновление. Предложение с более сильным синтаксисом находится на своем пути. Взносы приветствуются.


Да, есть - для облачного доступа к объектам - ES6 представляет Symbol s.

Символы уникальны, вы не можете получить доступ к одному извне, кроме как с отражением (например, privates в Java/С#), но любой, кто имеет доступ к символу внутри, может использовать его для доступа к ключам:

var property = Symbol();
class Something {
    constructor(){
        this[property] = "test";
    }
}

var instance = new Something();

console.log(instance.property); //=> undefined, can only access with access to the Symbol

Ответ 5

Ответ: "Нет". Но вы можете создать частный доступ к следующим свойствам:

  • Использовать модули. Все в модуле является частным, если оно не опубликовано с помощью ключевого слова export.
  • Внутри модулей используйте функцию закрытия: http://www.kirupa.com/html5/closures_in_javascript.htm

(Предложение о том, что Символы могут использоваться для обеспечения конфиденциальности, было истинным в более ранней версии спецификации ES6, но это уже не так: https://mail.mozilla.org/pipermail/es-discuss/2014-January/035604.html и fooobar.com/questions/28094/.... Более подробную информацию о символах и конфиденциальности см. в разделе https://curiosity-driven.org/private-properties-in-javascript)

Ответ 6

Единственный способ получить истинную конфиденциальность в JS - это определить область видимости, поэтому нет способа иметь свойство, являющееся членом this, которое будет доступно только внутри компонента. Лучший способ хранить действительно частные данные в ES6 - с помощью WeakMap.

const privateProp1 = new WeakMap();
const privateProp2 = new WeakMap();

class SomeClass {
  constructor() {
    privateProp1.set(this, "I am Private1");
    privateProp2.set(this, "I am Private2");

    this.publicVar = "I am public";
    this.publicMethod = () => {
      console.log(privateProp1.get(this), privateProp2.get(this))
    };        
  }

  printPrivate() {
    console.log(privateProp1.get(this));
  }
}

Очевидно, что это, вероятно, медленный и определенно уродливый, но он обеспечивает конфиденциальность.

Имейте в виду, что ДАЖЕ ЭТО НЕ СОВЕРШЕННО, потому что Javascript настолько динамичен. Кто-то все еще мог

var oldSet = WeakMap.prototype.set;
WeakMap.prototype.set = function(key, value){
    // Store 'this', 'key', and 'value'
    return oldSet.call(this, key, value);
};

чтобы поймать значения по мере их хранения, поэтому, если вы хотите быть более осторожными, вам нужно будет привязать локальную ссылку к .set и .get, чтобы использовать явно, вместо того, чтобы полагаться на переопределяемый прототип.

const {set: WMSet, get: WMGet} = WeakMap.prototype;

const privateProp1 = new WeakMap();
const privateProp2 = new WeakMap();

class SomeClass {
  constructor() {
    WMSet.call(privateProp1, this, "I am Private1");
    WMSet.call(privateProp2, this, "I am Private2");

    this.publicVar = "I am public";
    this.publicMethod = () => {
      console.log(WMGet.call(privateProp1, this), WMGet.call(privateProp2, this))
    };        
  }

  printPrivate() {
    console.log(WMGet.call(privateProp1, this));
  }
}

Ответ 7

Для будущих ссылок на других пользователей, я слышал, что рекомендация состоит в том, чтобы использовать WeakMaps для хранения личных данных.

Вот более понятный рабочий пример:

function storePrivateProperties(a, b, c, d) {
  let privateData = new WeakMap;
  // unique object as key, weak map can only accept object as key, when key is no longer referened, garbage collector claims the key-value 
  let keyA = {}, keyB = {}, keyC = {}, keyD = {};

  privateData.set(keyA, a);
  privateData.set(keyB, b);
  privateData.set(keyC, c);
  privateData.set(keyD, d);

  return {
    logPrivateKey(key) {
      switch(key) {
      case "a":
        console.log(privateData.get(keyA));
        break;
      case "b":
        console.log(privateData.get(keyB));
        break;
      case "c":
        console.log(privateData.get(keyC));
        break;
      case "d":
        console.log(privateData.set(keyD));
        break;
      default:
        console.log(`There is no value for ${key}`)
      }
    }
  }
}

Ответ 8

Зависит от о котором вы спрашиваете: -)

Нет private модификатор свойства включен в Предложение минимально минимальных классов, которое, похоже, превратилось в текущий проект.

Однако может существовать поддержка личные имена, что позволяет использовать частные свойства - и они, вероятно, могут также использоваться в определениях классов.

Ответ 9

Использование модулей ES6 (изначально предложенных @d13) хорошо работает для меня. Он не идеально имитирует частные свойства, но по крайней мере вы можете быть уверены, что свойства, которые должны быть частными, не будут протекать вне вашего класса. Вот пример:

something.js

let _message = null;
const _greet = name => {
  console.log('Hello ' + name);
};

export default class Something {
  constructor(message) {
    _message = message;
  }

  say() {
    console.log(_message);
    _greet('Bob');
  }
};

Затем потребительский код может выглядеть так:

import Something from './something.js';

const something = new Something('Sunny day!');
something.say();
something._message; // undefined
something._greet(); // exception

Обновление (важно):

Как отмечают в комментариях @DanyalAytekin, эти частные свойства являются статическими, поэтому глобальны по объему. Они будут хорошо работать при работе с Singletons, но для объектов Transient необходимо соблюдать осторожность. Расширение приведенного выше примера:

import Something from './something.js';
import Something2 from './something.js';

const a = new Something('a');
a.say(); // a

const b = new Something('b');
b.say(); // b

const c = new Something2('c');
c.say(); // c

a.say(); // c
b.say(); // c
c.say(); // c

Ответ 10

Завершение @d13 и комментарии @johnny-oshika и @DanyalAytekin:

Я думаю, что в примере, предоставленном @johnny-oshika, мы могли бы использовать обычные функции вместо функций стрелок, а затем .bind их с текущим объектом плюс объект _privates в качестве валютного параметра:

something.js

function _greet(_privates) {
  return 'Hello ' + _privates.message;
}

function _updateMessage(_privates, newMessage) {
  _privates.message = newMessage;
}

export default class Something {
  constructor(message) {
    const _privates = {
      message
    };

    this.say = _greet.bind(this, _privates);
    this.updateMessage = _updateMessage.bind(this, _privates);
  }
}

main.js

import Something from './something.js';

const something = new Something('Sunny day!');

const message1 = something.say();
something.updateMessage('Cloudy day!');
const message2 = something.say();

console.log(message1 === 'Hello Sunny day!');  // true
console.log(message2 === 'Hello Cloudy day!');  // true

// the followings are not public
console.log(something._greet === undefined);  // true
console.log(something._privates === undefined);  // true
console.log(something._updateMessage === undefined);  // true

// another instance which doesn't share the _privates
const something2 = new Something('another Sunny day!');

const message3 = something2.say();

console.log(message3 === 'Hello another Sunny day!'); // true

Преимущества, о которых я могу думать:

  • мы можем иметь частные методы (_greet и _updateMessage действовать как частные методы, если мы не export ссылки)
  • хотя они и не находятся в прототипе, вышеупомянутые методы будут сохранять память, потому что экземпляры создаются один раз, вне класса (в отличие от их определения в конструкторе)
  • мы не утечка каких-либо глобальных переменных, так как мы находимся внутри модуля
  • мы также можем иметь частные свойства, используя связанный объект _privates

Некоторые недостатки, о которых я могу думать:

  • менее интуитивно понятный
  • смешанное использование синтаксиса класса и старых шаблонов школы (привязки объектов, переменные с областью модуля/функции)
  • жесткие привязки - мы не можем перепроверять общедоступные методы (хотя мы можем улучшить это, используя мягкие привязки (https://github.com/getify/You-Dont-Know-JS/blob/master/this%20%26%20object%20prototypes/ch2.md#softening-binding))

Доступный фрагмент можно найти здесь: http://www.webpackbin.com/NJgI5J8lZ

Ответ 11

Да - вы можете создать инкапсулированное свойство, но это не было сделано с помощью модификаторов доступа (public | private), по крайней мере, не с ES6.

Вот простой пример того, как это можно сделать с ES6:

1 Создайте класс, используя class слово

2 Внутри этого конструктора объявить переменную с блочной областью, используя let ИЛИ const зарезервированные слова → поскольку они являются областью блока, к которой они не могут быть доступны извне (инкапсулированные)

3 Чтобы позволить некоторым элементам управления доступом (seters | getters) к этим переменным, вы можете объявить метод экземпляра внутри этого конструктора, используя: this.methodName=function(){} синтаксис

"use strict";
    class Something{
        constructor(){
            //private property
            let property="test";
            //private final (immutable) property
            const property2="test2";
            //public getter
            this.getProperty2=function(){
                return property2;
            }
            //public getter
            this.getProperty=function(){
                return property;
            }
            //public setter
            this.setProperty=function(prop){
                property=prop;
            }
        }
    }

Теперь давайте проверим его:

var s=new Something();
    console.log(typeof s.property);//undefined 
    s.setProperty("another");//set to encapsulated `property`
    console.log(s.getProperty());//get encapsulated `property` value
    console.log(s.getProperty2());//get encapsulated immutable `property2` value

Ответ 12

Другой подход к "private"

Вместо того, чтобы бороться с тем, что частная видимость в настоящее время недоступна в ES6, я решил использовать более практичный подход, который отлично подходит, если ваша среда IDE поддерживает JSDoc (например, Webstorm). Идея заключается в использовании тега @private. Что касается разработки, среда IDE не позволит вам получить доступ к любому частному члену вне его класса. Работает очень хорошо для меня, и это было действительно полезно для скрытия внутренних методов, поэтому функция автозаполнения показывает мне именно то, что класс действительно хотел разоблачить. Вот пример:

автозаполнение, показывающее только общедоступные вещи

Ответ 13

WeakMap

  • поддерживается в IE11 (символов нет)
  • hard-private (реквизиты с использованием символов мягко-частные из-за Object.getOwnPropertySymbols)
  • может выглядеть действительно чистым (в отличие от закрытий, для которых требуются все реквизиты и методы в конструкторе)

Сначала определите функцию для переноса WeakMap:

function Private() {
  const map = new WeakMap();
  return obj => {
    let props = map.get(obj);
    if (!props) {
      props = {};
      map.set(obj, props);
    }
    return props;
  };
}

Затем создайте ссылку вне вашего класса:

const p = new Private();

class Person {
  constructor(name, age) {
    this.name = name;
    p(this).age = age; // it easy to set a private variable
  }

  getAge() {
    return p(this).age; // and get a private variable
  }
}

Примечание. класс не поддерживается IE11, но в этом примере он выглядит более чистым.

Ответ 14

Я думаю, ответ Benjamin, вероятно, лучший для большинства случаев, пока язык не поддерживает явно частные переменные.

Однако, если по какой-то причине вам необходимо предотвратить доступ с помощью Object.getOwnPropertySymbols(), метод, который я рассмотрел, неконфигурируемое, неперечислимое, незаписываемое свойство, которое может использоваться как идентификатор свойства для каждого объекта при построении (например, уникальный Symbol, если у вас еще нет другого уникального свойства, такого как id). Затем просто держите карту каждого объекта 'private' переменных, используя этот идентификатор.

const privateVars = {};

class Something {
    constructor(){
        Object.defineProperty(this, '_sym', {
            configurable: false,
            enumerable: false,
            writable: false,
            value: Symbol()
        });

        var myPrivateVars = {
            privateProperty: "I'm hidden"
        };

        privateVars[this._sym] = myPrivateVars;

        this.property = "I'm public";
    }

    getPrivateProperty() {
        return privateVars[this._sym].privateProperty;
    }

    // A clean up method of some kind is necessary since the
    // variables won't be cleaned up from memory automatically
    // when the object is garbage collected
    destroy() {
        delete privateVars[this._sym];
    }
}

var instance = new Something();
console.log(instance.property); //=> "I'm public"
console.log(instance.privateProperty); //=> undefined
console.log(instance.getPrivateProperty()); //=> "I'm hidden"

Потенциальное преимущество этого подхода в использовании WeakMap - более быстрое время доступа, если производительность становится проблемой.

Ответ 15

Я считаю, что можно получить "лучшее из обоих миров", используя замыкания внутри конструкторов. Существует два варианта:

Все члены данных являются закрытыми

function myFunc() {
   console.log('Value of x: ' + this.x);
   this.myPrivateFunc();
}

function myPrivateFunc() {
   console.log('Enhanced value of x: ' + (this.x + 1));
}

class Test {
   constructor() {

      let internal = {
         x : 2,
      };
      
      internal.myPrivateFunc = myPrivateFunc.bind(internal);
      
      this.myFunc = myFunc.bind(internal);
   }
};

Ответ 16

Фактически это возможно с использованием символов и прокси. Вы используете символы в области видимости класса и устанавливаете два ловушки в прокси: один для прототипа класса, так что Reflect.ownKeys(экземпляр) или Object.getOwnPropertySymbols не дает ваши символы, а другой для самого конструктора поэтому, когда вызывается new ClassName(attrs), возвращаемый экземпляр будет перехвачен и будет заблокирован собственный символ свойств. Здесь код:

const Human = (function() {
  const pet = Symbol();
  const greet = Symbol();

  const Human = privatizeSymbolsInFn(function(name) {
    this.name = name; // public
    this[pet] = 'dog'; // private 
  });

  Human.prototype = privatizeSymbolsInObj({
    [greet]() { // private
      return 'Hi there!';
    },
    revealSecrets() {
      console.log(this[greet]() + ` The pet is a ${this[pet]}`);
    }
  });

  return Human;
})();

const bob = new Human('Bob');

console.assert(bob instanceof Human);
console.assert(Reflect.ownKeys(bob).length === 1) // only ['name']
console.assert(Reflect.ownKeys(Human.prototype).length === 1 ) // only ['revealSecrets']


// Setting up the traps inside proxies:
function privatizeSymbolsInObj(target) { 
  return new Proxy(target, { ownKeys: Object.getOwnPropertyNames });
}

function privatizeSymbolsInFn(Class) {
  function construct(TargetClass, argsList) {
    const instance = new TargetClass(...argsList);
    return privatizeSymbolsInObj(instance);
  }
  return new Proxy(Class, { construct });
}

Ответ 17

Лично мне нравится предложение оператора привязки :: и затем объединить его с упомянутым решением @d13, но на данный момент придерживаться с ответом @d13, где вы используете ключевое слово export для своего класса и помещаете частные функции в модуль.

существует еще одно жесткое решение, которое не упоминалось здесь ниже, более функциональный подход и позволит ему иметь все частные реквизиты/методы внутри класса.

Private.js

export const get = state => key => state[key];
export const set = state => (key,value) => { state[key] = value; }

Test.js

import { get, set } from './utils/Private'
export default class Test {
  constructor(initialState = {}) {
    const _set = this.set = set(initialState);
    const _get = this.get = get(initialState);

    this.set('privateMethod', () => _get('propValue'));
  }

  showProp() {
    return this.get('privateMethod')();
  }
}

let one = new Test({ propValue: 5});
let two = new Test({ propValue: 8});
two.showProp(); // 8
one.showProp(); // 5

комментарии к этому будут оценены.

Ответ 18

Я столкнулся с этим сообщением при поиске лучшей практики для "частных данных для классов". Было упомянуто, что некоторые из шаблонов будут иметь проблемы с производительностью.

Я собрал несколько тестов jsperf на основе 4 основных шаблонов из онлайн-книги "Изучение ES6":

http://exploringjs.com/es6/ch_classes.html#sec_private-data-for-classes

Тесты можно найти здесь:

https://jsperf.com/private-data-for-classes

В Chrome 63.0.3239/Mac OS X 10.11.6 наиболее эффективными моделями были "Частные данные через среды конструктора" и "Частные данные через соглашение об именах". Для меня Safari хорошо зарекомендовал себя для WeakMap, но Chrome не очень хорошо.

Я не знаю влияния памяти, но шаблон для "среды конструктора", который некоторые предупреждали, был проблемой производительности, был очень результативным.

4 основных шаблона:

Частные данные через среды конструктора

class Countdown {
    constructor(counter, action) {
        Object.assign(this, {
            dec() {
                if (counter < 1) return;
                counter--;
                if (counter === 0) {
                    action();
                }
            }
        });
    }
}
const c = new Countdown(2, () => {});
c.dec();
c.dec();

Частные данные через среды конструктора 2

class Countdown {
    constructor(counter, action) {
        this.dec = function dec() {
            if (counter < 1) return;
            counter--;
            if (counter === 0) {
                action();
            }
        }
    }
}
const c = new Countdown(2, () => {});
c.dec();
c.dec();

Частные данные с помощью соглашения об именах

class Countdown {
    constructor(counter, action) {
        this._counter = counter;
        this._action = action;
    }
    dec() {
        if (this._counter < 1) return;
        this._counter--;
        if (this._counter === 0) {
            this._action();
        }
    }
}
const c = new Countdown(2, () => {});
c.dec();
c.dec();

Личные данные через WeakMaps

const _counter = new WeakMap();
const _action = new WeakMap();
class Countdown {
    constructor(counter, action) {
        _counter.set(this, counter);
        _action.set(this, action);
    }
    dec() {
        let counter = _counter.get(this);
        if (counter < 1) return;
        counter--;
        _counter.set(this, counter);
        if (counter === 0) {
            _action.get(this)();
        }
    }
}
const c = new Countdown(2, () => {});
c.dec();
c.dec();

Личные данные с помощью символов

const _counter = Symbol('counter');
const _action = Symbol('action');

class Countdown {
    constructor(counter, action) {
        this[_counter] = counter;
        this[_action] = action;
    }
    dec() {
        if (this[_counter] < 1) return;
        this[_counter]--;
        if (this[_counter] === 0) {
            this[_action]();
        }
    }
}
const c = new Countdown(2, () => {});
c.dec();
c.dec();

Ответ 19

class Something {
  constructor(){
    var _property = "test";
    Object.defineProperty(this, "property", {
        get: function(){ return _property}
    });
  }
}

var instance = new Something();
console.log(instance.property); //=> "test"
instance.property = "can read from outside, but can't write";
console.log(instance.property); //=> "test"

Ответ 20

Даже Typescript не может этого сделать. Из документация:

Когда элемент отмечен как личный, доступ к нему из-за пределов его класса невозможен. Например:

class Animal {
    private name: string;
    constructor(theName: string) { this.name = theName; }
}

new Animal("Cat").name; // Error: 'name' is private;

Но на их игровая площадка это дает:

var Animal = (function () {
    function Animal(theName) {
        this.name = theName;
    }
    return Animal;
}());
console.log(new Animal("Cat").name);

Таким образом, их ключевое слово "private" неэффективно.

Ответ 21

Придя очень поздно к этой вечеринке, но я попал в вопрос OP в поиске, поэтому... Да, вы можете иметь частные свойства, обернув объявление класса в закрытии

Есть пример того, как у меня есть частные методы в этот код. В нижеприведенном фрагменте класс Subscribable имеет две функции 'private' process и processCallbacks. Любые свойства могут быть добавлены таким образом, и они остаются закрытыми с помощью закрытия. Конфиденциальность IMO - редкая необходимость, если проблемы хорошо разделены, и Javascript не нуждается в том, чтобы стать раздутым, добавив больше синтаксиса, когда закрытие выполняет эту работу аккуратно.

const Subscribable = (function(){

  const process = (self, eventName, args) => {
    self.processing.set(eventName, setTimeout(() => processCallbacks(self, eventName, args)))};

  const processCallbacks = (self, eventName, args) => {
    if (self.callingBack.get(eventName).length > 0){
      const [nextCallback, ...callingBack] = self.callingBack.get(eventName);
      self.callingBack.set(eventName, callingBack);
      process(self, eventName, args);
      nextCallback(...args)}
    else {
      delete self.processing.delete(eventName)}};

  return class {
    constructor(){
      this.callingBack = new Map();
      this.processing = new Map();
      this.toCallbacks = new Map()}

    subscribe(eventName, callback){
      const callbacks = this.unsubscribe(eventName, callback);
      this.toCallbacks.set(eventName,  [...callbacks, callback]);
      return () => this.unsubscribe(eventName, callback)}  // callable to unsubscribe for convenience

    unsubscribe(eventName, callback){
      let callbacks = this.toCallbacks.get(eventName) || [];
      callbacks = callbacks.filter(subscribedCallback => subscribedCallback !== callback);
      if (callbacks.length > 0) {
        this.toCallbacks.set(eventName, callbacks)}
      else {
        this.toCallbacks.delete(eventName)}
      return callbacks}

    emit(eventName, ...args){
      this.callingBack.set(eventName, this.toCallbacks.get(eventName) || []);
      if (!this.processing.has(eventName)){
        process(this, eventName, args)}}}})();

Мне нравится этот подход, потому что он прекрасно разделяет проблемы и сохраняет все по-настоящему частным. Единственным недостатком является необходимость использования "я" (или чего-то подобного) для ссылки на 'this' в частном контенте.

Ответ 22

О, так много экзотических решений! Меня обычно не волнует конфиденциальность, поэтому я использую "псевдо-конфиденциальность", как здесь сказано. Но если все равно (если для этого есть особые требования), я использую что-то вроде этого:

class jobImpl{
  // public
  constructor(name){
    this.name = name;
  }
  // public
  do(time){
    console.log('${this.name} started at ${time}');
    this.prepare();
    this.execute();
  }
  //public
  stop(time){
    this.finish();
    console.log('${this.name} finished at ${time}');
  }
  // private
  prepare(){ console.log('prepare..'); }
  // private
  execute(){ console.log('execute..'); }
  // private
  finish(){ console.log('finish..'); }
}

function Job(name){
  var impl = new jobImpl(name);
  return {
    do: time => impl.do(time),
    stop: time => impl.stop(time)
  };
}

// Test:
// create class "Job"
var j = new Job("Digging a ditch");
// call public members..
j.do("08:00am");
j.stop("06:00pm");

// try to call private members or fields..
console.log(j.name); // undefined
j.execute(); // error

Другая возможная реализация функции (конструктор) Job:

function Job(name){
  var impl = new jobImpl(name);
  this.do = time => impl.do(time),
  this.stop = time => impl.stop(time)
}

Ответ 23

См. этот ответ для чистого и простого "класса" решения с частным и общедоступным интерфейсом и поддержкой композиции

Ответ 24

Да, вполне может, и довольно легко. Это делается путем просмотра ваших частных переменных и функций путем возврата графа объекта-прототипа в конструкторе. Это ничего нового, но возьмите немного js foo, чтобы понять его элегантность. Этот способ не использует глобальные облачные или слабые карты. Это форма отражения, встроенная в язык. В зависимости от того, как вы это используете; можно либо вызвать исключение, которое прерывает стек вызовов, либо закопать исключение как undefined. Это показано ниже и может больше узнать об этих функциях здесь

class Clazz {
  constructor() {
    var _level = 1

    function _private(x) {
      return _level * x;
    }
    return {
      level: _level,
      public: this.private,
      public2: function(x) {
        return _private(x);
      },
      public3: function(x) {
        return _private(x) * this.public(x);
      },
    };
  }

  private(x) {
    return x * x;
  }
}

var clazz = new Clazz();

console.log(clazz._level); //undefined
console.log(clazz._private); // undefined
console.log(clazz.level); // 1
console.log(clazz.public(1)); //2
console.log(clazz.public2(2)); //2
console.log(clazz.public3(3)); //27
console.log(clazz.private(0)); //error

Ответ 25

На самом деле это возможно.
1. Сначала создайте класс и в конструкторе верните вызываемую _public функцию.
2. В вызываемой _public функции передайте this ссылку (чтобы получить доступ ко всем частным методам и реквизитам) и все аргументы из constructor (которые будут переданы в new Names())
3. В области _public function также есть класс Names с доступом к this (_this) ссылке класса private Names

class Names {
  constructor() {
    this.privateProperty = 'John';
    return _public(this, arguments);
  }
  privateMethod() { }
}

const names = new Names(1,2,3);
console.log(names.somePublicMethod); //[Function]
console.log(names.publicProperty); //'Jasmine'
console.log(names.privateMethod); //undefined
console.log(names.privateProperty); //undefind

function _public(_this, _arguments) {
  class Names {
    constructor() {
      this.publicProperty = 'Jasmine';
      _this.privateProperty; //"John";
      _this.privateMethod; //[Function]
    }

    somePublicMethod() {
      _this.privateProperty; //"John";
      _this.privateMethod; //[Function]
    }

  }
  return new Names(..._arguments);
}

Ответ 26

Я нашел очень простое решение, просто используйте Object.freeze(). Конечно, проблема заключается в том, что вы не можете ничего добавить к объекту позже.

class Cat {
    constructor(name ,age) {
        this.name = name
        this.age = age
        Object.freeze(this)
    }
}

let cat = new Cat('Garfield', 5)
cat.age = 6 // doesn't work, even throws an error in strict mode

Ответ 27

Я использую этот шаблон, и он всегда работал для меня

class Test {
    constructor(data) {
        class Public {
            constructor(prv) {

                // public function (must be in constructor on order to access "prv" variable)
                connectToDb(ip) {
                    prv._db(ip, prv._err);
                } 
            }

            // public function w/o access to "prv" variable
            log() {
                console.log("I'm logging");
            }
        }

        // private variables
        this._data = data;
        this._err = function(ip) {
            console.log("could not connect to "+ip);
        }
    }

    // private function
    _db(ip, err) {
        if(!!ip) {
		    console.log("connected to "+ip+", sending data '"+this.data+"'");
			return true;
		}
        else err(ip);
    }
}



var test = new Test(10),
		ip = "185.167.210.49";
test.connectToDb(ip); // true
test.log(); // I'm logging
test._err(ip); // undefined
test._db(ip, function() { console.log("You have got hacked!"); }); // undefined

Ответ 28

Вы можете попробовать этот https://www.npmjs.com/package/private-members

Этот пакет сохранит элементы по экземпляру.

const pvt = require('private-members');
const _ = pvt();

let Exemplo = (function () {    
    function Exemplo() {
        _(this).msg = "Minha Mensagem";
    }

    _().mensagem = function() {
        return _(this).msg;
    }

    Exemplo.prototype.showMsg = function () {
        let msg = _(this).mensagem();
        console.log(msg);
    };

    return Exemplo;
})();

module.exports = Exemplo;

Ответ 29

Большинство ответов либо говорят, что это невозможно, либо требуют использования WeakMap или Symbol, которые являются функциями ES6, которые, вероятно, потребуют многопотоков. Там, тем не менее, другой путь! Проверьте это:

// 1. Create closure
var SomeClass = function() {
  // 2. Create `key` inside a closure
  var key = {};
  // Function to create private storage
  var private = function() {
    var obj = {};
    // return Function to access private storage using `key`
    return function(testkey) {
      if(key === testkey) return obj;
      // If `key` is wrong, then storage cannot be accessed
      console.error('Cannot access private properties');
      return undefined;
    };
  };
  var SomeClass = function() {
    // 3. Create private storage
    this._ = private();
    // 4. Access private storage using the `key`
    this._(key).priv_prop = 200;
  };
  SomeClass.prototype.test = function() {
    console.log(this._(key).priv_prop); // Using property from prototype
  };
  return SomeClass;
}();

// Can access private property from within prototype
var instance = new SomeClass();
instance.test(); // `200` logged

// Cannot access private property from outside of the closure
var wrong_key = {};
instance._(wrong_key); // undefined; error logged

Ответ 30

Другой способ, похожий на последние два опубликованных

class Example {
  constructor(foo) {

    // privates
    const self = this;
    this.foo = foo;

    // public interface
    return self.public;
  }

  public = {
    // empty data
    nodata: { data: [] },
    // noop
    noop: () => {},
  }

  // everything else private
  bar = 10
}

const test = new Example('FOO');
console.log(test.foo); // undefined
console.log(test.noop); // { data: [] }
console.log(test.bar); // undefined