Mengakses variabel anggota pribadi dari fungsi yang ditentukan prototipe

187

Apakah ada cara untuk membuat variabel "pribadi" (yang didefinisikan dalam konstruktor), tersedia untuk metode yang didefinisikan prototipe?

TestClass = function(){
    var privateField = "hello";
    this.nonProtoHello = function(){alert(privateField)};
};
TestClass.prototype.prototypeHello = function(){alert(privateField)};

Ini bekerja:

t.nonProtoHello()

Tetapi ini tidak:

t.prototypeHello()

Saya terbiasa mendefinisikan metode saya di dalam konstruktor, tetapi saya menjauh dari itu karena beberapa alasan.

kode morgancodes
sumber
14
@campver, Kecuali yang ini diminta 2 tahun sebelumnya ....
Pacerier

Jawaban:

191

Tidak, tidak ada cara untuk melakukannya. Itu pada dasarnya adalah pelingkupan terbalik.

Metode yang didefinisikan di dalam konstruktor memiliki akses ke variabel pribadi karena semua fungsi memiliki akses ke ruang lingkup di mana mereka didefinisikan.

Metode yang didefinisikan pada prototipe tidak didefinisikan dalam ruang lingkup konstruktor, dan tidak akan memiliki akses ke variabel lokal konstruktor.

Anda masih dapat memiliki variabel pribadi, tetapi jika Anda ingin metode yang didefinisikan pada prototipe untuk memiliki akses kepada mereka, Anda harus mendefinisikan getter dan setter di thisobjek, yang metode prototipe (bersama dengan segala sesuatu yang lain) akan memiliki akses ke. Sebagai contoh:

function Person(name, secret) {
    // public
    this.name = name;

    // private
    var secret = secret;

    // public methods have access to private members
    this.setSecret = function(s) {
        secret = s;
    }

    this.getSecret = function() {
        return secret;
    }
}

