класс — Частное лицо свойства в классах JavaScript ES6

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

с помощью JQuery: instance.property?

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

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

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

class Something {
  #property;

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

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

Краткий ответ, нет, встроенная поддержка частных свойств в классах 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; }
}

Подробнее о ответе @ 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

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

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's name but not age

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

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

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

Полуответ: Символы с областью видимости

Символ — это тип примитивного значения, которое может служить именем свойства. Вы можете использовать метод переменной области видимости, чтобы создать частный символ, а затем сохранить личные данные в 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's 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)`.

Half-Answer: Подчеркивает

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

Пример:

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's name and age. But we know we aren't
// supposed to access his age, which just might stop us.

Заключение

Начиная с ES2017, до сих пор не существует идеального способа сделать частную собственность. Различные подходы имеют свои плюсы и минусы. Переменные с областью видимости являются действительно частными ; Сфера видимости с областью видимости очень закрыты и более практичны, чем переменные с областью видимости ; Символы являются достаточно частными и достаточно практичными ; подчеркиваниями часто являются достаточно частными и очень практичными.

для исправления серверной части php предложение с более приятным синтаксисом готовится. Вклад приветствуется.


Да, есть — для доступа к областям в объектах — генераторы Symbol s Глядя на вывод

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

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

Ответ — «Нет». Но вы можете создать частный доступ к таким свойствам, как это:

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

Единственный способ получить истинную конфиденциальность в 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));
  }
}

