Properti pribadi di kelas JavaScript ES6

444

Apakah mungkin membuat properti pribadi di kelas ES6?

Ini sebuah contoh. Bagaimana saya bisa mencegah akses instance.property?

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

var instance = new Something();
console.log(instance.property); //=> "test"
d13
sumber
5
Sebenarnya ada proposal tahap 3 untuk fitur ini - tc39.github.io/proposal-class-fields github.com/tc39/proposal-class-fields
arty
@arty Saya telah memberikan jawaban untuk ini dengan contoh: stackoverflow.com/a/52237988/1432509
Alister

Jawaban:

165

Bidang pribadi (dan metode) sedang diterapkan dalam standar ECMA . Anda dapat mulai menggunakannya hari ini dengan babel 7 dan tahap 3 yang telah ditetapkan.

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
Alister
sumber
Saya bertanya-tanya bagaimana cara kerja bidang kelas tersebut. Saat ini Anda tidak dapat menggunakan thiskonstruktor sebelum menelepon super(). Namun babel menempatkan mereka di depan super.
seeker_of_bacon
Bagaimana cara mengkonfigurasi ESLint untuk mengizinkan #privateCrapsintaks?
Marecky
6
Dan bagaimana dengan eslint? Saya mendapat kesalahan parser pada tanda sama dengan. Babel berfungsi, hanya eslint tidak dapat menguraikan sintaks js baru ini.
martonx
6
Wah ini sangat jelek. Hashtag adalah karakter yang valid. Properti itu tidak benar-benar pribadi, atau? .. Saya memeriksanya di TypeScript. Anggota pribadi tidak dikompilasi secara pribadi atau hanya-baca (dari luar). Baru dinyatakan seperti properti (publik) lainnya. (ES5).
Dominik
2
Bagaimana Anda menulis metode pribadi dengan ini? Bisakah saya melakukan ini #beep() {}:; dan ini async #bzzzt() {}:?
Константин Ван
277

Jawaban singkat, tidak, tidak ada dukungan asli untuk properti pribadi dengan kelas ES6.

Tetapi Anda bisa meniru perilaku itu dengan tidak melampirkan properti baru ke objek, tetapi menyimpannya di dalam konstruktor kelas, dan menggunakan getter dan setter untuk mencapai properti tersembunyi. Perhatikan bahwa getter dan setter mendapat definisi ulang pada setiap instance baru kelas.

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; }
}
MetalGodwin
sumber
1
Saya suka solusi ini yang terbaik. Saya setuju itu tidak boleh digunakan untuk penskalaan tetapi itu sempurna untuk kelas yang biasanya hanya akan dipakai satu kali per termasuk
Blake Regalia
2
Anda juga mendefinisikan ulang setiap komponen tunggal dari kelas ini setiap kali yang baru dibuat.
Quentin Roy
10
Ini sangat aneh! Di ES6 Anda membuat lebih banyak "piramida penutup" daripada sebelum ES6! Mendefinisikan fungsi DALAM WAKTU konstruktor terlihat lebih jelek daripada yang ada dalam contoh ES5 di atas.
Kokodoko
1
Karena OP secara khusus bertanya tentang kelas ES6, saya pribadi berpikir ini adalah solusi yang buruk, meskipun secara teknis berfungsi. Keterbatasan utama adalah bahwa sekarang setiap metode kelas yang menggunakan variabel pribadi harus dideklarasikan di dalam konstruktor, sangat merusak keuntungan memiliki classsintaksis di tempat pertama.
NanoWizard
10
Yang dilakukan adalah memperkenalkan tipuan. Sekarang bagaimana Anda menjadikan getNamedan setNameproperti pribadi?
aij
195

Untuk memperluas jawaban @ loganfsmyth:

Satu-satunya data yang benar-benar pribadi dalam JavaScript masih variabel cakupan. Anda tidak dapat memiliki properti pribadi dalam arti properti yang diakses secara internal dengan cara yang sama seperti properti publik, tetapi Anda dapat menggunakan variabel scoped untuk menyimpan data pribadi.

Variabel cakupan

Pendekatan di sini adalah dengan menggunakan ruang lingkup fungsi konstruktor, yang bersifat pribadi, untuk menyimpan data pribadi. Agar metode memiliki akses ke data pribadi ini, mereka juga harus dibuat di dalam konstruktor, artinya Anda membuat ulang dengan setiap contoh. Ini adalah penalti kinerja dan memori, tetapi beberapa percaya penalti itu dapat diterima. Hukuman dapat dihindari untuk metode yang tidak memerlukan akses ke data pribadi dengan menambahkannya ke prototipe seperti biasa.

Contoh:

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 Bercakupan

