JavaScript ES6 sınıflarındaki özel mülkler


444

ES6 sınıflarında özel mülkler oluşturmak mümkün müdür?

İşte bir örnek. Erişimini nasıl önleyebilirim instance.property?

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

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

5
Aslında bu özellik için 3. aşama teklifi var - tc39.github.io/proposal-class-fields github.com/tc39/proposal-class-fields
arty

@arty Buna örneklerle bir cevap verdim: stackoverflow.com/a/52237988/1432509
Alister

Yanıtlar:


165

Özel alanlar (ve yöntemler) ECMA standardında uygulanmaktadır . Bunları bugün babel 7 ve aşama 3 ön ayarıyla kullanmaya başlayabilirsiniz .

class Something {
  #property;

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

  #privateMethod() {
    return 'hello world';
  }

  getPrivateMessage() {
      return this.#privateMethod();
  }
}

const instance = new Something();
console.log(instance.property); //=> undefined
console.log(instance.privateMethod); //=> undefined
console.log(instance.getPrivateMessage()); //=> hello world

Bu sınıf alanlarının nasıl çalıştığını merak ediyorum. thisArama yapmadan önce şu anda kurucuda kullanamazsınız super(). Yine de babel onları daha önce koyar.
seeker_of_bacon

ESLint'in #privateCrapsözdizimine izin verecek şekilde nasıl yapılandırılır ?
Marecky

6
Peki ya eslint? Eşit işarette ayrıştırıcı hatası aldım. Babel çalışıyor, eslint bu yeni js sözdizimini ayrıştıramıyor.
martonx

6
Vay canına, bu çok çirkin. Hashtag geçerli bir karakterdir. Mülkiyet gerçekten özel değil mi? .. TypeScript'te kontrol ettim. Özel üyeler özel veya salt okunur olarak (dışarıdan) derlenmez. Başka bir (kamu) mülk gibi ilan ettim. (ES5).
Dominik

2
Bununla nasıl özel yöntemler yazıyorsunuz ? Bunu yapabilir miyim #beep() {}; ve bu async #bzzzt() {}:?
Константин Ван

277

Kısa cevap, hayır, ES6 sınıfları olan özel mülkler için yerel destek yoktur.

Ancak, yeni özellikleri nesneye iliştirmeden değil, bunları bir sınıf yapıcısının içinde tutarak bu davranışı taklit edebilir ve gizli özelliklere ulaşmak için alıcıları ve ayarlayıcıları kullanabilirsiniz. Alıcıların ve ayarlayıcıların sınıfın her yeni örneğinde yeniden tanımlandığını unutmayın.

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

1
Bu çözümü en çok seviyorum. Ölçeklendirme için kullanılmaması gerektiğine katılıyorum, ancak genellikle dahil başına sadece bir kez örneklenecek sınıflar için mükemmeldir.
Blake Regalia

2
Ayrıca, her yeni oluşturma işleminde bu sınıfın her bir bileşenini yeniden tanımlıyorsunuz.
Quentin Roy

10
Bu çok garip! ES6'da ES6'dan daha fazla "kapatma piramidi" oluşturuyorsunuz! Bir yapıcı İÇİNDE işlevlerin tanımlanması, yukarıdaki ES5 örneğinde olduğundan daha çirkin görünür.
Kokodoko

1
OP özellikle ES6 sınıflarını sorduğundan, teknik olarak çalışmasına rağmen kişisel olarak bunun kötü bir çözüm olduğunu düşünüyorum. En büyük sınırlama, şimdi özel değişkenleri kullanan her sınıf yönteminin yapıcı içinde bildirilmesi classve ilk olarak sözdizimine sahip olmanın avantajlarını ciddi şekilde azaltmasıdır .
NanoWizard

10
Bütün bunları dolaylı olarak tanıtmak. Şimdi getNameve setNameözelliklerini nasıl özel hale getirirsiniz ?
aij

195

@ Loganfsmyth'in cevabını genişletmek için:

JavaScript'teki tek gerçek veri yine de kapsam dahilindeki değişkenlerdir. Herkese açık mülklerle aynı şekilde dahili olarak erişilen mülkler anlamında özel mülkleriniz olamaz, ancak özel verileri depolamak için kapsamlandırılmış değişkenleri kullanabilirsiniz.

Kapsamlı değişkenler

Buradaki yaklaşım, özel verileri saklamak için özel olan yapıcı işlevinin kapsamını kullanmaktır. Bu özel verilere erişme yöntemlerinin, yapıcı içinde de oluşturulması gerekir, yani bunları her örnekle yeniden oluşturuyorsunuz. Bu bir performans ve hafıza cezasıdır, ancak bazıları cezanın kabul edilebilir olduğuna inanmaktadır. Özel verilere her zamanki gibi prototipe eklenerek erişilmesi gerekmeyen yöntemler için ceza önlenebilir.