// Must use getters/setters 
Person.prototype.spillSecret = function() { alert(this.getSecret()); };
Triptych
sumber
14
"scoping in reverse" adalah fitur C ++ dengan kata kunci "friend". Pada dasarnya setiap fungsi harus mendefinisikan prototipe sebagai teman. Sayangnya konsep ini adalah C ++ dan bukan JS :(
TWiStErRob
1
Saya ingin menambahkan posting ini ke atas daftar favorit saya dan menyimpannya di sana.
Donato
2
Saya tidak mengerti intinya - Anda hanya menambahkan lapisan abstraksi yang tidak melakukan apa-apa. Anda mungkin juga membuat secretproperti dari this. JavaScript tidak mendukung variabel pribadi dengan prototipe karena prototipe terikat pada konteks situs panggilan, bukan konteks situs pembuatan.
nicodemus13
1
Mengapa tidak lakukan saja person.getSecret()?
Fahmi
1
Mengapa ini memiliki begitu banyak upvotes? Ini tidak menjadikan variabel pribadi. Seperti disebutkan di atas menggunakan person.getSecret () akan memungkinkan Anda mengakses variabel pribadi dari mana saja.
alexr101
64

Pembaruan: Dengan ES6, ada cara yang lebih baik:

Singkatnya, Anda bisa menggunakan yang baru Symboluntuk membuat bidang pribadi.
Berikut ini deskripsi yang bagus: https://curiosity-driven.org/private-properties-in-javascript

Contoh:

var Person = (function() {
    // Only Person can access nameSymbol
    var nameSymbol = Symbol('name');

    function Person(name) {
        this[nameSymbol] = name;
    }

    Person.prototype.getName = function() {
        return this[nameSymbol];
    };

    return Person;
}());

Untuk semua browser modern dengan ES5:

Anda bisa menggunakan Penutup saja

Cara paling sederhana untuk membangun objek adalah dengan menghindari warisan prototipal sama sekali. Cukup tentukan variabel pribadi dan fungsi publik dalam penutupan, dan semua metode publik akan memiliki akses pribadi ke variabel.

Atau Anda dapat menggunakan Prototipe saja

Dalam JavaScript, pewarisan prototypal terutama merupakan optimasi . Ini memungkinkan beberapa instance untuk berbagi metode prototipe, daripada setiap contoh memiliki metode sendiri.
Kekurangannya adalah satuthis - satunya hal yang berbeda setiap kali fungsi prototypal dipanggil.
Oleh karena itu, setiap bidang pribadi harus dapat diakses melalui this, yang berarti mereka akan menjadi publik. Jadi kami hanya tetap pada penamaan konvensi untuk _privatebidang.

Jangan repot-repot mencampur Penutupan dengan Prototipe

Saya pikir Anda tidak harus mencampur variabel penutupan dengan metode prototipe. Anda harus menggunakan yang satu atau yang lain.

Ketika Anda menggunakan penutupan untuk mengakses variabel pribadi, metode prototipe tidak dapat mengakses variabel. Jadi, Anda harus mengekspos penutupan itu this, yang berarti Anda mengeksposnya secara terbuka. Sangat sedikit yang bisa didapat dengan pendekatan ini.

Yang mana yang saya pilih?

Untuk benda yang benar-benar sederhana, cukup gunakan benda biasa dengan penutup.

Jika Anda membutuhkan warisan prototipal - untuk warisan, kinerja, dll - maka tetaplah dengan konvensi penamaan "_private", dan jangan repot-repot dengan penutupan.

Saya tidak mengerti mengapa pengembang JS berusaha keras untuk membuat bidang benar-benar pribadi.

Scott Rippey
sumber
4
Sayangnya, _privatekonvensi penamaan masih merupakan solusi terbaik jika Anda ingin memanfaatkan warisan prototipal.
naksir
1
ES6 akan memiliki konsep baru, yaitu Symbol, yang merupakan cara terbaik untuk membuat bidang pribadi. Berikut penjelasan yang bagus: curiosity-driven.org/private-properties-in-javascript
Scott Rippey
1
Tidak, Anda bisa Symbolmenutupnya yang mencakup seluruh kelas Anda. Dengan begitu, semua metode prototipe dapat menggunakan Simbol, tetapi tidak pernah diekspos di luar kelas.
Scott Rippey
2
Artikel yang Anda tautkan mengatakan " Simbol mirip dengan nama pribadi tetapi - tidak seperti nama pribadi - simbol tidak memberikan privasi sejati . ". Secara efektif, jika Anda memiliki instance, Anda bisa mendapatkan simbolnya Object.getOwnPropertySymbols. Jadi ini hanya privasi karena ketidakjelasan.
Oriol
2
@Oriol Ya, privasinya tidak jelas. Masih dimungkinkan untuk beralih melalui simbol, dan Anda menyimpulkan tujuan simbol melalui toString. Ini tidak berbeda dengan Java atau C # ... anggota pribadi masih dapat diakses melalui refleksi, tetapi biasanya sangat dikaburkan. Yang semuanya memperkuat poin terakhir saya, "Saya tidak mengerti mengapa pengembang JS berusaha keras untuk membuat bidang benar-benar pribadi."
Scott Rippey
31

Ketika saya membaca ini, itu terdengar seperti tantangan yang sulit jadi saya memutuskan untuk mencari cara. Apa yang saya temukan adalah CRAAAAZY tetapi benar-benar berfungsi.

Pertama, saya mencoba mendefinisikan kelas dalam fungsi langsung sehingga Anda akan memiliki akses ke beberapa properti pribadi dari fungsi itu. Ini berfungsi dan memungkinkan Anda untuk mendapatkan beberapa data pribadi, namun, jika Anda mencoba mengatur data pribadi Anda akan segera menemukan bahwa semua objek akan berbagi nilai yang sama.

var SharedPrivateClass = (function() { // use immediate function
    // our private data
    var private = "Default";

    // create the constructor
    function SharedPrivateClass() {}

    // add to the prototype
    SharedPrivateClass.prototype.getPrivate = function() {
        // It has access to private vars from the immediate function!
        return private;
    };

    SharedPrivateClass.prototype.setPrivate = function(value) {
        private = value;
    };

    return SharedPrivateClass;
})();

var a = new SharedPrivateClass();
console.log("a:", a.getPrivate()); // "a: Default"

var b = new SharedPrivateClass();
console.log("b:", b.getPrivate()); // "b: Default"

a.setPrivate("foo"); // a Sets private to "foo"
console.log("a:", a.getPrivate()); // "a: foo"
console.log("b:", b.getPrivate()); // oh no, b.getPrivate() is "foo"!

console.log(a.hasOwnProperty("getPrivate")); // false. belongs to the prototype
console.log(a.private); // undefined

// getPrivate() is only created once and instanceof still works
console.log(a.getPrivate === b.getPrivate);
console.log(a instanceof SharedPrivateClass);
console.log(b instanceof SharedPrivateClass);

Ada banyak kasus di mana ini akan memadai seperti jika Anda ingin memiliki nilai konstan seperti nama acara yang dibagikan di antara instance. Tetapi pada dasarnya, mereka bertindak seperti variabel statis pribadi.

Jika Anda benar-benar membutuhkan akses ke variabel dalam namespace pribadi dari dalam metode Anda yang ditentukan pada prototipe, Anda dapat mencoba pola ini.

var PrivateNamespaceClass = (function() { // immediate function
    var instance = 0, // counts the number of instances
        defaultName = "Default Name",  
        p = []; // an array of private objects

    // create the constructor
    function PrivateNamespaceClass() {
        // Increment the instance count and save it to the instance. 
        // This will become your key to your private space.
        this.i = instance++; 
        
        // Create a new object in the private space.
        p[this.i] = {};
        // Define properties or methods in the private space.
        p[this.i].name = defaultName;
        
        console.log("New instance " + this.i);        
    }

    PrivateNamespaceClass.prototype.getPrivateName = function() {
        // It has access to the private space and it's children!
        return p[this.i].name;
    };
    PrivateNamespaceClass.prototype.setPrivateName = function(value) {
        // Because you use the instance number assigned to the object (this.i)
        // as a key, the values set will not change in other instances.
        p[this.i].name = value;
        return "Set " + p[this.i].name;
    };

    return PrivateNamespaceClass;
})();

var a = new PrivateNamespaceClass();
console.log(a.getPrivateName()); // Default Name

var b = new PrivateNamespaceClass();
console.log(b.getPrivateName()); // Default Name

console.log(a.setPrivateName("A"));
console.log(b.setPrivateName("B"));
console.log(a.getPrivateName()); // A
console.log(b.getPrivateName()); // B

// private objects are not accessible outside the PrivateNamespaceClass function
console.log(a.p);

// the prototype functions are not re-created for each instance
// and instanceof still works
console.log(a.getPrivateName === b.getPrivateName);
console.log(a instanceof PrivateNamespaceClass);
console.log(b instanceof PrivateNamespaceClass);

Saya suka umpan balik dari siapa pun yang melihat kesalahan dengan cara melakukannya.

Mims H. Wright
sumber
4
Saya kira satu kekhawatiran potensial adalah bahwa setiap instance dapat mengakses instance pribadi vars lainnya dengan menggunakan id instance yang berbeda. Belum tentu hal yang buruk ...
Mims H. Wright
15
Anda mendefinisikan kembali fungsi prototipe pada setiap panggilan konstruktor
Lu4
10
@ Lu4 Saya tidak yakin itu benar. Konstruktor dikembalikan dari dalam penutupan; satu-satunya waktu fungsi prototipe didefinisikan adalah yang pertama kali, di mana ekspresi fungsi segera dipanggil. Masalah privasi yang disebutkan di atas selain, ini terlihat baik bagi saya (sekilas).
Guypursey
1
@ MimsH. Oke bahasa lain memungkinkan akses ke objek privat lain dari kelas yang sama , tetapi hanya jika Anda memiliki referensi ke mereka. Untuk memungkinkan ini, Anda bisa menyembunyikan privat di belakang fungsi yang mengambil pointer objek sebagai kunci (seperti yang terlampir pada ID). Dengan begitu Anda hanya memiliki akses ke data pribadi objek yang Anda ketahui, yang lebih sesuai dengan pelingkupan dalam bahasa lain. Namun, implementasi ini menyoroti masalah yang lebih dalam dengan ini. Objek pribadi tidak akan pernah Dikumpulkan Sampah sampai fungsi Konstruktor.
Thomas Nadin
3
Saya ingin menyebutkan bahwa itelah ditambahkan ke semua instance. Jadi itu tidak sepenuhnya "transparan", dan imasih bisa dirusak.
Scott Rippey
18

lihat halaman Doug Crockford tentang ini . Anda harus melakukannya secara tidak langsung dengan sesuatu yang dapat mengakses ruang lingkup variabel pribadi.

contoh lain:

Incrementer = function(init) {
  var counter = init || 0;  // "counter" is a private variable
  this._increment = function() { return counter++; }
  this._set = function(x) { counter = x; }
}
Incrementer.prototype.increment = function() { return this._increment(); }
Incrementer.prototype.set = function(x) { return this._set(x); }

gunakan case:

js>i = new Incrementer(100);
[object Object]
js>i.increment()
100
js>i.increment()
101
js>i.increment()
102
js>i.increment()
103
js>i.set(-44)
js>i.increment()
-44
js>i.increment()
-43
js>i.increment()
-42
Jason S
sumber
47
Contoh ini tampaknya merupakan praktik yang mengerikan. Inti dari menggunakan metode prototipe adalah agar Anda tidak harus membuat yang baru untuk setiap contoh. Anda tetap melakukannya. Untuk setiap metode yang Anda buat yang lain.
Kir
2
@ArmedMonkey Konsepnya terlihat bagus, tetapi setuju bahwa ini adalah contoh buruk karena fungsi prototipe yang ditampilkan sepele. Jika fungsi prototipe adalah fungsi yang jauh lebih lama yang membutuhkan akses get / set sederhana ke variabel 'pribadi', itu masuk akal.
panekuk
9
Kenapa repot repot mengekspos _setvia set? Mengapa tidak menyebutkannya saja set?
Scott Rippey
15

Saya menyarankan itu mungkin akan menjadi ide yang baik untuk menggambarkan "memiliki tugas prototipe dalam konstruktor" sebagai anti-pola Javascript. Pikirkan tentang itu. Itu terlalu berisiko.

Apa yang sebenarnya Anda lakukan di sana pada pembuatan objek kedua (yaitu b) adalah mendefinisikan kembali fungsi prototipe untuk semua objek yang menggunakan prototipe itu. Ini akan secara efektif mengatur ulang nilai untuk objek dalam contoh Anda. Ini akan berfungsi jika Anda ingin variabel yang dibagikan dan jika Anda membuat semua instance objek di depan, tetapi rasanya terlalu berisiko.

Saya menemukan bug di beberapa Javascript yang saya kerjakan baru-baru ini karena anti-pola yang tepat ini. Itu mencoba mengatur drag and drop handler pada objek tertentu yang sedang dibuat tetapi malah melakukannya untuk semua contoh. Tidak baik.

Solusi Doug Crockford adalah yang terbaik.

Lance Ewing
sumber
10

@ Kai

Itu tidak akan berhasil. Jika kamu melakukan

var t2 = new TestClass();

kemudian t2.prototypeHelloakan mengakses bagian pribadi t.

@AnglesCrimes

Kode sampel berfungsi dengan baik, tetapi sebenarnya membuat anggota pribadi "statis" dibagikan oleh semua contoh. Ini mungkin bukan solusi yang dicari oleh morgancodes.

Sejauh ini saya belum menemukan cara yang mudah dan bersih untuk melakukan ini tanpa memperkenalkan fungsi pembersihan dan hash pribadi. Fungsi anggota pribadi dapat disimulasikan sampai batas tertentu:

(function() {
    function Foo() { ... }
    Foo.prototype.bar = function() {
       privateFoo.call(this, blah);
    };
    function privateFoo(blah) { 
        // scoped to the instance by passing this to call 
    }

    window.Foo = Foo;
}());
Tim
sumber
Pahami poin Anda dengan jelas, tetapi bisakah Anda tolong jelaskan apa yang coba dilakukan cuplikan kode Anda?
Vishwanath
privateFoosepenuhnya pribadi dan dengan demikian tidak terlihat ketika mendapatkan new Foo(). Hanya bar()metode publik di sini, yang memiliki akses ke privateFoo. Anda bisa menggunakan mekanisme yang sama untuk variabel dan objek sederhana, namun Anda harus selalu ingat bahwa itu privatessebenarnya statis dan akan dibagikan oleh semua objek yang Anda buat.
Philzen
6

Iya itu mungkin. Pola desain PPF baru saja menyelesaikan ini.

PPF adalah singkatan dari Private Prototype Functions. PPF dasar menyelesaikan masalah ini:

  1. Fungsi prototipe mendapatkan akses ke data instance pribadi.
  2. Fungsi prototipe dapat dibuat pribadi.

Untuk yang pertama, cukup:

  1. Masukkan semua variabel instance pribadi yang ingin Anda dapat diakses dari fungsi prototipe di dalam wadah data terpisah, dan
  2. Berikan referensi ke wadah data ke semua fungsi prototipe sebagai parameter.

Sesederhana itu. Sebagai contoh:

// Helper class to store private data.
function Data() {};

// Object constructor
function Point(x, y)
{
  // container for private vars: all private vars go here
  // we want x, y be changeable via methods only
  var data = new Data;
  data.x = x;
  data.y = y;

  ...
}

// Prototype functions now have access to private instance data
Point.prototype.getX = function(data)
{
  return data.x;
}

Point.prototype.getY = function(data)
{
  return data.y;
}

...

Baca cerita selengkapnya di sini:

Pola Desain PPF

Edward
sumber
4
Jawaban khusus tautan hanya disukai pada SO. Tolong tunjukkan sebuah contoh.
Corey Adler
Artikel ini memiliki contoh di dalamnya, jadi silakan lihat di sana
Edward
5
Apa yang terjadi, jika suatu saat nanti situs itu mati? Bagaimana seharusnya seseorang melihat contoh itu? Kebijakan ini berlaku sehingga segala sesuatu yang bernilai dalam tautan dapat disimpan di sini, dan tidak harus bergantung pada situs web yang ini tidak berada di bawah kendali kami.
Corey Adler
3
@Edward, tautan Anda adalah bacaan yang menarik! Namun, menurut saya alasan utama untuk mengakses data pribadi menggunakan fungsi prototipikal, adalah untuk mencegah agar setiap objek menghabiskan memori dengan fungsi publik yang identik. Metode yang Anda gambarkan tidak menyelesaikan masalah ini, karena untuk penggunaan publik, fungsi prototipe perlu dibungkus dengan fungsi publik biasa. Saya kira polanya bisa berguna untuk menghemat memori jika Anda memiliki banyak ppf yang digabungkan dalam satu fungsi publik. Apakah Anda menggunakannya untuk hal lain?
Bersantap Filsuf
@DiningPhilosofer, terima kasih telah menghargai artikel saya. Ya, Anda benar, kami masih menggunakan fungsi instance. Tetapi idenya adalah untuk membuat mereka seringan mungkin dengan hanya memanggil kembali rekan-rekan PPF mereka yang melakukan semua pekerjaan berat. Akhirnya semua instance memanggil PPF yang sama (tentu saja melalui pembungkus), sehingga penghematan memori tertentu mungkin diharapkan. Pertanyaannya adalah berapa banyak. Saya berharap penghematan besar.
Edward
5

Anda benar-benar dapat mencapai ini dengan menggunakan Verifikasi Accessor :

(function(key, global) {
  // Creates a private data accessor function.
  function _(pData) {
    return function(aKey) {
      return aKey === key && pData;
    };
  }

  // Private data accessor verifier.  Verifies by making sure that the string
  // version of the function looks normal and that the toString function hasn't
  // been modified.  NOTE:  Verification can be duped if the rogue code replaces
  // Function.prototype.toString before this closure executes.
  function $(me) {
    if(me._ + '' == _asString && me._.toString === _toString) {
      return me._(key);
    }
  }
  var _asString = _({}) + '', _toString = _.toString;

  // Creates a Person class.
  var PersonPrototype = (global.Person = function(firstName, lastName) {
    this._ = _({
      firstName : firstName,
      lastName : lastName
    });
  }).prototype;
  PersonPrototype.getName = function() {
    var pData = $(this);
    return pData.firstName + ' ' + pData.lastName;
  };
  PersonPrototype.setFirstName = function(firstName) {
    var pData = $(this);
    pData.firstName = firstName;
    return this;
  };
  PersonPrototype.setLastName = function(lastName) {
    var pData = $(this);
    pData.lastName = lastName;
    return this;
  };
})({}, this);

var chris = new Person('Chris', 'West');
alert(chris.setFirstName('Christopher').setLastName('Webber').getName());

Contoh ini berasal dari posting saya tentang Fungsi Prototip & Data Pribadi dan dijelaskan secara lebih rinci di sana.

Chris West
sumber
1
Jawaban ini terlalu "pintar" untuk berguna, tapi saya suka jawaban menggunakan variabel terikat IFFE sebagai jabat tangan rahasia. Implementasi ini menggunakan terlalu banyak penutupan untuk berguna; titik memiliki metode prototipe didefinisikan adalah untuk mencegah pembangunan objek fungsi baru untuk setiap metode pada setiap objek.
greg.kindel
Pendekatan ini menggunakan kunci rahasia untuk mengidentifikasi metode prototipe mana yang dipercaya dan mana yang tidak. Namun, instance yang memvalidasi kunci, jadi kunci tersebut harus dikirim ke instance. Tapi kemudian, kode yang tidak dipercaya bisa memanggil metode tepercaya pada contoh palsu, yang akan mencuri kunci. Dan dengan kunci itu, buat metode baru yang akan dianggap dipercaya oleh contoh nyata. Jadi ini hanya privasi karena ketidakjelasan.
Oriol
4

Dalam JavaScript saat ini, saya cukup yakin bahwa ada satu dan hanya satu cara untuk memiliki negara pribadi , dapat diakses dari fungsi prototipe , tanpa menambahkan apa pun untuk publikthis . Jawabannya adalah dengan menggunakan pola "peta lemah".

Untuk meringkasnya: PersonKelas memiliki peta lemah tunggal, di mana kuncinya adalah contoh Person, dan nilainya adalah objek polos yang digunakan untuk penyimpanan pribadi.

Berikut ini adalah contoh yang berfungsi penuh: (bermain di http://jsfiddle.net/ScottRippey/BLNVr/ )

var Person = (function() {
    var _ = weakMap();
    // Now, _(this) returns an object, used for private storage.
    var Person = function(first, last) {
        // Assign private storage:
        _(this).firstName = first;
        _(this).lastName = last;
    }
    Person.prototype = {
        fullName: function() {
            // Retrieve private storage:
            return _(this).firstName + _(this).lastName;
        },
        firstName: function() {
            return _(this).firstName;
        },
        destroy: function() {
            // Free up the private storage:
            _(this, true);
        }
    };
    return Person;
})();

function weakMap() {
    var instances=[], values=[];
    return function(instance, destroy) {
        var index = instances.indexOf(instance);
        if (destroy) {
            // Delete the private state:
            instances.splice(index, 1);
            return values.splice(index, 1)[0];
        } else if (index === -1) {
            // Create the private state:
            instances.push(instance);
            values.push({});
            return values[values.length - 1];
        } else {
            // Return the private state:
            return values[index];
        }
    };
}

Seperti saya katakan, ini benar-benar satu-satunya cara untuk mencapai semua 3 bagian.

Namun, ada dua peringatan. Pertama, ini membutuhkan kinerja - setiap kali Anda mengakses data pribadi, ini adalah O(n)operasi, di mana njumlah instance. Jadi Anda tidak akan mau melakukan ini jika Anda memiliki banyak contoh. Kedua, ketika Anda selesai dengan sebuah instance, Anda harus menelepon destroy; jika tidak, instance dan data tidak akan menjadi sampah yang dikumpulkan, dan Anda akan berakhir dengan kebocoran memori.

Dan itulah sebabnya jawaban awal saya, "Anda seharusnya tidak" , adalah sesuatu yang ingin saya pertahankan.

Scott Rippey
sumber
Jika Anda tidak secara eksplisit menghancurkan instance Person sebelum keluar dari ruang lingkup, bukankah peta yang lemah menyimpan referensi untuknya sehingga Anda akan memiliki kebocoran memori? Saya datang dengan pola untuk dilindungi sebagai contoh lain dari Person dapat mengakses variabel dan yang mewarisi dari Person dapat. Hanya mengotak-atiknya sehingga tidak yakin apakah ada kelebihan dis selain pemrosesan tambahan (tidak terlihat sebanyak mengakses privat ) stackoverflow.com/a/21800194/1641941 Mengembalikan benda pribadi / dilindungi adalah hal yang menyebalkan karena memanggil kode kemudian dapat bermutasi pribadi / dilindungi.
HMR
2
@HMR Ya, Anda harus secara eksplisit menghancurkan data pribadi. Saya akan menambahkan peringatan ini untuk jawaban saya.
Scott Rippey
3

Ada cara yang lebih sederhana dengan memanfaatkan penggunaan binddan callmetode.

Dengan mengatur variabel pribadi ke objek, Anda dapat memanfaatkan cakupan objek itu.

Contoh

function TestClass (value) {
    // The private value(s)
    var _private = {
        value: value
    };

    // `bind` creates a copy of `getValue` when the object is instantiated
    this.getValue = TestClass.prototype.getValue.bind(_private);

    // Use `call` in another function if the prototype method will possibly change
    this.getValueDynamic = function() {
        return TestClass.prototype.getValue.call(_private);
    };
};

TestClass.prototype.getValue = function() {
    return this.value;
};

Metode ini bukan tanpa kekurangan. Karena konteks cakupan ditimpa secara efektif, Anda tidak memiliki akses di luar _privateobjek. Namun, bukan tidak mungkin untuk tetap memberikan akses ke cakupan objek instance. Anda bisa meneruskan dalam konteks objek ( this) sebagai argumen kedua bindatau callmasih memiliki akses ke nilai-nilai publik itu dalam fungsi prototipe.

Mengakses nilai-nilai publik

function TestClass (value) {
    var _private = {
        value: value
    };

    this.message = "Hello, ";

    this.getMessage = TestClass.prototype.getMessage.bind(_private, this);

}

TestClass.prototype.getMessage = function(_public) {

    // Can still access passed in arguments
    // e.g. – test.getValues('foo'), 'foo' is the 2nd argument to the method
    console.log([].slice.call(arguments, 1));
    return _public.message + this.value;
};

var test = new TestClass("World");
test.getMessage(1, 2, 3); // [1, 2, 3]         (console.log)
                          // => "Hello, World" (return value)

test.message = "Greetings, ";
test.getMessage(); // []                    (console.log)
                   // => "Greetings, World" (return value)
terima kasih
sumber
2
Mengapa seseorang membuat salinan dari metode prototipe sebagai lawan dari hanya membuat metode yang sudah ada di tempat pertama?
naksir
3

Cobalah!

    function Potatoe(size) {
    var _image = new Image();
    _image.src = 'potatoe_'+size+'.png';
    function getImage() {
        if (getImage.caller == null || getImage.caller.owner != Potatoe.prototype)
            throw new Error('This is a private property.');
        return _image;
    }
    Object.defineProperty(this,'image',{
        configurable: false,
        enumerable: false,
        get : getImage          
    });
    Object.defineProperty(this,'size',{
        writable: false,
        configurable: false,
        enumerable: true,
        value : size            
    });
}
Potatoe.prototype.draw = function(ctx,x,y) {
    //ctx.drawImage(this.image,x,y);
    console.log(this.image);
}
Potatoe.prototype.draw.owner = Potatoe.prototype;

var pot = new Potatoe(32);
console.log('Potatoe size: '+pot.size);
try {
    console.log('Potatoe image: '+pot.image);
} catch(e) {
    console.log('Oops: '+e);
}
pot.draw();
AlanNLohse
sumber
1
Ini bergantung pada caller, yang merupakan ekstensi bergantung pada implementasi tidak diizinkan dalam mode ketat.
Oriol
1

Inilah yang saya pikirkan.

(function () {
    var staticVar = 0;
    var yrObj = function () {
        var private = {"a":1,"b":2};
        var MyObj = function () {
            private.a += staticVar;
            staticVar++;
        };
        MyObj.prototype = {
            "test" : function () {
                console.log(private.a);
            }
        };

        return new MyObj;
    };
    window.YrObj = yrObj;
}());

var obj1 = new YrObj;
var obj2 = new YrObj;
obj1.test(); // 1
obj2.test(); // 2

masalah utama dengan implementasi ini adalah ia mendefinisikan ulang prototipe pada setiap instanciation.

Xeltor
sumber
Menarik, saya sangat suka upaya dan memikirkan hal yang sama, tetapi Anda benar bahwa mendefinisikan kembali fungsi prototipe pada setiap instantiation adalah batasan yang cukup besar. Ini bukan hanya karena siklus CPU yang terbuang, tetapi karena jika Anda pernah mengubah prototipe nanti, itu akan mendapatkan "reset" kembali ke keadaan semula sebagaimana didefinisikan dalam konstruktor pada instantiasi berikutnya: /
Niko Bellic
1
Ini tidak hanya mendefinisikan ulang prototipe, itu mendefinisikan konstruktor baru untuk setiap contoh. Jadi "instance" bukan lagi instance dari kelas yang sama.
Oriol
1

Ada cara yang sangat sederhana untuk melakukan ini

function SharedPrivate(){
  var private = "secret";
  this.constructor.prototype.getP = function(){return private}
  this.constructor.prototype.setP = function(v){ private = v;}
}

var o1 = new SharedPrivate();
var o2 = new SharedPrivate();

console.log(o1.getP()); // secret
console.log(o2.getP()); // secret
o1.setP("Pentax Full Frame K1 is on sale..!");
console.log(o1.getP()); // Pentax Full Frame K1 is on sale..!
console.log(o2.getP()); // Pentax Full Frame K1 is on sale..!
o2.setP("And it's only for $1,795._");
console.log(o1.getP()); // And it's only for $1,795._

Prototipe JavaScript berwarna emas.

Redu
sumber
2
Saya percaya lebih baik tidak menggunakan prototipe dalam fungsi konstruktor karena akan membuat fungsi baru setiap kali instance baru dibuat.
whamsicore
@whamsicore Ya benar tetapi dalam kasus ini sangat penting karena untuk setiap objek tunggal instantiated kita harus mengatur penutupan bersama. Itulah alasan mengapa definisi fungsi berada di dalam konstruktor dan kita harus merujuknya SharedPrivate.prototypekarena this.constructor.prototypeini bukan masalah besar untuk mendefinisikan ulang getP dan setP beberapa kali ...
Redu
1

Saya terlambat ke pesta, tapi saya pikir saya bisa berkontribusi. Di sini, 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, tetapi saya pikir ini sangat kuat.

guitarino
sumber
0

Tidak bisakah Anda menempatkan variabel dalam cakupan yang lebih tinggi?

(function () {
    var privateVariable = true;

    var MyClass = function () {
        if (privateVariable) console.log('readable from private scope!');
    };

    MyClass.prototype.publicMethod = function () {
        if (privateVariable) console.log('readable from public scope!');
    };
}))();
Ev Haus
sumber
4
Kemudian variabel dibagi antara semua instance MyClass.
naksir
0

Anda juga dapat mencoba menambahkan metode tidak secara langsung pada prototipe, tetapi pada fungsi konstruktor seperti ini:

var MyArray = function() {
    var array = [];

    this.add = MyArray.add.bind(null, array);
    this.getAll = MyArray.getAll.bind(null, array);
}

MyArray.add = function(array, item) {
    array.push(item);
}
MyArray.getAll = function(array) {
    return array;
}

var myArray1 = new MyArray();
myArray1.add("some item 1");
console.log(myArray1.getAll()); // ['some item 1']
var myArray2 = new MyArray();
myArray2.add("some item 2");
console.log(myArray2.getAll()); // ['some item 2']
console.log(myArray1.getAll()); // ['some item 2'] - FINE!
Maciej Dzikowicki
sumber
0

Inilah sesuatu yang saya buat ketika mencoba menemukan solusi paling sederhana untuk masalah ini, mungkin itu bisa bermanfaat bagi seseorang. Saya baru mengenal javascript, jadi mungkin ada beberapa masalah dengan kode tersebut.

// pseudo-class definition scope
(function () {

    // this is used to identify 'friend' functions defined within this scope,
    // while not being able to forge valid parameter for GetContext() 
    // to gain 'private' access from outside
    var _scope = new (function () { })();
    // -----------------------------------------------------------------

    // pseudo-class definition
    this.Something = function (x) {

        // 'private' members are wrapped into context object,
        // it can be also created with a function
        var _ctx = Object.seal({

            // actual private members
            Name: null,
            Number: null,

            Somefunc: function () {
                console.log('Something(' + this.Name + ').Somefunc(): number = ' + this.Number);
            }
        });
        // -----------------------------------------------------------------

        // function below needs to be defined in every class
        // to allow limited access from prototype
        this.GetContext = function (scope) {

            if (scope !== _scope) throw 'access';
            return _ctx;
        }
        // -----------------------------------------------------------------

        {
            // initialization code, if any
            _ctx.Name = (x !== 'undefined') ? x : 'default';
            _ctx.Number = 0;

            Object.freeze(this);
        }
    }
    // -----------------------------------------------------------------

    // prototype is defined only once
    this.Something.prototype = Object.freeze({

        // public accessors for 'private' field
        get Number() { return this.GetContext(_scope).Number; },
        set Number(v) { this.GetContext(_scope).Number = v; },

        // public function making use of some private fields
        Test: function () {

            var _ctx = this.GetContext(_scope);
            // access 'private' field
            console.log('Something(' + _ctx.Name + ').Test(): ' + _ctx.Number);
            // call 'private' func
            _ctx.Somefunc();
        }
    });
    // -----------------------------------------------------------------

    // wrap is used to hide _scope value and group definitions
}).call(this);

function _A(cond) { if (cond !== true) throw new Error('assert failed'); }
// -----------------------------------------------------------------

function test_smth() {

    console.clear();

    var smth1 = new Something('first'),
      smth2 = new Something('second');

    //_A(false);
    _A(smth1.Test === smth2.Test);

    smth1.Number = 3;
    smth2.Number = 5;
    console.log('smth1.Number: ' + smth1.Number + ', smth2.Number: ' + smth2.Number);

    smth1.Number = 2;
    smth2.Number = 6;

    smth1.Test();
    smth2.Test();

    try {
        var ctx = smth1.GetContext();
    } catch (err) {
        console.log('error: ' + err);
    }
}

test_smth();
V.Mihaly4
sumber
0

Saya menghadapi pertanyaan yang sama persis hari ini dan setelah menguraikan respons kelas satu Scott Rippey, saya datang dengan solusi yang sangat sederhana (IMHO) yang keduanya kompatibel dengan ES5 dan efisien, juga aman untuk nama clash (menggunakan _private tampaknya tidak aman) .

/*jslint white: true, plusplus: true */

 /*global console */

var a, TestClass = (function(){
    "use strict";
    function PrefixedCounter (prefix) {
        var counter = 0;
        this.count = function () {
            return prefix + (++counter);
        };
    }
    var TestClass = (function(){
        var cls, pc = new PrefixedCounter("_TestClass_priv_")
        , privateField = pc.count()
        ;
        cls = function(){
            this[privateField] = "hello";
            this.nonProtoHello = function(){
                console.log(this[privateField]);
            };
        };
        cls.prototype.prototypeHello = function(){
            console.log(this[privateField]);
        };
        return cls;
    }());
    return TestClass;
}());

a = new TestClass();
a.nonProtoHello();
a.prototypeHello();

Diuji dengan ringojs dan nodejs. Saya ingin membaca pendapat Anda.

alexgirao
sumber
Berikut ini adalah referensi: periksa bagian 'Selangkah lebih dekat'. philipwalton.com/articles/…
jimasun
0
var getParams = function(_func) {
  res = _func.toString().split('function (')[1].split(')')[0].split(',')
  return res
}

function TestClass(){

  var private = {hidden: 'secret'}
  //clever magic accessor thing goes here
  if ( !(this instanceof arguments.callee) ) {
    for (var key in arguments) {
      if (typeof arguments[key] == 'function') {
        var keys = getParams(arguments[key])
        var params = []
        for (var i = 0; i <= keys.length; i++) {
          if (private[keys[i]] != undefined) {
            params.push(private[keys[i]])
          }
        }
        arguments[key].apply(null,params)
      }
    }
  }
}


TestClass.prototype.test = function(){
  var _hidden; //variable I want to get
  TestClass(function(hidden) {_hidden = hidden}) //invoke magic to get
};

new TestClass().test()

Bagaimana dengan ini? Menggunakan accessor pribadi. Hanya memungkinkan Anda untuk mendapatkan variabel meskipun tidak mengaturnya, tergantung pada use case.

dylan0150
sumber
Ini tidak menjawab pertanyaan dengan cara yang bermanfaat. mengapa kamu percaya ini adalah jawabannya? bagaimana cara kerjanya? Memberitahu seseorang untuk mengubah kode mereka tanpa konteks atau makna apa pun tidak membantu mereka mengetahui kesalahan mereka.
GrumpyCrouton
Dia ingin cara untuk mengakses variabel pribadi tersembunyi dari Kelas melalui prototipe tanpa harus membuat variabel tersembunyi pada setiap instance kelas. Kode di atas adalah contoh metode melakukan hal itu. Bagaimana itu bukan jawaban untuk pertanyaan itu?
dylan0150
Saya tidak mengatakan itu bukan jawaban untuk pertanyaan itu. Saya mengatakan itu bukan jawaban yang berguna , karena itu tidak membantu siapa pun belajar. Anda harus menjelaskan kode Anda, mengapa itu bekerja, mengapa itu cara yang tepat untuk melakukannya. Jika saya adalah penulis pertanyaan, saya tidak akan menerima jawaban Anda karena itu tidak mendorong pembelajaran, itu tidak mengajari saya apa yang saya lakukan salah atau apa yang dilakukan kode yang diberikan atau cara kerjanya.
GrumpyCrouton
0

Saya punya satu solusi, tetapi saya tidak yakin itu tanpa cacat.

Agar berfungsi, Anda harus menggunakan struktur berikut:

  1. Gunakan 1 objek pribadi yang berisi semua variabel pribadi.
  2. Gunakan fungsi 1 instance.
  3. Menerapkan penutupan pada konstruktor dan semua fungsi prototipe.
  4. Setiap instance yang dibuat dilakukan di luar penutupan yang ditentukan.

Ini kodenya:

var TestClass = 
(function () {
    // difficult to be guessed.
    var hash = Math.round(Math.random() * Math.pow(10, 13) + + new Date());
    var TestClass = function () {
        var privateFields = {
            field1: 1,
            field2: 2
        };
        this.getPrivateFields = function (hashed) {
            if(hashed !== hash) {
                throw "Cannot access private fields outside of object.";
                // or return null;
            }
            return privateFields;
        };
    };

    TestClass.prototype.prototypeHello = function () {
        var privateFields = this.getPrivateFields(hash);
        privateFields.field1 = Math.round(Math.random() * 100);
        privateFields.field2 = Math.round(Math.random() * 100);
    };

    TestClass.prototype.logField1 = function () {
        var privateFields = this.getPrivateFields(hash);
        console.log(privateFields.field1);
    };

    TestClass.prototype.logField2 = function () {
        var privateFields = this.getPrivateFields(hash);
        console.log(privateFields.field2);
    };

    return TestClass;
})();

Cara kerjanya adalah menyediakan fungsi instance "this.getPrivateFields" untuk mengakses objek variabel privat "privateFields", tetapi fungsi ini hanya akan mengembalikan objek "privateFields" di dalam penutupan utama yang ditentukan (juga fungsi prototipe menggunakan "this.getPrivateFields "perlu didefinisikan di dalam penutupan ini).

Hash yang dihasilkan selama runtime dan sulit ditebak digunakan sebagai parameter untuk memastikan bahwa meskipun "getPrivateFields" disebut di luar lingkup penutupan tidak akan mengembalikan objek "privateFields".

Kekurangannya adalah kita tidak bisa memperpanjang TestClass dengan lebih banyak fungsi prototipe di luar penutupan.

Berikut ini beberapa kode tes:

var t1 = new TestClass();
console.log('Initial t1 field1 is: ');
t1.logField1();
console.log('Initial t1 field2 is: ');
t1.logField2();
t1.prototypeHello();
console.log('t1 field1 is now: ');
t1.logField1();
console.log('t1 field2 is now: ');
t1.logField2();
var t2 = new TestClass();
console.log('Initial t2 field1 is: ');
t2.logField1();
console.log('Initial t2 field2 is: ');
t2.logField2();
t2.prototypeHello();
console.log('t2 field1 is now: ');
t2.logField1();
console.log('t2 field2 is now: ');
t2.logField2();

console.log('t1 field1 stays: ');
t1.logField1();
console.log('t1 field2 stays: ');
t1.logField2();

t1.getPrivateFields(11233);

EDIT: Menggunakan metode ini, juga dimungkinkan untuk "mendefinisikan" fungsi pribadi.

TestClass.prototype.privateFunction = function (hashed) {
    if(hashed !== hash) {
        throw "Cannot access private function.";
    }
};

TestClass.prototype.prototypeHello = function () {
    this.privateFunction(hash);
};
Jannes Botis
sumber
0

Bermain-main dengan ini hari ini dan ini adalah satu-satunya solusi yang bisa kutemukan tanpa menggunakan Simbol. Hal terbaik tentang ini adalah semuanya bisa benar-benar pribadi.

Solusinya didasarkan di sekitar loader modul homegrown yang pada dasarnya menjadi mediator untuk cache penyimpanan pribadi (menggunakan peta yang lemah).

   const loader = (function() {
        function ModuleLoader() {}

    //Static, accessible only if truly needed through obj.constructor.modules
    //Can also be made completely private by removing the ModuleLoader prefix.
    ModuleLoader.modulesLoaded = 0;
    ModuleLoader.modules = {}

    ModuleLoader.prototype.define = function(moduleName, dModule) {
        if (moduleName in ModuleLoader.modules) throw new Error('Error, duplicate module');

        const module = ModuleLoader.modules[moduleName] = {}

        module.context = {
            __moduleName: moduleName,
            exports: {}
        }

        //Weak map with instance as the key, when the created instance is garbage collected or goes out of scope this will be cleaned up.
        module._private = {
            private_sections: new WeakMap(),
            instances: []
        };

        function private(action, instance) {
            switch (action) {
                case "create":
                    if (module._private.private_sections.has(instance)) throw new Error('Cannot create private store twice on the same instance! check calls to create.')
                    module._private.instances.push(instance);
                    module._private.private_sections.set(instance, {});
                    break;
                case "delete":
                    const index = module._private.instances.indexOf(instance);
                    if (index == -1) throw new Error('Invalid state');
                    module._private.instances.slice(index, 1);
                    return module._private.private_sections.delete(instance);
                    break;
                case "get":
                    return module._private.private_sections.get(instance);
                    break;
                default:
                    throw new Error('Invalid action');
                    break;
            }
        }

        dModule.call(module.context, private);
        ModuleLoader.modulesLoaded++;
    }

    ModuleLoader.prototype.remove = function(moduleName) {
        if (!moduleName in (ModuleLoader.modules)) return;

        /*
            Clean up as best we can.
        */
        const module = ModuleLoader.modules[moduleName];
        module.context.__moduleName = null;
        module.context.exports = null;
        module.cotext = null;
        module._private.instances.forEach(function(instance) { module._private.private_sections.delete(instance) });
        for (let i = 0; i < module._private.instances.length; i++) {
            module._private.instances[i] = undefined;
        }
        module._private.instances = undefined;
        module._private = null;
        delete ModuleLoader.modules[moduleName];
        ModuleLoader.modulesLoaded -= 1;
    }


    ModuleLoader.prototype.require = function(moduleName) {
        if (!(moduleName in ModuleLoader.modules)) throw new Error('Module does not exist');

        return ModuleLoader.modules[moduleName].context.exports;
    }



     return new ModuleLoader();
    })();

    loader.define('MyModule', function(private_store) {
        function MyClass() {
            //Creates the private storage facility. Called once in constructor.
            private_store("create", this);


            //Retrieve the private storage object from the storage facility.
            private_store("get", this).no = 1;
        }

        MyClass.prototype.incrementPrivateVar = function() {
            private_store("get", this).no += 1;
        }

        MyClass.prototype.getPrivateVar = function() {
            return private_store("get", this).no;
        }

        this.exports = MyClass;
    })

    //Get whatever is exported from MyModule
    const MyClass = loader.require('MyModule');

    //Create a new instance of `MyClass`
    const myClass = new MyClass();

    //Create another instance of `MyClass`
    const myClass2 = new MyClass();

    //print out current private vars
    console.log('pVar = ' + myClass.getPrivateVar())
    console.log('pVar2 = ' + myClass2.getPrivateVar())

    //Increment it
    myClass.incrementPrivateVar()

    //Print out to see if one affected the other or shared
    console.log('pVar after increment = ' + myClass.getPrivateVar())
    console.log('pVar after increment on other class = ' + myClass2.getPrivateVar())

    //Clean up.
    loader.remove('MyModule')
Eladian
sumber
0

Saya tahu ini sudah lebih dari 1 dekade sejak ditanya, tetapi saya hanya memikirkan ini untuk kali ke-9 dalam kehidupan programmer saya, dan menemukan solusi yang mungkin saya tidak tahu apakah saya sepenuhnya suka belum . Saya belum pernah melihat metodologi ini didokumentasikan sebelumnya, jadi saya akan menamainya "pola dolar privat / publik" atau pola _ $ / $ .

var ownFunctionResult = this.$("functionName"[, arg1[, arg2 ...]]);
var ownFieldValue = this._$("fieldName"[, newValue]);

var objectFunctionResult = objectX.$("functionName"[, arg1[, arg2 ...]]);

//Throws an exception. objectX._$ is not defined
var objectFieldValue = objectX._$("fieldName"[, newValue]);

Konsep ini menggunakan fungsi ClassDefinition yang mengembalikan fungsi Konstruktor yang mengembalikan objek Interface . Satu-satunya metode antarmuka adalah $yang menerima nameargumen untuk memanggil fungsi yang sesuai dalam objek konstruktor, setiap argumen tambahan yang diteruskan setelah namediteruskan dalam doa.

Fungsi helper yang didefinisikan secara global ClassValuesmenyimpan semua bidang dalam suatu objek sesuai kebutuhan. Ini mendefinisikan _$fungsi untuk mengaksesnya name. Ini mengikuti pola get / set pendek jadi jika valuedilewatkan, itu akan digunakan sebagai nilai variabel baru.

var ClassValues = function (values) {
  return {
    _$: function _$(name, value) {
      if (arguments.length > 1) {
        values[name] = value;
      }

      return values[name];
    }
  };
};

Fungsi yang didefinisikan secara global Interfacemengambil objek dan Valuesobjek untuk mengembalikan sebuah _interfacedengan satu fungsi tunggal $yang memeriksa objuntuk menemukan fungsi yang dinamai parameter namedan memanggilnya dengan valuessebagai objek scoped . Argumen tambahan yang diteruskan $akan diteruskan pada pemanggilan fungsi.

var Interface = function (obj, values, className) {
  var _interface = {
    $: function $(name) {
      if (typeof(obj[name]) === "function") {
        return obj[name].apply(values, Array.prototype.splice.call(arguments, 1));
      }

      throw className + "." + name + " is not a function.";
    }
  };

  //Give values access to the interface.
  values.$ = _interface.$;

  return _interface;
};

Dalam sampel di bawah ini, ClassXditugaskan untuk hasil ClassDefinition, yang merupakan Constructorfungsinya. Constructordapat menerima sejumlah argumen. Interfaceadalah apa yang didapat kode eksternal setelah memanggil konstruktor.

var ClassX = (function ClassDefinition () {
  var Constructor = function Constructor (valA) {
    return Interface(this, ClassValues({ valA: valA }), "ClassX");
  };

  Constructor.prototype.getValA = function getValA() {
    //private value access pattern to get current value.
    return this._$("valA");
  };

  Constructor.prototype.setValA = function setValA(valA) {
    //private value access pattern to set new value.
    this._$("valA", valA);
  };

  Constructor.prototype.isValAValid = function isValAValid(validMessage, invalidMessage) {
    //interface access pattern to call object function.
    var valA = this.$("getValA");

    //timesAccessed was not defined in constructor but can be added later...
    var timesAccessed = this._$("timesAccessed");

    if (timesAccessed) {
      timesAccessed = timesAccessed + 1;
    } else {
      timesAccessed = 1;
    }

    this._$("timesAccessed", timesAccessed);

    if (valA) {
      return "valA is " + validMessage + ".";
    }

    return "valA is " + invalidMessage + ".";
  };

  return Constructor;
}());

Tidak ada gunanya memiliki fungsi non-prototip Constructor, meskipun Anda bisa mendefinisikannya di badan fungsi konstruktor. Semua fungsi dipanggil dengan pola dolar publik this.$("functionName"[, param1[, param2 ...]]) . Nilai-nilai pribadi diakses dengan pola dolar pribadi this._$("valueName"[, replacingValue]); . Karena Interfacetidak memiliki definisi untuk _$, nilai tidak dapat diakses oleh objek eksternal. Karena masing-masing fungsi tubuh prototipe thisdiatur ke valuesobjek dalam fungsi $, Anda akan mendapatkan pengecualian jika Anda memanggil fungsi saudara Konstruktor secara langsung; pola _ $ / $ perlu diikuti dalam badan fungsi yang di-prototip juga. Penggunaan sampel di bawah ini.

var classX1 = new ClassX();
console.log("classX1." + classX1.$("isValAValid", "valid", "invalid"));
console.log("classX1.valA: " + classX1.$("getValA"));
classX1.$("setValA", "v1");
console.log("classX1." + classX1.$("isValAValid", "valid", "invalid"));
var classX2 = new ClassX("v2");
console.log("classX1.valA: " + classX1.$("getValA"));
console.log("classX2.valA: " + classX2.$("getValA"));
//This will throw an exception
//classX1._$("valA");

Dan output konsol.

classX1.valA is invalid.
classX1.valA: undefined
classX1.valA is valid.
classX1.valA: v1
classX2.valA: v2

Pola _ $ / $ memungkinkan privasi penuh nilai di kelas yang sepenuhnya di-prototyped. Saya tidak tahu apakah saya akan pernah menggunakan ini, atau apakah itu memiliki kekurangan, tapi hei, itu adalah teka-teki yang bagus!

JPortillo
sumber
0

ES6 WeakMaps

Dengan menggunakan pola sederhana berbasis ES6, WeakMaps dimungkinkan untuk mendapatkan variabel anggota pribadi, yang dapat dijangkau dari fungsi prototipe .

Catatan: Penggunaan WeakMaps menjamin keamanan terhadap kebocoran memori , dengan membiarkan Pengumpul Sampah mengidentifikasi dan membuang mesin virtual yang tidak digunakan.

// Create a private scope using an Immediately 
// Invoked Function Expression...
let Person = (function() {

    // Create the WeakMap that will hold each  
    // Instance collection's of private data
    let privateData = new WeakMap();
    
    // Declare the Constructor :
    function Person(name) {
        // Insert the private data in the WeakMap,
        // using 'this' as a unique acces Key
        privateData.set(this, { name: name });
    }
    
    // Declare a prototype method 
    Person.prototype.getName = function() {
        // Because 'privateData' is in the same 
        // scope, it's contents can be retrieved...
        // by using  again 'this' , as  the acces key 
        return privateData.get(this).name;
    };

    // return the Constructor
    return Person;
}());

Penjelasan lebih rinci tentang pola ini dapat ditemukan di sini

colxi
sumber
-1

Anda perlu mengubah 3 hal dalam kode Anda:

  1. Ganti var privateField = "hello"dengan this.privateField = "hello".
  2. Dalam prototipe ganti privateFielddengan this.privateField.
  3. Dalam non-prototipe juga ganti privateFielddengan this.privateField.

Kode akhir akan menjadi sebagai berikut:

TestClass = function(){
    this.privateField = "hello";
    this.nonProtoHello = function(){alert(this.privateField)};
}

TestClass.prototype.prototypeHello = function(){alert(this.privateField)};

var t = new TestClass();

t.prototypeHello()
A-Sharabiani
sumber
this.privateFieldtidak akan menjadi bidang pribadi. itu dapat diakses dari luar:t.privateField
V. Rubinetti
-2

Anda dapat menggunakan tugas prototipe dalam definisi konstruktor.

Variabel akan terlihat oleh metode yang ditambahkan prototipe tetapi semua contoh fungsi akan mengakses variabel SHARED yang sama.

function A()
{
  var sharedVar = 0;
  this.local = "";

  A.prototype.increment = function(lval)
  {    
    if (lval) this.local = lval;    
    alert((++sharedVar) + " while this.p is still " + this.local);
  }
}

var a = new A();
var b = new A();    
a.increment("I belong to a");
b.increment("I belong to b");
a.increment();
b.increment();

Semoga ini bermanfaat.

AngelsCrimes
sumber