WeakMap dapat digunakan untuk menghindari kinerja pendekatan dan memori memori sebelumnya. WeakMaps mengaitkan data dengan Objects (di sini, instance) sedemikian rupa sehingga hanya dapat diakses menggunakan WeakMap itu. Jadi, kami menggunakan metode variabel lingkup untuk membuat WeakMap pribadi, lalu gunakan WeakMap itu untuk mengambil data pribadi yang terkait dengannya this. Ini lebih cepat daripada metode variabel cakupan karena semua instance Anda dapat membagikan WeakMap tunggal, jadi Anda tidak perlu membuat ulang metode hanya untuk membuatnya mengakses WeakMaps mereka sendiri.

Contoh:

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

Contoh ini menggunakan Objek untuk menggunakan satu WeakMap untuk beberapa properti pribadi; Anda juga dapat menggunakan beberapa WeakMaps dan menggunakannya age.set(this, 20), atau menulis pembungkus kecil dan menggunakannya dengan cara lain, seperti privateProps.set(this, 'age', 0).

Privasi dari pendekatan ini secara teoritis dapat dilanggar dengan merusak WeakMapobjek global . Karena itu, semua JavaScript dapat dipatahkan oleh global yang hancur. Kode kami sudah dibangun dengan asumsi bahwa ini tidak terjadi.

(Metode ini juga bisa dilakukan Map, tetapi WeakMaplebih baik karena Mapakan membuat kebocoran memori kecuali Anda sangat berhati-hati, dan untuk tujuan ini keduanya tidak berbeda.)

Setengah Jawab: Simbol yang Dicakup

Simbol adalah jenis nilai primitif yang dapat berfungsi sebagai nama properti. Anda dapat menggunakan metode variabel cakupan untuk membuat Simbol pribadi, lalu menyimpan data pribadi di this[mySymbol].

Privasi metode ini dapat dilanggar menggunakan Object.getOwnPropertySymbols, tetapi agak canggung untuk dilakukan.

Contoh:

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: Menggarisbawahi

Default yang lama, cukup gunakan properti publik dengan awalan garis bawah. Meskipun bukan merupakan properti pribadi dengan cara apa pun, konvensi ini cukup lazim sehingga ia melakukan pekerjaan yang baik dengan mengomunikasikan bahwa pembaca harus memperlakukan properti itu sebagai milik pribadi, yang sering kali menyelesaikan pekerjaannya. Sebagai gantinya selang ini, kami mendapatkan pendekatan yang lebih mudah dibaca, lebih mudah untuk mengetik, dan lebih cepat.

Contoh:

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.

Kesimpulan

Pada ES2017, masih belum ada cara sempurna untuk melakukan properti pribadi. Berbagai pendekatan memiliki pro dan kontra. Variabel yang dicakup benar-benar pribadi; WeakMaps scoped sangat pribadi dan lebih praktis daripada variabel scoped; Simbol yang dicakup cukup pribadi dan praktis; garis bawah sering kali cukup pribadi dan sangat praktis.

tristan
sumber
7
Cuplikan contoh pertama ("variabel cakupan") adalah antipattern total - setiap objek yang dikembalikan akan memiliki kelas yang berbeda. Jangan lakukan itu. Jika Anda menginginkan metode istimewa, buatlah di konstruktor.
Bergi
1
Membungkus kelas di dalam suatu fungsi tampaknya mengalahkan seluruh tujuan menggunakan kelas di tempat pertama. Jika Anda sudah menggunakan fungsi untuk membuat instance, Anda mungkin juga menempatkan semua anggota pribadi / publik Anda di dalam fungsi itu juga, dan melupakan kata kunci seluruh kelas.
Kokodoko
2
@Bergi @Kododoko Saya mengedit pendekatan variabel lingkup menjadi sedikit lebih cepat dan tidak rusak instanceof. Saya akui bahwa saya memikirkan pendekatan itu hanya untuk kepentingan kelengkapan dan seharusnya lebih memikirkan seberapa besar kemampuannya.
tristan
1
Penjelasan yang bagus! Saya masih terkejut bahwa ES6 sebenarnya membuat lebih sulit untuk mensimulasikan variabel pribadi, di mana di ES5 Anda bisa menggunakan var dan ini di dalam fungsi untuk mensimulasikan pribadi dan publik.
Kokodoko
2
@ Kodokoko Jika Anda membuang kelas dan hanya meletakkan segala sesuatu dalam fungsi, Anda juga harus kembali menerapkan warisan dengan menggunakan metode prototipe. Menggunakan extended pada kelas sejauh ini merupakan pendekatan yang lebih bersih, sehingga menggunakan kelas di dalam suatu fungsi benar-benar dapat diterima.
AndroidDev
117

Pembaruan: Sebuah proposal dengan sintaksis yang lebih baik sedang dalam perjalanan. Kontribusi dipersilahkan.


Ya, ada - untuk akses cakupanSymbol dalam objek - ES6 memperkenalkan s .