Misal:

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

Kapsamlı Zayıf Harita

WeakMap, önceki yaklaşımın performansını ve bellek cezasını önlemek için kullanılabilir. WeakMaps, verileri yalnızca bu WeakMap kullanılarak erişilebilecek şekilde Nesneler (burada, örnekler) ile ilişkilendirir. Bu nedenle, özel bir WeakMap oluşturmak için kapsamlandırılmış değişkenler yöntemini kullanırız, ardından bu WeakMap'i ile ilişkili özel verileri almak için kullanırız.this . Bu, kapsamdaki değişkenler yönteminden daha hızlıdır, çünkü tüm örnekleriniz tek bir WeakMap'i paylaşabilir, bu nedenle yalnızca kendi WeakMap'lerine erişmelerini sağlamak için yöntemleri yeniden oluşturmanız gerekmez.

Misal:

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

Bu örnek, birden çok özel özellik için bir WeakMap kullanmak için bir Nesne kullanır; ayrıca birden fazla WeakMaps kullanabilir veage.set(this, 20) veya küçük bir sarmalayıcı yazıp başka bir şekilde kullanabilirsiniz privateProps.set(this, 'age', 0).

Bu yaklaşımın gizliliği teorik olarak küresel WeakMap nesneye . Bununla birlikte, tüm JavaScript karışık küreseller tarafından kırılabilir. Kodumuz zaten bunun olmadığı varsayımına dayanmaktadır.

(Bu yöntem de yapılabilir Map, ancak WeakMapdaha iyidir, çünkü Mapçok dikkatli olmadığınız sürece bellek sızıntıları yaratacaktır ve bu amaç için ikisi farklı değildir.)

Yarı Cevap: Kapsamlı Semboller

Sembol, bir özellik adı olarak işlev görebilen bir tür ilkel değerdir. Özel bir Sembol oluşturmak için kapsamlı değişken yöntemini kullanabilir, ardından özel verileri adresinde depolayabilirsiniz this[mySymbol].

Bu yöntemin gizliliği kullanılarak ihlal edilebilir Object.getOwnPropertySymbols, ancak yapılması biraz gariptir.