Для дальнейшего ознакомления со зрителями, я слышу, что сейчас рекомендуется использовать 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}`)
      }
    }
  }
}

Зависит от кого вы спрашиваете : — ) Модификатор свойства включен в

Я обнаружил, что private Максимально минимальные классы , которые, кажется, вошли в предложение текущий черновик . Однако может быть Глядя на вывод

поддержка личных имен , который допускает закрытые свойства — и они, вероятно, также могут использоваться в определениях классов. Завершение @ d13 и комментариев @ johnny-oshika и @DanyalAytekin:

something.js

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

main.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);
  }
}

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

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 объект

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

Здесь можно найти работающий фрагмент: http: //www.webpackbin .com / NJgI5J8lZ

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

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

Update (Важно):

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

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

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

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

1 Создать класс с помощью class word

2 Внутри его конструктора объявить переменную в блоке, используя let или const зарезервированные слова — & gt ;, поскольку они относятся к области блока, к ним нельзя получить доступ извне (инкапсулировано) 3 Чтобы разрешить некоторый контроль доступа (сеттеры | геттеры) к этим переменным, вы можете объявить экземпляр метод внутри его конструктора, используя:

синтаксис 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;
            }
        }
    }

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

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

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

auto-complete showing just public stuff

WeakMap

  • hard-private (реквизиты, использующие символы, являются soft-private, потому что
  • может выглядеть действительно чисто (в отличие от замыканий, которые требуют все реквизиты и методы в конструкторе) ) Object.getOwnPropertySymbols С чем вы столкнулись
  • Сначала определим функцию для переноса WeakMap:

class

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's easy to set a private variable
  }

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

Примечание: Я считаю, что можно получить «лучшее из обоих миров», используя замыкания внутри конструкторов. Есть два варианта: не поддерживается IE11, но в примере он выглядит чище.

Некоторые члены являются приватными

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

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

ПРИМЕЧАНИЕ. Это ужасно. Если вы знаете лучшее решение, отредактируйте этот ответ.

На самом деле это возможно с помощью символов и прокси. Вы используете символы в области видимости класса и устанавливаете две ловушки в прокси. : один для прототипа класса, чтобы Reflect.ownKeys (instance) или Object.getOwnPropertySymbols не отдавал ваши символы, другой — для самого конструктора, поэтому при вызове

function myFunc(priv, pub) {
   pub.y = 3; // The Test object now gets a member 'y' with value 3.
   console.log('Value of x: '   priv.x);
   this.myPrivateFunc();
}

function myPrivateFunc() {
   pub.z = 5; // The Test object now gets a member 'z' with value 3.
   console.log('Enhanced value of x: '   (priv.x   1));
}

class Test {
   constructor() {
      
      let self = this;

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

возвращаемый экземпляр будет перехвачен и есть собственные символы свойств, заблокированные. Вот код: 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 });
}

Reflect.ownKeys() работает так: Object.getOwnPropertyNames(myObj).concat(Object.getOwnPropertySymbols(myObj)) Лично я ike предложение оператора

связывания , а затем объединить его с упомянутым решением @ d13, но сейчас придерживайтесь ответа @ d13, где вы используете ::, есть еще одно жесткое решение, которое не ‘ Здесь упоминалось, что ниже приведен более функциональный подход, и он позволил бы иметь все частные реквизиты / методы в классе. export Этот шаблон также известен как «цепочки прокси». Несколько других шаблонов дизайна используют делегирование — от него зависят состояние, шаблоны и шаблоны посетителей.

Private.js

Test.js

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

комментарии будут приветствоваться.

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

Ответ Бенджамина

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

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

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, , если производительность становится проблемой. если производительность становится проблемой.

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

Я собрал несколько тестов 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();
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"

Даже 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);

Вот

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

). С этой небольшой тестовой строкой прирост производительности незначителен, но при очень большом тексте вы почувствуете разницу (особенно если строки длинные, а разрывы строк не очень многочисленны). этого кодекса . В приведенном ниже фрагменте класс Subscribeable имеет две «приватные» функции 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)}}}})();

Мне нравится этот подход, потому что он хорошо разделяет проблемы и сохраняет конфиденциальность. Единственным недостатком является необходимость использовать «я» (или что-то подобное), чтобы ссылаться на «это» в частном контенте.

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

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

, поэтому ваш код будет работать только после загрузки окна, когда все ресурсы имеют был загружен. В этот момент jQuery (. Он включает интерфейс на основе Java, который определяет URL-адреса и упрощает доступ к ним. для простого & Простого ; простого решения ‘класса’ с приватным и открытый интерфейс и поддержка композиции

Да, вполне может, и довольно легко тоже. Это делается путем предоставления ваших личных переменных и функций путем возврата графа прототипа объекта в конструктор. В этом нет ничего нового, но потратьте немного 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

Я нашел очень простое решение, просто используйте 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

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

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

На самом деле это закрытие. Функция не должна возможно.
1. Сначала создайте класс и в конструкторе верните вызываемую функцию _public.
запустите _public, передайте this ссылку (чтобы получить доступ ко всем закрытым методам и реквизитам) и все аргументы из constructor (которые будут переданы в new Names() С чем вы столкнулись
3. В классе _public, также существует Names с доступом к this (_this) ссылке частного 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);
}

Вы можете попробовать это 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;

В классах можно использовать закрытые методы, использующие WeakMap Глядя на вывод

В соответствии с веб-документацией MDN :

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

Ссылки на объекты в ключах хранятся слабо, что означает, что они являются целью сборки мусора (GC), если нет другой ссылки на объект больше.

И это пример создания Queue структуры данных с закрытым членом _items, который содержит массив.

const _items = new WeakMap();

class Queue {    
    constructor() {
        _items.set(this, []);
    }

    enqueue( item) {
        _items.get(this).push(item);
    }    

    get count() {
        return _items.get(this).length;        
    }

    peek() {
        const anArray = _items.get(this);
        if( anArray.length == 0)
            throw new Error('There are no items in array!');

        if( anArray.length > 0)
            return anArray[0];
    }

    dequeue() {        
        const anArray = _items.get(this);
        if( anArray.length == 0)
            throw new Error('There are no items in array!');

        if( anArray.length > 0)
            return anArray.splice(0, 1)[0];
    }    
}

Пример использования:

const c = new Queue();
c.enqueue("one");
c.enqueue("two");
c.enqueue("three");
c.enqueue("four");
c.enqueue("five");
console.log(c);

Закрытый член _items скрыт и не может быть видно в собственности или методы Queue в любом случае для всех классных массивов высшего порядка :-) Используйте

enter image description here

Тем не менее, закрытый член _items в объекте Queue может быть достигнут следующим образом:

const anArray = _items.get(this);

Большинство ответов либо говорят, что это невозможно, либо требуют использования 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

Я вызываю этот метод шаблон метода доступа . Основная идея заключается в том, что у нас есть Закрытие, a Таким образом, обычное расширение состоит в том, чтобы разбить материал инициализации на его собственную функцию, а не на сам конструктор. Затем эта функция может просто унаследовать от базы: внутри замыкания, и мы создаем закрытый объект (в конструкторе), к которому можно получить доступ, только если у вас есть ключ { *} Если вам интересно, вы можете узнать больше об этом в Глядя на вывод

моей статье . Используя этот метод, вы можете создавать для каждого объекта свойства, к которым нельзя получить доступ за пределами замыкания. Следовательно, вы можете использовать их в конструкторе или прототипе, но не где-либо еще. Я нигде не видел, чтобы этот метод использовался, но я думаю, что он действительно мощный. Другой способ, похожий на два последних опубликованных здесь

, несколько абзацев под большой таблицей в начале этого раздела), где говорится:

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

с помощью JQuery: instance.property?

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

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

, здесь переменная

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

myThing является закрытой и является частью замыкания: Я разработал модуль, который помогает вам использовать ограничение доступа в класс JavaScript называется Capsulable. (Private & Amp ; Protected Static)

class Person {
  constructor() {

    var myThing = "Hello World";

    return {
      thing: myThing,
      sayThing: this.sayThing
    }
  }

  sayThing() {
    console.log(this.thing);
  }
}

var person = new Person();

console.log(person);

Если вам интересно, посмотрите мой пакет ниже.

https://github.com/hmmhmmhm/capsulable Читая предыдущий ответ, я подумал, что этот пример может суммировать вышеприведенные решения

const Capsulable = require('capsulable')
const Field = Capsulable()

class A {
    constructor(_field){
        // Configure data fields.
        Field(this, _field)

        // The code below provides access to
        // the data fields when creating
        // functions within the class.
        Field(this).private
        Field(this).protected
        Field(this).protectedStatic
    }
}

module.exports = A

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

const friend = Symbol('friend');

const ClassName = ((hidden, hiddenShared = 0) => {

    class ClassName {
        constructor(hiddenPropertyValue, prop){
            this[hidden] = hiddenPropertyValue *   hiddenShared;
            this.prop = prop
        }

        get hidden(){
            console.log('getting hidden');
            return this[hidden];
        }

        set [friend](v){
            console.log('setting hiddenShared');
            hiddenShared = v;
        }

        get counter(){
            console.log('getting hiddenShared');
            return hiddenShared;
        }

        get privileged(){
            console.log('calling privileged method');
            return privileged.bind(this);
        }
    }

    function privileged(value){
        return this[hidden]   value;
    }

    return ClassName;
})(Symbol('hidden'), 0);

const OtherClass = (() => class OtherClass extends ClassName {
    constructor(v){
        super(v, 100);
        this[friend] = this.counter - 1;
    }
})();

Ниже приведено только то, что я использую (может быть полезно). По сути, я заворачиваю класс внутри фабрики.

У меня есть веб-приложение с маркой в ​​соответствии с пользователем, который в данный момент вошел в систему. Я хотел бы изменить значок страницы на логотип частного ярлыка, но я не могу найти …

function Animal(name) {
    const privateData = 'NO experiments on animals have been done!';

    class Animal {
        constructor(_name) {
            this.name = _name;
        }
        getName() {
            return this.name
        }
        getDisclamer() {
            return `${privateData} Including ${this.name}`
        }
    }
    return new Animal(name)
}

метод массива следующим образом для получения массива с уникальными значениями:

Понравилась статья? Поделиться с друзьями:
JavaScript & TypeScript
Adblock
detector