Simbol itu unik, Anda tidak bisa mendapatkan akses dari luar kecuali dengan refleksi (seperti privat di Java / C #) tetapi siapa pun yang memiliki akses ke simbol di dalam dapat menggunakannya untuk akses utama:

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
Benjamin Gruenbaum
sumber
6
Tidak dapat Anda gunakan Object.getOwnPropertySymbols? ;)
Qantas 94 Heavy
41
@BenjaminGruenbaum: Rupanya Simbol tidak lagi memastikan privasi sejati: stackoverflow.com/a/22280202/1282216
d13
28
@ trusktr melalui tombol thre? Tidak. Melalui simbol? Iya. Sangat mirip dengan bagaimana Anda dapat menggunakan refleksi dalam bahasa seperti C # dan Java untuk mengakses bidang pribadi. Pengubah akses bukan tentang keamanan - mereka tentang kejelasan niat.
Benjamin Gruenbaum
9
Sepertinya menggunakan Simbol mirip dengan melakukan const myPrivateMethod = Math.random(); Something.prototype[''+myPrivateMethod] = function () { ... } new Something()[''+myPrivateMethod]();. Ini bukan benar-benar privasi, itu tidak jelas, dalam arti JavaScript tradisional. Saya akan menganggap JavaScript "pribadi" berarti menggunakan penutupan untuk merangkum variabel. Variabel-variabel tersebut karenanya tidak dapat diakses melalui refleksi.
trusktr
13
Juga, saya merasa bahwa menggunakan privatedan protectedkata kunci akan jauh lebih bersih daripada Symbolatau Name. Saya lebih suka notasi titik daripada notasi braket. Saya ingin tetap menggunakan titik untuk hal-hal pribadi. this.privateVar
trusktr
33

Jawabannya adalah tidak". Tetapi Anda dapat membuat akses pribadi ke properti seperti ini:

(Saran bahwa Symbols dapat digunakan untuk memastikan privasi benar dalam versi sebelumnya dari spesifikasi ES6 tetapi tidak lagi terjadi: https://mail.mozilla.org/pipermail/es-discuss/2014-January/035604. html dan https://stackoverflow.com/a/22280202/1282216 . Untuk diskusi lebih lama tentang Simbol dan privasi, lihat: https://curiosity-driven.org/private-properties-in-javascript )

d13
sumber
6
-1, ini tidak benar-benar menjawab pertanyaan Anda. (Anda dapat menggunakan penutupan dengan IIFEs di ES5 juga). Properti pribadi dapat dihitung melalui refleksi dalam sebagian besar bahasa (Java, C #, dll). Maksud dari hak milik pribadi adalah untuk menyampaikan maksud kepada para pembuat program lainnya dan bukan untuk menegakkan keamanan.
Benjamin Gruenbaum
1
@BenjaminGruenbaum, saya tahu, saya berharap saya punya jawaban yang lebih baik, saya juga tidak senang dengan itu.
d13
Saya pikir simbol masih merupakan cara yang valid untuk mencapai anggota yang tidak dapat diakses saat berada di lingkungan pemrograman. Ya, mereka masih dapat ditemukan jika Anda benar-benar ingin, tetapi bukan itu intinya bukan? Anda tidak boleh menyimpan informasi sensitif di dalamnya, tetapi Anda seharusnya tidak melakukannya di kode sisi klien. Tetapi ini berfungsi untuk tujuan menyembunyikan properti atau metode dari kelas luar.
Kokodoko
Menggunakan variabel yang dicakup pada tingkat modul sebagai pengganti properti pribadi di kelas akan mengarah ke singleton. Perilaku atau perilaku serupa dengan sifat statitc. Bahan vars akan dibagikan.
Adrian Moisa
30

Satu-satunya cara untuk mendapatkan privasi sejati di JS adalah melalui pelingkupan, jadi tidak ada cara untuk memiliki properti yang merupakan anggota thisyang hanya dapat diakses di dalam komponen. Cara terbaik untuk menyimpan data yang benar-benar pribadi di ES6 adalah dengan 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));
  }
}

Jelas ini mungkin lambat, dan tentu saja jelek, tetapi memang memberikan privasi.

Perlu diingat bahwa BAHKAN INI tidak sempurna, karena Javascript sangat dinamis. Seseorang masih bisa melakukannya

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