Misal:

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)`.

Yarı Cevap: Alt Çizgiler

Eski varsayılan, yalnızca alt çizgi önekine sahip bir ortak mülk kullanın. Herhangi bir şekilde özel bir mülk olmamasına rağmen, bu sözleşme, okuyucuların mülkiyeti özel olarak ele alması gerektiğini bildiren iyi bir iş çıkardığı kadar yaygındır ve bu da genellikle işi yapar. Bu gecikme karşılığında, okunması daha kolay, yazması daha kolay ve daha hızlı bir yaklaşım elde ediyoruz.

Misal:

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.

Sonuç

ES2017'den itibaren, özel mülkler yapmanın mükemmel bir yolu yoktur. Çeşitli yaklaşımların artıları ve eksileri vardır. Kapsamlı değişkenler gerçekten özeldir; Kapsamlı WeakMaps kapsam kapsamındaki değişkenlerden çok özel ve daha pratiktir; Kapsamlı Semboller oldukça özel ve oldukça pratiktir; alt çizgiler genellikle yeterince özel ve çok pratiktir.


7
İlk örnek snippet ("kapsamlandırılmış değişkenler") tam bir antipatterndir - döndürülen her nesnenin farklı bir sınıfı olacaktır. Bunu yapma. Ayrıcalıklı yöntemler istiyorsanız, bunları yapıcıda oluşturun.
Bergi

1
Bir sınıfın bir fonksiyonun içine sarılması, ilk etapta sınıfları kullanmanın tüm amacını yenmektedir. Bu işlevi zaten bir örnek oluşturmak için kullanıyorsanız, tüm özel / genel üyelerinizi de bu işlevin içine yerleştirebilir ve tüm sınıf anahtar sözcüğünü unutabilirsiniz.
Kokodoko

2
@Bergi @Kokodoko Kapsamlı değişkenler yaklaşımını biraz daha hızlı olacak ve kırılmayacak şekilde düzenledim instanceof. Bu yaklaşımı sadece tamlık uğruna içerdiğini düşündüğümü ve gerçekten ne kadar yetenekli olabileceğini daha fazla düşünmesi gerektiğini itiraf ediyorum.
tristan

1
Mükemmel açıklama! ES6'nın özel bir değişkeni simüle etmeyi zorlaştırdığıma hala şaşırıyorum, ES5'te sadece var ve bunu özel ve genel simüle etmek için bir işlev içinde kullanabilirsiniz.
Kokodoko

2
@Kokodoko Sınıftan vazgeçerseniz ve her şeyi işleve koyarsanız, prototip yöntemini kullanarak miras uygulamaya dönmeniz de gerekecektir. Sınıflarda genişletme kullanmak açık bir yaklaşımdır, bu nedenle bir işlev içinde bir sınıf kullanmak tamamen kabul edilebilir.
AndroidDev

117

Güncelleme: Daha iyi sözdizimi içeren bir teklif yolda. Katkıları bekliyoruz.


Evet, - nesnelerde kapsamlı erişim için - ES6 Symbols .

Semboller benzersizdir, yansıma (Java / C # içindeki ayrıcalıklar gibi) dışında dışarıdan birine erişemezsiniz, ancak içerideki bir sembole erişimi olan herkes tuş erişimi için kullanabilir:

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

6
Kullanamaz mısın Object.getOwnPropertySymbols? ;)
Qantas 94 Heavy

41
@BenjaminGruenbaum: Görünüşe göre Semboller artık gerçek gizliliği garanti etmiyor: stackoverflow.com/a/22280202/1282216
d13

28
@trusktr tuşlarıyla mı? Hayır. Sembollerle mi? Evet. Özel alanlara erişmek için C # ve Java gibi dillerde yansımayı nasıl kullanabileceğinize çok benzer. Erişim değiştiriciler güvenlikle ilgili değildir - niyetin netliği ile ilgilidir.
Benjamin Gruenbaum

9
Bu, Symbols kullanmak gibi bir şey gibi görünüyor.Bu const myPrivateMethod = Math.random(); Something.prototype[''+myPrivateMethod] = function () { ... } new Something()[''+myPrivateMethod]();gerçekten gizlilik değil, geleneksel JavaScript anlamında belirsiz. Değişkenleri kapsüllemek için kapatma kullanarak "özel" JavaScript düşünün. Dolayısıyla bu değişkenlere yansıma yoluyla erişilemez.
trusktr

13
Ayrıca kullanıyorum hissediyorum privateve protectedanahtar kelimeleri çok daha temiz olacak Symbolya Name. Köşeli ayraç gösterimi yerine nokta gösterimini tercih ederim. Özel şeyler için nokta kullanmaya devam etmek istiyorum. this.privateVar
trusktr

33

Cevap hayır". Ancak, aşağıdaki gibi özelliklere özel erişim oluşturabilirsiniz:

(Sembollerin gizliliği sağlamak için kullanılabileceği önerisi ES6 spesifikasyonunun önceki bir sürümünde geçerliydi, ancak artık geçerli değil: https://mail.mozilla.org/pipermail/es-discuss/2014-Ocak/035604. html ve https://stackoverflow.com/a/22280202/1282216 . Semboller ve gizlilik hakkında daha uzun bir tartışma için bkz. https://curiosity-driven.org/private-properties-in-javascript )


6
-1, bu gerçekten sorunuza cevap vermiyor. (Kapakları ES5'te IIFE'lerle de kullanabilirsiniz). Özel özellikler çoğu dilde (Java, C #, vb.) Yansıtılarak numaralandırılabilir. Özel mülklerin amacı, niyeti diğer programcılara iletmek ve güvenliği sağlamak değildir.
Benjamin Gruenbaum

1
@BenjaminGruenbaum, biliyorum, daha iyi bir yanıtım olsaydı, ben de bundan memnun değilim.
d13

Bence semboller programlama ortamında erişilemeyen üyelere ulaşmanın geçerli bir yolu. Evet, eğer istersen reallllyyy hala bulunabilir, ama mesele bu değil mi? Hassas bilgileri içinde saklamamalısınız, ancak bunu yine de istemci tarafı kodunda yapmamalısınız. Ancak, bir özelliği veya yöntemi bir dış sınıftan gizlemek amacıyla çalışır.
Kokodoko

Sınıf düzeyinde kapsamlandırılmış değişkenlerin bir sınıftaki özel özelliklerin yerine kullanılması, statitc özelliklerine benzer bir singleton.behaviour veya davranışa yol açacaktır.
Adrian Moisa

30

JS'de gerçek gizliliği elde etmenin tek yolu kapsam oluşturmaktır, bu nedenle thissadece bileşen içinden erişilebilecek bir özelliğe sahip olmanın bir yolu yoktur . Gerçekten özel verileri ES6'da depolamanın en iyi yolu bir WeakMap kullanmaktır.

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

Açıkçası bu muhtemelen yavaş ve kesinlikle çirkin, ama gizlilik sağlıyor.

Javencript çok dinamik olduğu için, BU BİLE mükemmel olmadığını unutmayın. Birisi hala yapabilirdi

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

değerleri saklandıkça yakalamak için, bu yüzden ekstra dikkatli olmak istiyorsanız , geçersiz kılınan prototipe güvenmek yerine yerel bir referans almanız .setve .getaçıkça kullanmanız gerekir.

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

3
Öneri olarak, bir nesneyi değer olarak kullanarak özellik başına bir zayıf harita kullanmaktan kaçınabilirsiniz. Bu şekilde, gether yöntem için harita sayısını bire azaltabilirsiniz (örn. const _ = privates.get(this); console.log(_.privateProp1);).
Quentin Roy

Evet, bu da tamamen bir seçenek. Bir kullanıcının gerçek özellikleri kullanırken ne yazacağına daha doğrudan eşlendiği için çoğunlukla bununla gittim.
loganfsmyth

@loganfsmyth const myObj = new SomeClass(); console.log(privateProp1.get(myObj)) // "I am Private1", mülkünüzün özel veya gizli olduğu anlamına mı geliyor?
Barbu Barbu

2
Bunun çalışması için, özelliğe erişen kodun normalde bir modülün içinde yer alan ve erişilemeyen WeakMap nesnesine erişmesi gerekir.
loganfsmyth

22

Diğerlerine bakanlar için gelecekte başvurmak üzere, şimdi öneri özel verileri tutmak için WeakMaps kullanmak olduğunu duyuyorum .

İşte daha açık, çalışan bir örnek:

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

20
Bu özelliklerin statik olduğunu unutmayın.
Michael Theriot

8
Seni küçümsemedim ama zayıf harita örneğin tamamen yanlış.
Benjamin Gruenbaum

4
Yani - Verileri örnek başına değil tüm sınıf örnekleri arasında paylaşıyorsunuz - en azından düzeltebilir miyim?
Benjamin Gruenbaum

1
Gerçekten de, zayıf haritanın belirli bir örneğe bağlanması gerekir. Örnek için fitzgeraldnick.com/weblog/53 adresine bakın .
genişletilmiş

2
MDN'ye göre, Semboller gibi ilkel veri türlerine WeakMap anahtarı olarak izin verilmez. MDN WeakMap Belgeleri
leepowell

12

Kime sorduğunuza bağlı olarak değişir :-)

Geçerli taslakta yapılmış gibi görünen Maksimal düzeyde sınıf teklifine hiçbir privateözellik değiştirici dahil edilmemiştir .

Bununla birlikte, özel mülklere izin veren özel adlar için destek olabilir - ve muhtemelen sınıf tanımlarında da kullanılabilirler.


3
Bu var oldukça ES7 için özel bir şey bazı formun onlar olsa ediyoruz düşünme, ES6 içine özel isimler yapacaktır olası.
Qantas 94 Heavy

Hem özel adları hem de benzersiz dize değerlerini, anladığım kadarıyla Semboller yerine aldı.
Benjamin Gruenbaum

Evet, muhtemelen Semboller olacak. Ancak, şu anda teknik özelliklerde bulunan "semboller" yalnızca [[prototip]] gibi dahili özellikleri tanımlamak için kullanılır ve bunları kullanıcı kodunda oluşturmanın ve kullanmanın bir yolu yoktur. Bazı dokümanlar biliyor musunuz?
Bergi

Modüllerin gizliliği ayarlamak için kullanılabileceğini fark ettim. İhtiyacınız olan tek şey olabilecek Sembollerle birlikte ...?
d13

1
@Cody: Tüm modül kodunuzun zaten ES6'da kendi kapsamı var, IEFE'ye gerek yok. Ve evet, semboller mahremiyet için değil, benzersizlik (çarpışma-kaçınma) için tasarlanmıştır.
Bergi

10

ES6 modüllerini kullanmak (başlangıçta @ d13 tarafından önerilen) benim için iyi çalışıyor. Özel mülkleri mükemmel bir şekilde taklit etmez, ancak en azından özel olması gereken mülklerin sınıfınızın dışına sızmayacağından emin olabilirsiniz. İşte bir örnek:

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

Sonra tüketen kod şöyle görünebilir:

import Something from './something.js';

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

Güncelleme (Önemli):

@DanyalAytekin'in yorumlarda belirttiği gibi, bu özel mülkler durağandır, dolayısıyla kapsamı küreseldir. Singletons ile çalışırken iyi çalışırlar, ancak Geçici nesneler için dikkatli olunmalıdır. Yukarıdaki örneği genişletmek:

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

4
İçin iyi private static.
Danyal Aytekin

@DanyalAytekin: Bu çok iyi bir nokta. Bu özel mülkler statiktir ve kapsam bakımından globaldir. Cevabımı bunu yansıtacak şekilde güncelledim.
Johnny Oshika

İşlevsel programlamayı (özellikle Elm ve Haskell) öğrendikçe JS programcılarının OOP sınıfı tabanlı bir modülden ziyade "modülerlik" modül tabanlı bir yaklaşımdan faydalanacağına inanıyorum. ES6 modüllerini uygulama geliştirmenin temeli olarak düşünürsek ve sınıfları tamamen unutursak, genel olarak çok daha iyi uygulamalarla sonuçlanabileceğimize inanıyorum. Deneyimli Elm veya Haskell kullanıcıları bu yaklaşım hakkında yorum yapabilir mi?
d13

1
Güncellemede, ikinci a.say(); // aolması gerekirb.say(); // b
grokky

denedim let _message = null, çok serin değil, çağrı yapıcı birden çok kez, karışıklık.
Littlee

9

@ D13 ve yorumlar @ johnny-oshika ve @DanyalAytekin tarafından tamamlanıyor:

@ Johnny-oshika tarafından sağlanan örnekte ok işlevleri yerine normal işlevleri kullanabileceğimizi ve sonra .bindgeçerli nesne artı _privatesbir curried parametresi olarak bir nesne ile tahmin edebilirsiniz :

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

Düşünebileceğim faydalar:

  • özel yöntemlere sahip olabiliriz ( _greetve referansları kullanmadığımız _updateMessagesürece özel yöntemler gibi davranabiliriz export)
  • prototipte olmamasına rağmen, yukarıda belirtilen yöntemler hafızayı kurtaracaktır çünkü örnekler bir kez, sınıfın dışında yaratılmıştır (bunları kurucuda tanımlamak yerine)
  • bir modülün içinde olduğumuzdan küresellere sızmıyoruz
  • bağlı _privatesnesneyi kullanarak özel özelliklere de sahip olabiliriz

Düşünebileceğim bazı dezavantajlar:

Çalışan bir snippet'i burada bulabilirsiniz: http://www.webpackbin.com/NJgI5J8lZ


8

Evet - kapsüllenmiş özellik oluşturabilirsiniz , ancak erişim değiştiricileriyle (genel | özel) en azından ES6 ile yapılmaz.

ES6 ile nasıl yapılabileceğine dair basit bir örnek:

1 Sınıfı kullanarak sınıf oluşturma kelimesini

2 İçinde yapıcı let OR const kullanarak blok kapsamındaki değişkeni bildirir ayrılmış kelimeleri blok kapsamındaki bildirir -> blok kapsamı oldukları için dışarıdan erişilemez (kapsüllenmiş)

3 Bu değişkenlere bazı erişim kontrollerine (setters | getters) izin vermek için yapıcısının içindeki örnek yöntemini kullanarak şunu kullanabilirsiniz: this.methodName=function(){}sözdizimi

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

Şimdi kontrol edelim:

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

1
Bu, (şimdilik), yapıcıda bildirilen tüm yöntemlerin sınıfın her örneği için yeniden bildirilmesine rağmen, bu soruna tek çözümdür. Bu, performans ve bellek kullanımı ile ilgili oldukça kötü bir fikirdir. Sınıf yöntemleri kurucu kapsamı dışında bildirilmelidir.
Freezystem

@Freezystem İlk: İlk bu, örnek yöntemler (değil Sınıf yöntemleri) vardır. İkinci OP sorusu şuydu: _ instance.property?_ erişimini nasıl önleyebilirim ve cevabım: nasıl bir örnek ... Üçüncü olarak , daha iyi bir fikriniz varsa - duyalım
Nikita Kurtin

1
Yanlış olduğunuzu söylemiyordum, çözümlerinizin her çağrı yaptığınızda her örnek yönteminin bir kopyasının oluşturulmasına rağmen özel değişkenlere ulaşmak için en iyi uzlaşma olduğunu söyledim new Something();çünkü yöntemleriniz yapıcıya bunlara erişebileceği bildirildi özel değişkenler. Sınıfınızın çok sayıda örneğini oluşturursanız bu çok fazla bellek tüketimine neden olabilir, bu nedenle performans sorunları. Yöntemler, yapıcı kapsamı dışında beyan edilmiş olmalıdır. Benim yorumum, bir çözümlemeden ziyade çözümün dezavantajlarının bir açıklamasıydı.
Freezystem

1
Ancak yapıcı içinde tüm sınıfınızı tanımlamak kötü bir uygulama değil mi? Şu an javascript'i "hacklemek" değil miyiz? Diğer herhangi bir OOP programlama diline bakın ve yapıcı bir sınıf tanımlamak için değildir göreceksiniz.
Kokodoko

1
Evet demek istediğim bu ve çözümünüz işe yarıyor! Sadece genel olarak ES6'nın bir 'sınıf' anahtar kelime eklediğine şaşırdım, ancak kapsülleme sağlamak için var ve bununla çalışmanın zarif çözümünü kaldırdım.
Kokodoko

8

"Özel" e farklı bir yaklaşım

Özel görünürlüğün şu anda ES6'da kullanılamamasına karşı mücadele etmek yerine, IDE'niz JSDoc'u (ör. Webstorm) destekliyorsa daha iyi olan daha pratik bir yaklaşım benimsemeye karar verdim. Fikir @privateetiketi kullanmaktır . Geliştirme ilerledikçe, IDE kendi sınıfından herhangi bir özel üyeye erişmenizi engelleyecektir. Benim için oldukça iyi çalışıyor ve dahili yöntemleri gizlemek için gerçekten yararlı oldu, bu yüzden otomatik tamamlama özelliği bana sınıfın gerçekten ne anlama geldiğini gösteriyor. İşte bir örnek:

yalnızca herkese açık şeyleri gösteren otomatik tamamlama


1
Sorun şu ki, Editör üzerinden özel değişkenlere erişmek istemiyoruz, özel değişkenleri dışarıdan korumak istemiyoruz - Ve bu, kamu / özelin yaptığı. Kodunuz tamamlandıysa, erişebilirsiniz (ve önemli düşünceler: geçersiz kıl bu değişkenlere sınıf dışından ). Sizin @privatebu önleyemez yorum, bu sadece bir var Özelliği belgelerine nesil ve you'r IDE için.
Adrian Preuss

Evet, bunun farkındayım. Bu benim için yeterli ve dışarıdaki diğer insanlar için yeterli olabilir. Değişkenlerimi özel yapmadığını biliyorum; sadece dışarıdan erişmeye çalışmama konusunda beni uyarıyor (sadece, tabii ki ekibim ve ben bu özelliği destekleyen bir IDE kullanıyorsanız). Javascript (ve Python gibi diğer diller) erişim düzeyleri göz önünde bulundurularak tasarlanmamıştır. İnsanlar bir şekilde bu işlevselliği uygulamak için her türlü şeyi yaparlar, ancak sonunda bunu başarmak için dili hackliyoruz. İsterseniz daha "doğal" bir yaklaşımla gitmeye karar verdim.
Lucio Paiva

6

WeakMap

  • IE11'de desteklenir (Semboller yoktur)
  • hard-private (Sembolleri kullanan sahne nedeniyle yumuşak-özeldir Object.getOwnPropertySymbols)
  • gerçekten temiz görünebilir (yapıcıdaki tüm sahne ve yöntemleri gerektiren kapakların aksine)

İlk olarak, WeakMap'i sarmak için bir işlev tanımlayın:

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

Ardından, sınıfınızın dışında bir referans oluşturun:

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

Not: sınıf IE11 tarafından desteklenmez, ancak örnekte daha temiz görünür.


6

Ah, birçok egzotik çözüm! Ben genellikle burada söylendiği gibi "sözde gizlilik" kullanın gizlilik umurumda değil . Ama eğer ilgileniyorsanız (bunun için bazı özel gereksinimler varsa) bu örnekteki gibi bir şey kullanıyorum:

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

Fonksiyonun başka bir olası uygulaması (kurucu) Job:

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

5

Şahsen bağlama operatörünün önerisini seviyorum ::ve daha sonra bahsedilen @ d13 çözümü ile birleştireceğim, ancak şimdilik exportsınıfınız için anahtar kelimeyi kullandığınız ve özel işlevleri modüle koyduğunuz yerde @ d13'ün cevabı ile sopa .

burada daha işlevsel bir yaklaşım olan ve sınıftaki tüm özel sahne / yöntemlere sahip olmasına izin verecek olan burada değinilmeyen bir çözüm daha vardır.

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

bununla ilgili yorumlar takdir edilecektir.


Genellikle yaklaşımı severim. Geri bildirim: 1. çatışmayı önlemek için her sınıf için farklı bir private.js modülüne ihtiyacınız olacaktır. 2. Özel yöntemlerinizin her birini satır içi tanımlayarak yapıcıyı gerçekten uzun yapma potansiyelinden hoşlanmıyorum. 3. Sınıf yöntemlerinin tümü tek bir dosyada olsaydı iyi olurdu.
Doug Coburn

5

"Sınıflar için özel veriler" için en iyi uygulamaları ararken bu yazıyla karşılaştım. Kalıplardan birkaçının performans sorunları olacağı belirtildi.

"ES6'yı Keşfetme" adlı çevrimiçi kitabın 4 ana desenine dayanan birkaç jsperf testi bir araya getirdim:

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

Testler burada bulunabilir:

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

Chrome 63.0.3239 / Mac OS X 10.11.6'da en iyi performans gösteren desenler "Yapıcı ortamları aracılığıyla özel veriler" ve "Adlandırma kuralı aracılığıyla özel veriler" idi. Benim için Safari WeakMap için iyi performans gösterdi ancak Chrome o kadar iyi değil.

Hafıza etkisini bilmiyorum, ancak bazılarının uyardığı "yapıcı ortamları" nın deseni bir performans meselesi olacaktı.

4 temel model:

Yapıcı ortamları aracılığıyla özel veriler

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

Yapıcı ortamları aracılığıyla özel veriler 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();

Adlandırma kuralı ile özel veriler

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 aracılığıyla özel veriler

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

Sembollerle özel veriler

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

4

Inşaatçılar içindeki kapakları kullanarak 'her iki dünyanın en iyi' elde etmek mümkün olduğuna inanıyorum. İki varyasyon vardır:

Tüm veri üyeleri özeldir

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

Bazı üyeler özeldir

NOT: Bu kuşkusuz çirkin. Daha iyi bir çözüm biliyorsanız, lütfen bu yanıtı düzenleyin.

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


4

Aslında Semboller ve Proxy'ler kullanmak mümkündür. Sınıf kapsamındaki sembolleri kullanın ve bir proxy'de iki tuzak ayarlayın: biri sınıf prototipi için, böylece Reflect.ownKeys (örnek) veya Object.getOwnPropertySymbols sembollerinizi vermez, diğeri yapıcıya aittir bu nedenle new ClassName(attrs)çağrıldığında, döndürülen örnek ele geçirilir ve kendi özellik simgeleri engellenir. İşte kod:

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()şu şekilde çalışır: Object.getOwnPropertyNames(myObj).concat(Object.getOwnPropertySymbols(myObj))bu yüzden bu nesneler için bir tuzağa ihtiyacımız var.


Teşekkürler, sembolleri deneyeceğim :) Yukarıdaki tüm cevaplardan erişilemez bir sınıf üyesi oluşturmak için en basit yol gibi görünüyor :)
Kokodoko

4

Typescript bile yapamaz. Onların Gönderen belgeler :

Bir üye özel olarak işaretlendiğinde, üyeye kendi sınıfının dışından erişilemez. Örneğin:

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

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

Ancak oyun alanlarına aktarılan bu:

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

Dolayısıyla "özel" anahtar kelimeleri etkisizdir.


2
Eh, hala etkili çünkü IDE'deyken "kötü" programlamayı önlüyor. Hangi üyeleri kullanmanız ve kullanmamanız gerektiğini gösterir. Bence özel ve kamusal kullanımın ana nedeni budur. (Örneğin, C # 'ı makine koduna derlediğinizde private hala özel olacak mı? Kim bilir?). Diğer cevapları okurken, @Symbol kullanmanın bir üyeyi erişilemez hale getirebileceği anlaşılıyor. Ancak Semboller bile konsoldan bulunabilir.
Kokodoko

TypeScript hatası, TypeScript'in JavaScript'e aktarılması sırasında ortaya çıkıyor mu? (Tip kontrolünün zaman aşımına uğraması gibi. Bazı çalışma zamanı özel mekanizmalarından ziyade.)
Eljay

4

Bu partiye çok geç geliyor ama bir aramada OP sorusuna çarptım yani ... Evet, sınıf bildirimini bir kapanışta sararak özel mülkleriniz olabilir

Bu codepen özel yöntemleri nasıl bir örnek var . Aşağıdaki snippet'te, Subscribable sınıfının iki 'özel' işlevi vardır processve processCallbacks. Herhangi bir özellik bu şekilde eklenebilir ve kapak kullanımı ile özel tutulur. Endişeler iyi ayrılırsa ve bir kapatma düzgün bir şekilde iş yaptığında daha fazla sözdizimi ekleyerek Javascript'in şişirilmesine gerek yoksa IMO Gizlilik nadir bir gereksinimdir.

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

Bu yaklaşımı seviyorum çünkü endişeleri güzel bir şekilde ayırıyor ve işleri gerçekten gizli tutuyor. Tek dezavantajı, özel içerikte 'buna' atıfta bulunmak için 'kendini' (veya benzer bir şeyi) kullanma gereğidir.


4

Bence Benjamin'in cevabı , dil doğal olarak açıkça özel değişkenleri destekleyene kadar çoğu durumda en iyisi.

Ancak, herhangi bir nedenle erişimi önlemeniz gerekiyorsa Object.getOwnPropertySymbols(), kullanmayı düşündüğüm bir yöntem, inşaattaki her nesneye özellik tanımlayıcısı olarak kullanılabilen benzersiz, yapılandırılamaz, numaralandırılamaz, yazılamaz bir özellik eklemektir. (benzersiz gibi, a gibi Symbolbaşka benzersiz mülkleriniz yoksa id). Ardından, bu tanımlayıcıyı kullanarak her bir nesnenin 'özel' değişkenlerinin haritasını tutun.

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"

Bu yaklaşımın a WeakMapkullanımına göre potansiyel avantajı performans bir sorun haline geldiğinde daha hızlı erişim süresidir .


1
Yanılıyorsam beni düzeltin, ancak privateVars zaten yok edilmiş olsa bile privateVars bir nesnenin özel değişkenlerini saklayacağından bu kod bellek sızıntıları içermez mi?
Russell Santos

@RussellSantos haklısın, nesnelerin bir noktada çöp toplanması gerektiğini varsayarsak. Bunu işaret ettiğiniz için teşekkür ederim. destroy()Örneğimde, bir nesnenin kaldırılması gerektiğinde, kullanım kodu tarafından çağrılması gereken bir yöntem ekledim .
NanoWizard

4

Evet tamamen olabilir ve oldukça kolay. Bu, yapıcıdaki prototip nesne grafiğini döndürerek özel değişkenlerinizi ve işlevlerinizi göstererek yapılır. Bu yeni bir şey değil, ama onun zarafetini anlamak için biraz js foo al. Bu şekilde genel kapsam veya zayıf haritalar kullanılmaz. Dilde yerleşik bir yansıma biçimidir. Bunu nasıl kullandığınıza bağlı olarak; kişi çağrı yığınını kesintiye uğratan bir istisnayı zorlayabilir veya istisnayı bir undefined. Bu aşağıda kanıtlanmıştır ve bu özellikler hakkında daha fazla bilgiyi buradan edinebilirsiniz.

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)); //1
console.log(clazz.public2(2)); //2
console.log(clazz.public3(3)); //27
console.log(clazz.private(0)); //error


3
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"

2
Sadece kod cevaplarını önlemek en iyisidir. Kodunuzun OP'nin sorusuna nasıl yanıt verdiğini açıklamanız daha iyi olurdu
Stewart_R

Gerçekten bir salt okunur değişkeni özel bir değişkenten daha fazla nasıl yapılır. Özel bir değişken dışarıdan erişilemez. console.log(instance.property)atmak veya tanımsız vermek, geri vermek değil "test".
oooyaya

3

Gönderilen son ikisine benzer başka bir yol

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

2

Çoğu yanıt ya imkansız olduğunu söylüyor ya da muhtemelen çoklu dolgular gerektiren ES6 özellikleri olan bir WeakMap ya da Symbol kullanmanızı gerektiriyor. Ancak başka bir yol var! Şuna bir göz atın:

// 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

Bu yönteme erişimci kalıbı diyorum . Temel fikir, bir kapağın , kapağın içinde bir anahtarımız olması ve yalnızca anahtarınız varsa erişilebilen özel bir nesne (yapıcıda) oluşturmamızdır .

Eğer ilgileniyorsanız, makalemde bunun hakkında daha fazla bilgi edinebilirsiniz . Bu yöntemi kullanarak, kapatmanın dışında erişilemeyen nesne özellikleri başına oluşturabilirsiniz. Bu nedenle, bunları yapıcı veya prototipte kullanabilirsiniz, ancak başka hiçbir yerde kullanamazsınız. Bu yöntemin hiçbir yerde kullanılmadığını gördüm, ama bence gerçekten çok güçlü.


Soru, ES6 sınıflarında bunu nasıl başaracağınızla ilgiliydi.
Michael Franzl

ES6 sınıflarında tam olarak aynı yöntemi kullanabilirsiniz. ES6 sınıfları esas olarak benim örneğimde sunduğum gibi fonksiyonların üstünde şekerdir. Orijinal posterin bir transpiler kullanması oldukça mümkündür, bu durumda WeakMaps veya Symbols hala çoklu dolgular gerektirir. Cevabım ne olursa olsun geçerlidir.
guitarino

2

Özel ve genel arayüz ve kompozisyon desteği ile temiz ve basit bir 'sınıf' çözüm için bu cevaba bakınız.


2

Çok basit bir çözüm buldum, sadece kullanın Object.freeze(). Elbette sorun, nesneye daha sonra hiçbir şey ekleyememenizdir.

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

bu da ayarlayıcı yöntemini devre dışı bırakacaksetName(name) { this.name = name; }
ngakak

2

Bu kalıbı kullanıyorum ve her zaman benim için çalıştı

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


2

Aslında olduğunu mümkün.
1. İlk olarak sınıfı oluşturun ve yapıcıda çağrılan _publicişlevi döndürün.
Adı 2. _publicfonksiyonu geçen thisreferans (bütün özel yöntemler ve sahne erişmek için) , ve tüm bağımsız değişkenler constructor (yani geçilecek new Names())
3'tür _publicişlev kapsamında, aynı zamanda var olan Nameserişimi olan sınıf this(_this ) özel Namessınıfın referansı

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

2

Bunu deneyebilirsiniz https://www.npmjs.com/package/private-members

Bu paket üyeleri örnek olarak kaydeder.

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;
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.