untuk menangkap nilai saat disimpan, jadi jika Anda ingin ekstra hati-hati, Anda harus menangkap referensi lokal .setdan .getmenggunakan secara eksplisit alih-alih mengandalkan prototipe yang dapat ditimpa.

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));
  }
}
loganfsmyth
sumber
3
Sebagai saran, Anda dapat menghindari menggunakan satu peta lemah per properti dengan menggunakan objek sebagai nilai. Dengan cara ini Anda juga dapat mengurangi jumlah peta getmenjadi satu per metode (misalnya const _ = privates.get(this); console.log(_.privateProp1);).
Quentin Roy
Yup, itu juga pilihan. Saya lebih sering menggunakan ini karena memetakan lebih langsung ke apa yang akan ditulis pengguna ketika menggunakan properti nyata.
loganfsmyth
@loganfsmyth const myObj = new SomeClass(); console.log(privateProp1.get(myObj)) // "I am Private1", artinya properti Anda pribadi atau tidak?
Barbu Barbu
2
Agar itu berfungsi, kode yang mengakses properti akan memerlukan akses ke objek WeakMap, yang biasanya akan dimasukkan ke dalam modul dan tidak dapat diakses
loganfsmyth
22

Untuk referensi lain di masa depan pada pemirsa, saya mendengar sekarang bahwa rekomendasi adalah menggunakan WeakMaps untuk menyimpan data pribadi.

Berikut ini contoh yang lebih jelas dan berfungsi:

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}`)
      }
    }
  }
}
Komunitas
sumber
20
Ketahuilah bahwa properti ini statis.
Michael Theriot
8
Saya tidak menurunkan suara Anda tetapi contoh peta lemah Anda sepenuhnya salah.
Benjamin Gruenbaum
4
Yaitu - Anda membagikan data antara semua instance kelas dan tidak per instance - bolehkah saya setidaknya memperbaikinya?
Benjamin Gruenbaum
1
Memang, peta lemah harus dilampirkan ke contoh yang diberikan. Lihat fitzgeraldnick.com/weblog/53 untuk contoh.
melebar
2
Menurut MDN, tipe data primitif seperti Simbol tidak diperbolehkan sebagai kunci WeakMap. Dokumentasi WeakMap MDN
leepowell
12

Tergantung pada siapa Anda bertanya :-)

Tidak ada privatepengubah properti yang termasuk dalam proposal kelas minimal Maksimal yang tampaknya telah masuk ke draft saat ini .

Namun, mungkin ada dukungan untuk nama pribadi , yang memungkinkan properti pribadi - dan mereka mungkin dapat digunakan dalam definisi kelas juga.

Bergi
sumber
3
Ini sangat tidak mungkin bahwa nama pribadi akan membuatnya menjadi ES6, meskipun mereka berpikir sedang dari beberapa bentuk hal pribadi untuk ES7.
Qantas 94 Heavy
@ Qantas94Heavy baik nama pribadi dan nilai string unik telah digantikan oleh Simbol dari apa yang saya mengerti.
Benjamin Gruenbaum
Ya, itu mungkin akan menjadi Simbol. Namun, afaik "simbol" yang saat ini terkandung dalam spec hanya digunakan untuk menggambarkan properti internal seperti [[prototipe]], dan tidak ada cara untuk membuat dan menggunakannya dalam kode pengguna. Apakah Anda tahu beberapa dokumen?
Bergi
Saya baru menyadari bahwa modul dapat digunakan untuk mengatur privasi. Dikombinasikan dengan Simbol yang mungkin semua yang Anda butuhkan ...?
d13
1
@Cody: Seluruh kode modul Anda memang memiliki ruang lingkup sendiri di ES6, tidak perlu untuk IEFE. Dan ya, simbol ditujukan untuk keunikan (penghindaran tabrakan), bukan privasi.
Bergi
10

Menggunakan modul ES6 (awalnya diusulkan oleh @ d13) bekerja dengan baik untuk saya. Itu tidak meniru properti pribadi dengan sempurna, tetapi setidaknya Anda bisa yakin bahwa properti yang seharusnya pribadi tidak akan bocor di luar kelas Anda. Ini sebuah contoh:

sesuatu

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

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

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

Maka kode konsumsi dapat terlihat seperti ini:

import Something from './something.js';

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

Pembaruan (Penting):

Seperti @DanyalAytekin diuraikan dalam komentar, properti pribadi ini bersifat statis, sehingga cakupannya global. Mereka akan bekerja dengan baik ketika bekerja dengan Lajang, tetapi harus diperhatikan untuk objek Transient. Memperluas contoh di atas:

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
Johnny Oshika
sumber
4
Bagus untuk private static.
Danyal Aytekin
@DanyalAytekin: itu poin yang sangat bagus. Properti pribadi ini bersifat statis sehingga memiliki cakupan global. Saya telah memperbarui jawaban saya untuk mencerminkan ini.
Johnny Oshika
Semakin saya belajar tentang pemrograman fungsional (terutama Elm dan Haskell) semakin saya percaya bahwa programmer JS akan mendapat manfaat dari pendekatan berbasis modul untuk "modularitas" daripada yang berbasis kelas OOP. Jika kita menganggap modul ES6 sebagai fondasi untuk membangun aplikasi, dan melupakan kelas sepenuhnya, saya percaya kita bisa berakhir dengan aplikasi yang jauh lebih baik secara keseluruhan. Bisakah pengguna Elm atau Haskell yang berpengalaman mengomentari pendekatan ini?
13
1
Dalam pembaruan, yang kedua a.say(); // aharusb.say(); // b
grokky
let _message = nullCara dicoba , tidak begitu keren, ketika memanggil konstruktor beberapa kali, itu mengacaukan.
Littlee
9

Melengkapi @ d13 dan komentar oleh @ johnny-oshika dan @DanyalAytekin:

Saya kira dalam contoh yang disediakan oleh @ johnny-oshika kita bisa menggunakan fungsi normal alih-alih fungsi panah dan kemudian .bindmereka dengan objek saat ini ditambah _privatesobjek sebagai parameter kari:

sesuatu

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

Manfaat yang dapat saya pikirkan:

  • kita dapat memiliki metode pribadi ( _greetdan _updateMessagebertindak seperti metode pribadi selama kita tidak exportreferensi)
  • walaupun mereka tidak ada pada prototipe, metode yang disebutkan di atas akan menghemat memori karena instans dibuat sekali, di luar kelas (sebagai lawan mendefinisikannya dalam konstruktor)
  • kita tidak membocorkan global karena kita berada di dalam sebuah modul
  • kita juga dapat memiliki properti pribadi menggunakan _privatesobjek yang diikat

Beberapa kekurangan yang dapat saya pikirkan:

Cuplikan berjalan dapat ditemukan di sini: http://www.webpackbin.com/NJgI5J8lZ

efidiles
sumber
8

Ya - Anda dapat membuat properti enkapsulasi , tetapi belum dilakukan dengan pengubah akses (publik | pribadi) setidaknya tidak dengan ES6.

Berikut adalah contoh sederhana bagaimana hal itu dapat dilakukan dengan ES6:

1 Buat kelas menggunakan kata kelas

2 Di dalamnya konstruktor menyatakan variabel blok-dicakup menggunakan membiarkan ATAU kata-kata dicadangkan const -> karena mereka blok-lingkup mereka tidak dapat diakses dari luar (dienkapsulasi)

3 Untuk mengizinkan beberapa kontrol akses (setter | getter) ke variabel-variabel tersebut, Anda dapat mendeklarasikan metode instan di dalam konstruktornya menggunakan: this.methodName=function(){}sintaksis

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

Sekarang mari kita periksa:

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
Nikita Kurtin
sumber
1
Ini (untuk saat ini) adalah satu-satunya solusi untuk masalah ini terlepas dari kenyataan bahwa semua metode yang dideklarasikan dalam konstruktor telah dideklarasikan ulang untuk setiap instance kelas. Ini adalah ide yang buruk tentang kinerja dan penggunaan memori. Metode kelas harus dideklarasikan di luar lingkup konstruktor.
Freezystem
@ Freezystem First: Pertama itu adalah metode instance (bukan metode Class). Pertanyaan OP kedua adalah: _ Bagaimana saya bisa mencegah akses ke instance.property?_ dan jawaban saya adalah: contoh bagaimana ... Ketiga jika Anda memiliki ide yang lebih baik - mari kita dengarkan
Nikita Kurtin
1
Saya tidak mengatakan Anda salah, saya mengatakan bahwa solusi Anda adalah kompromi terbaik untuk mencapai variabel pribadi meskipun faktanya salinan setiap contoh metode dibuat setiap kali Anda menelepon new Something();karena metode Anda dinyatakan dalam konstruktor untuk memiliki akses ke ini variabel pribadi. Itu dapat menyebabkan banyak konsumsi memori jika Anda membuat banyak instance dari kelas Anda, sehingga masalah kinerja. Metode seharusnya dinyatakan di luar lingkup konstruktor. Komentar saya lebih merupakan penjelasan tentang kekurangan solusi Anda daripada kritik.
Freezystem
1
Tapi bukankah praktik buruk mendefinisikan seluruh kelas Anda di dalam konstruktor? Bukankah kita hanya "meretas" javascript sekarang? Lihat saja bahasa pemrograman OOP lainnya, dan Anda akan melihat bahwa konstruktor tidak dimaksudkan untuk mendefinisikan kelas.
Kokodoko
1
Ya itulah yang saya maksudkan, dan solusi Anda berhasil! Saya hanya mengatakan bahwa secara umum saya terkejut bahwa ES6 menambahkan kata kunci 'kelas', tetapi menghapus solusi elegan bekerja dengan var dan ini, untuk mencapai enkapsulasi.
Kokodoko
8

Pendekatan berbeda untuk "pribadi"

Alih-alih melawan fakta bahwa visibilitas pribadi saat ini tidak tersedia di ES6, saya memutuskan untuk mengambil pendekatan yang lebih praktis yang tidak masalah jika IDE Anda mendukung JSDoc (misalnya, Webstorm). Idenya adalah menggunakan @privatetag . Sejauh pengembangan berjalan, IDE akan mencegah Anda mengakses anggota pribadi dari luar kelasnya. Berfungsi cukup baik untuk saya dan sudah sangat berguna untuk menyembunyikan metode internal sehingga fitur lengkapi-otomatis menunjukkan kepada saya apa yang sebenarnya ingin diungkapkan oleh kelas. Ini sebuah contoh:

lengkapi otomatis hanya menampilkan barang publik

Lucio Paiva
sumber
1
Masalahnya adalah, bahwa kita tidak ingin mengakses variabel pribadi melalui Editor, kita tidak ingin melindungi variabel pribadi dari luar - Dan itulah yang dilakukan publik / swasta. Jika kode Anda selesai, Anda dapat mengakses (dan yang penting dipikirkan: menimpa ) variabel-variabel ini dari luar kelas. @privateKomentar Anda tidak dapat mencegah ini, itu hanya Fitur untuk pembuatan dokumentasi dan IDE Anda.
Adrian Preuss
Ya, saya tahu itu. Hanya saja itu sudah cukup untuk saya dan mungkin cukup untuk orang lain di luar sana. Saya tahu itu tidak benar-benar membuat variabel saya pribadi; itu hanya memperingatkan saya untuk tidak mencoba dan mengaksesnya dari luar (hanya, tentu saja, jika tim saya dan saya semua menggunakan IDE yang mendukung fitur ini). Javascript (dan bahasa lain, seperti Python) tidak dirancang dengan mempertimbangkan tingkat akses. Orang-orang melakukan segala macam hal untuk mengimplementasikan fungsi itu, tetapi pada akhirnya kami hanya meretas bahasa untuk mencapainya. Saya memutuskan untuk pergi dengan pendekatan yang lebih "alami", jika Anda mau.
Lucio Paiva
6

WeakMap

  • didukung di IE11 (Simbol tidak)
  • hard-private (alat peraga yang menggunakan Simbol bersifat soft-private karena Object.getOwnPropertySymbols)
  • dapat terlihat sangat bersih (tidak seperti penutupan yang membutuhkan semua alat peraga dan metode dalam konstruktor)

Pertama, tentukan fungsi untuk membungkus WeakMap:

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

Kemudian, buat referensi di luar kelas Anda:

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

Catatan: kelas tidak didukung oleh IE11, tetapi terlihat lebih bersih dalam contoh.

kevlened
sumber
6

Oh, banyak sekali solusi eksotis! Saya biasanya tidak peduli dengan privasi jadi saya menggunakan "privasi semu" seperti yang dikatakan di sini . Tetapi jika peduli (jika ada beberapa persyaratan khusus untuk itu) saya menggunakan sesuatu seperti dalam contoh ini:

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

Kemungkinan implementasi fungsi lainnya (konstruktor) Job:

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

Secara pribadi saya suka proposal dari operator mengikat :: dan kemudian akan menggabungkannya dengan solusi @ d13 yang disebutkan tetapi untuk sekarang tetap dengan jawaban @ d13 di mana Anda menggunakan exportkata kunci untuk kelas Anda dan meletakkan fungsi pribadi dalam modul.

ada satu lagi solusi sulit yang belum disebutkan di sini yang berikut adalah pendekatan yang lebih fungsional dan akan memungkinkannya untuk memiliki semua alat peraga / metode pribadi di dalam kelas.

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

komentar tentang itu akan sangat dihargai.

Robin F.
sumber
Secara umum saya suka pendekatannya. Umpan balik: 1. Anda akan memerlukan modul private.js yang berbeda untuk setiap kelas untuk mencegah bentrok. 2. Saya tidak suka potensi membuat konstruktor sangat panjang dengan mendefinisikan setiap metode pribadi Anda. 3. Alangkah baiknya jika semua metode kelas berada dalam satu file.
Doug Coburn
5

Saya menemukan posting ini ketika mencari praktik terbaik untuk "data pribadi untuk kelas". Disebutkan bahwa beberapa pola akan memiliki masalah kinerja.

Saya mengumpulkan beberapa tes jsperf berdasarkan 4 pola utama dari buku online "Exploring ES6":

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

Tes dapat ditemukan di sini:

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

Di Chrome 63.0.3239 / Mac OS X 10.11.6, pola berkinerja terbaik adalah "Data pribadi melalui lingkungan konstruktor" dan "Data pribadi melalui konvensi penamaan". Bagi saya Safari berkinerja baik untuk WeakMap tetapi Chrome tidak begitu baik.

Saya tidak tahu dampak memori, tetapi pola "lingkungan konstruktor" yang beberapa orang peringatkan akan menjadi masalah kinerja sangat bagus.

4 pola dasar adalah:

Data pribadi melalui lingkungan konstruktor

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

Data pribadi melalui lingkungan konstruktor 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();

Data pribadi melalui konvensi penamaan

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

Data pribadi melalui 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();

Data pribadi melalui simbol

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();
MarkM
sumber
4

Saya percaya adalah mungkin untuk mendapatkan 'terbaik dari kedua dunia' menggunakan penutupan di dalam konstruktor. Ada dua variasi:

Semua anggota data bersifat pribadi

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

Beberapa anggota bersifat pribadi

CATATAN: Ini diakui jelek. Jika Anda tahu solusi yang lebih baik, harap edit jawaban ini.

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

JSInitiate
sumber
4

Bahkan dimungkinkan menggunakan Simbol dan Proxy. Anda menggunakan simbol dalam ruang lingkup kelas dan menetapkan dua jebakan dalam proxy: satu untuk prototipe kelas sehingga Reflect.ownKeys (instance) atau Object.getOwnPropertySymbols tidak memberikan simbol Anda, yang lain adalah untuk konstruktor itu sendiri jadi ketika new ClassName(attrs)dipanggil, instance dikembalikan akan dicegat dan memiliki simbol properti sendiri diblokir. Berikut kodenya:

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()berfungsi seperti itu: Object.getOwnPropertyNames(myObj).concat(Object.getOwnPropertySymbols(myObj))itu sebabnya kita perlu jebakan untuk objek-objek ini.

Francisco Neto
sumber
Terima kasih, saya akan mencoba simbol :) Dari semua jawaban di atas tampaknya cara yang paling mudah untuk membuat anggota kelas tidak dapat diakses :)
Kokodoko
4

Bahkan naskah tidak dapat melakukannya. Dari dokumentasi mereka :

Ketika seorang anggota ditandai pribadi, itu tidak dapat diakses dari luar kelasnya yang mengandung. Sebagai contoh:

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

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

Tapi ditransfasikan di taman bermain mereka ini memberi:

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

Jadi kata kunci "pribadi" mereka tidak efektif.

Michael Franzl
sumber
2
Yah, itu masih efektif karena mencegah pemrograman "buruk", sementara di IDE. Ini menunjukkan kepada Anda anggota mana yang harus dan tidak harus Anda gunakan. Saya pikir itulah alasan utama untuk menggunakan pribadi dan publik. (Misalnya, ketika Anda mengkompilasi C # ke kode mesin, apakah privat akan tetap privat? Siapa tahu?). Saat membaca jawaban lain, tampaknya menggunakan @Symbol juga dapat membuat anggota tidak dapat diakses. Tetapi bahkan Simbol masih dapat ditemukan dari konsol.
Kokodoko
Apakah kesalahan TypeScript terjadi selama transpile dari TypeScript ke JavaScript? (Seperti pengecekan tipe terjadi pada waktu transdom. Daripada beberapa mekanisme privat runtime.)
Eljay
4

Datang sangat terlambat ke pesta ini tapi saya menekan pertanyaan OP dalam pencarian jadi ... Ya, Anda dapat memiliki properti pribadi dengan membungkus deklarasi kelas dalam penutupan

Ada contoh bagaimana saya memiliki metode pribadi dalam codepen ini . Dalam cuplikan di bawah ini, kelas Berlangganan memiliki dua fungsi 'pribadi' processdan processCallbacks. Properti apa pun dapat ditambahkan dengan cara ini dan sifatnya dirahasiakan melalui penggunaan penutupan. Privasi IMO adalah kebutuhan yang langka jika kekhawatiran dipisahkan dengan baik dan Javascript tidak perlu membengkak dengan menambahkan lebih banyak sintaks ketika sebuah penutupan dengan rapi melakukan pekerjaan.

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

Saya suka pendekatan ini karena ini memisahkan masalah dengan baik dan menjaga hal-hal yang benar-benar pribadi. Satu-satunya downside adalah kebutuhan untuk menggunakan 'diri' (atau sesuatu yang serupa) untuk merujuk 'ini' dalam konten pribadi.

Paul Whipp
sumber
4

Saya pikir jawaban Benjamin mungkin yang terbaik untuk sebagian besar kasus sampai bahasa itu secara native mendukung variabel pribadi secara eksplisit.

Namun, jika karena alasan tertentu Anda perlu mencegah akses Object.getOwnPropertySymbols(), metode yang saya pertimbangkan untuk digunakan adalah melampirkan properti unik, tidak dapat dikonfigurasi, tidak dapat dihitung, tidak dapat ditulis yang dapat digunakan sebagai pengidentifikasi properti untuk setiap objek pada konstruksi (seperti unik Symbol, jika Anda belum memiliki beberapa properti unik lainnya seperti a id). Kemudian simpan saja peta variabel 'pribadi' setiap objek menggunakan pengenal itu.

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"

Keuntungan potensial dari pendekatan ini daripada menggunakan WeakMapadalah waktu akses yang lebih cepat jika kinerja menjadi perhatian.

NanoWizard
sumber
1
Koreksi saya jika saya salah, tetapi bukankah kode ini berisi kebocoran memori karena privateVars masih akan menyimpan variabel pribadi objek bahkan jika objek sudah dihancurkan?
Russell Santos
@RussellSantos Anda benar, dengan asumsi objek akan perlu dikumpulkan di beberapa titik. Terima kasih telah menunjukkannya. Dalam contoh saya, saya telah menambahkan destroy()metode yang harus dipanggil dengan menggunakan kode setiap kali sebuah objek perlu dihapus.
NanoWizard
4

Ya benar-benar bisa, dan sangat mudah juga. Ini dilakukan dengan mengekspos variabel dan fungsi pribadi Anda dengan mengembalikan grafik objek prototipe dalam konstruktor. Ini bukan hal yang baru, tetapi ambillah sedikit untuk memahami keanggunannya. Cara ini tidak menggunakan cakupan global, atau kelemahan. Ini adalah bentuk refleksi yang dibangun ke dalam bahasa. Tergantung pada bagaimana Anda memanfaatkan ini; seseorang dapat memaksa pengecualian yang menginterupsi tumpukan panggilan, atau mengubur pengecualian sebagai undefined. Ini ditunjukkan di bawah ini, dan dapat membaca lebih lanjut tentang fitur ini di sini

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

1-14x0r
sumber
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"
Ilya Zarembsky
sumber
2
Yang terbaik adalah menghindari jawaban hanya kode. Akan lebih baik jika Anda bisa menjelaskan bagaimana kode Anda menjawab pertanyaan OP
Stewart_R
Ini benar-benar cara membuat variabel readonly lebih dari variabel pribadi. Variabel pribadi tidak boleh diakses dari luar. console.log(instance.property)harus melempar atau memberi Anda tidak terdefinisi, tidak memberi Anda kembali "tes".
oooyaya
3

Cara lain mirip dengan dua yang terakhir diposting

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
Jayesbe
sumber
2

Sebagian besar jawaban mengatakan itu tidak mungkin, atau mengharuskan Anda untuk menggunakan WeakMap atau Symbol, yang merupakan fitur ES6 yang mungkin membutuhkan polyfill. Namun ada cara lain! Lihat ini:

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

Saya menyebutnya pola pengakses metode ini . Ide dasarnya adalah bahwa kami memiliki penutup , kunci di dalam penutupan, dan kami membuat objek pribadi (dalam konstruktor) yang hanya dapat diakses jika Anda memiliki kunci .

Jika Anda tertarik, Anda dapat membaca lebih lanjut tentang ini di artikel saya . Menggunakan metode ini, Anda bisa membuat per objek properti yang tidak dapat diakses di luar penutupan. Oleh karena itu, Anda dapat menggunakannya dalam konstruktor atau prototipe, tetapi tidak di tempat lain. Saya belum pernah melihat metode ini digunakan di mana pun, tapi saya pikir ini sangat kuat.

guitarino
sumber
Pertanyaannya adalah tentang bagaimana mencapai ini di kelas ES6.
Michael Franzl
Anda dapat menggunakan metode yang sama persis di kelas ES6. Kelas ES6 terutama hanya gula di atas fungsi seperti yang saya sajikan dalam contoh saya. Sangat mungkin bahwa poster asli menggunakan transpiler, dalam hal ini WeakMaps atau Symbols masih membutuhkan polyfill. Jawaban saya valid bagaimanapun juga.
guitarino
2

Lihat jawaban ini untuk solusi 'kelas' yang bersih & sederhana dengan antarmuka pribadi dan publik serta dukungan untuk komposisi

kofifus
sumber
2

Saya menemukan solusi yang sangat sederhana, cukup gunakan Object.freeze(). Tentu saja masalahnya adalah Anda tidak dapat menambahkan apa pun ke objek nanti.

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
Nikola Andreev
sumber
ini juga akan menonaktifkan metode penyetel sepertisetName(name) { this.name = name; }
ngakak
2

Saya menggunakan pola ini dan selalu berhasil untuk saya

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

Yami Teru
sumber
2

Sebenarnya adalah mungkin.
1. Pertama, buat kelas dan di konstruktor mengembalikan _publicfungsi yang dipanggil .
2. Dalam _publicfungsi yang dipanggil lulus thisreferensi (untuk mendapatkan akses ke semua metode pribadi dan alat peraga) , dan semua argumen dari constructor (yang akan diteruskan new Names()).
3. Dalam _publiclingkup fungsi ada juga Nameskelas dengan akses ke this(_ini ) referensi Nameskelas privat

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);
}
Paweł
sumber
2

Anda dapat mencoba ini https://www.npmjs.com/package/private-members

Paket ini akan menyelamatkan anggota secara instan.

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;
João Henrique
sumber