Prototip tanımlı işlevlerden özel üye değişkenlere erişme


187

Prototip tanımlı yöntemler için “özel” değişkenleri (yapıcıda tanımlanan değişkenler) kullanılabilir hale getirmenin bir yolu var mı?

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

Bu çalışıyor:

t.nonProtoHello()

Ancak bu şunları yapmaz:

t.prototypeHello()

Yöntemleri yapıcı içinde tanımlamaya alışkınım, ancak birkaç nedenden dolayı bundan uzaklaşıyorum.



14
@ecampver, Bunun dışında 2 yıl önce istendi ....
Pacerier

Yanıtlar:


191

Hayır, bunu yapmanın bir yolu yok. Aslında bunun tam tersi de geçerlidir.

Yapıcı içinde tanımlanan yöntemler özel değişkenlere erişebilir, çünkü tüm fonksiyonlar tanımlandıkları kapsama erişebilir.

Bir prototipte tanımlanan yöntemler kurucu kapsamında tanımlanmaz ve kurucunun yerel değişkenlerine erişemez.

Yine de özel değişkenleriniz olabilir, ancak prototipte tanımlanan yöntemlerin bunlara erişmesini istiyorsanız this, prototip yöntemlerinin (diğer her şeyle birlikte) olduğu nesne üzerinde alıcıları ve ayarlayıcıları tanımlamanız gerekir. olacak erişebilirler. Örneğin:

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

14
"tam tersine kapsam", "friend" anahtar kelimesine sahip bir C ++ özelliğidir. Esasen herhangi bir fonksiyon prototipini arkadaş olarak tanımlamalıdır. Ne yazık ki bu kavram C ++ değil, JS değil :(
TWiStErRob

1
Bu yayını favoriler listemin en üstüne eklemek ve orada tutmak istiyorum.
Donato

2
Bunun amacını görmüyorum - sadece hiçbir şey yapmayan bir soyutlama katmanı ekliyorsunuz. Siz de sadece secretbir mülk yapabilirsiniz this. JavaScript, prototiplerle özel değişkenleri desteklemez çünkü prototipler 'site oluşturma' bağlamına değil, çağrı sitesi bağlamına bağlıdır.
nicodemus13

1
Neden sadece person.getSecret()o zaman değil ?
Fahmi

1
Neden bu kadar çok oy var? Bu, değişkeni özel yapmaz. Yukarıda belirtildiği gibi, person.getSecret () yöntemini kullanarak bu özel değişkene her yerden erişebilirsiniz.
alexr101

64

Güncelleme: ES6 ile daha iyi bir yol var:

Uzun lafın kısası, yeni Symbolözel alanlar oluşturmak için kullanabilirsiniz .
İşte harika bir açıklama: https://curiosity-driven.org/private-properties-in-javascript

Misal:

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

ES5'e sahip tüm modern tarayıcılar için:

Yalnızca Kapakları kullanabilirsiniz

Nesneleri oluşturmanın en basit yolu prototip mirastan tamamen kaçınmaktır. Özel değişkenleri ve genel işlevleri kapatma içinde tanımlamanız yeterlidir ve tüm genel yöntemlerin değişkenlere özel erişimi olacaktır.

Veya sadece Prototipler kullanabilirsiniz

JavaScript'te, prototip palet mirası öncelikle bir optimizasyondur . Birden çok eşgörünümün kendi yöntemlerine sahip olması yerine, prototip yöntemlerini paylaşmasına izin verir.
Tek dezavantajı, bir prototip fonksiyonu her çağrıldığında farklı thisolan tek şey olmasıdır.
Bu nedenle, tüm özel alanlara erişilebilir olmalı this, yani herkese açık olacaklar. Bu yüzden sadece adlandırma kurallarına sadık kalıyoruz_private alanlar .

Prototiplerle Kapakları karıştırmayı zahmet etmeyin

Bence yapmamalısın prototip yöntemleri ile kapatma değişkenleri karıştırın. Birini veya diğerini kullanmalısınız.

Özel bir değişkene erişmek için bir kapatma kullandığınızda, prototip yöntemleri değişkene erişemez. Yani, kapanışıthis , yani onu halka açık şekilde şu şekilde açığa çıkarıyorsun. Bu yaklaşımla kazanılacak çok az şey var.

Hangisini seçeceğim?

Gerçekten basit nesneler için, kapaklı düz bir nesne kullanın.

Prototippal mirasına ihtiyacınız varsa - miras, performans vb. - "_private" adlandırma kuralına sadık kalın ve kapaklarla uğraşmayın.

JS geliştiricilerinin alanları gerçekten özel hale getirmek için neden bu kadar çok uğraştığını anlamıyorum.


4
Ne yazık ki, _privateprototip mirasından yararlanmak istiyorsanız , adlandırma kuralı hala en iyi çözümdür.
ezmek

1
ES6, Symbolözel alanlar oluşturmak için mükemmel bir yol olan yeni bir konsepte sahip olacak . İşte harika bir açıklama: curiosity-driven.org/private-properties-in-javascript
Scott Rippey

1
Hayır, Symboltüm sınıfınızı kapsayan bir kapakta tutabilirsiniz . Bu şekilde, tüm prototip yöntemleri Sembol'ü kullanabilir, ancak hiçbir zaman sınıf dışında gösterilmez.
Scott Rippey

2
Bağladığınız makalede " Semboller özel adlara benzer, ancak özel adların aksine - gerçek gizlilik sağlamaz . " Etkin bir şekilde, örneğiniz varsa, sembollerini ile alabilirsiniz Object.getOwnPropertySymbols. Bu sadece gizlilikten mahremiyettir.
Oriol

2
@Oriol Evet, mahremiyet çok belirsiz. Hala semboller arasında yineleme yapmak mümkündür ve sembolün amacını çıkarırsınız toString. Bu, Java veya C # ... 'dan farklı değildir, özel üyeler hala yansıma yoluyla erişilebilir, ancak genellikle belirsizdir. Bu da benim son noktamı güçlendirecek, "JS geliştiricilerinin alanları gerçekten özel yapmak için neden bu kadar çok uğraştığını anlamıyorum."
Scott Rippey

31

Bunu okuduğumda, zor bir mücadele gibi geldi, bu yüzden bir yol bulmaya karar verdim. Ben geldi CRAAAAZY oldu ama tamamen işe yarıyor.

İlk olarak, bu işlevin bazı özel özelliklerine erişebilmeniz için sınıfı hemen bir işlevde tanımlamayı denedim. Bu işe yarar ve bazı özel veriler elde etmenizi sağlar, ancak özel verileri ayarlamaya çalışırsanız yakında tüm nesnelerin aynı değeri paylaşacağını göreceksiniz.

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

Örnekler arasında paylaşılan olay adları gibi sabit değerlere sahip olmak istiyorsanız, bunun yeterli olacağı birçok durum vardır. Ama esasen, özel statik değişkenler gibi davranırlar.

Prototipte tanımlanan yöntemlerinizden özel bir ad alanındaki değişkenlere kesinlikle erişmeniz gerekiyorsa, bu kalıbı deneyebilirsiniz.

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

Bu şekilde bir hata gören herkesin geri bildirimlerini çok isterim.


4
Potansiyel bir kaygı, herhangi bir örneğin özel bir değişkenin farklı bir örnek kimliği kullanarak özel değişkenlere erişebilmesidir. Mutlaka kötü bir şey değil ...
Mims H.Wright

15
Her yapıcı çağrısında prototip işlevlerini yeniden tanımlarsınız
Lu4

10
@ Lu4 Bunun doğru olduğundan emin değilim. Yapıcı bir kapak içinden döndürülür; prototip işlevlerinin tanımlandığı tek zaman ilk defa, hemen çağrılan işlev ifadesidir. Yukarıda bahsedilen gizlilik sorunları bir yana, bu benim için iyi görünüyor (ilk bakışta).
guypursey

1
Diğer diller , aynı sınıftaki diğer nesne ayrıcalıklarına erişime izin verir , ancak yalnızca bunlara referansınız olduğunda. Buna izin vermek için, nesne işaretçisini anahtar olarak alan bir işlevin arkasındaki ayrıcalıkları gizleyebilirsiniz (tanıtıcıya ek olarak). Bu şekilde, yalnızca bildiğiniz nesnelerin özel verilerine erişebilirsiniz, bu da diğer dillerde kapsam oluşturma ile daha uyumludur. Ancak, bu uygulama bununla ilgili daha derin bir soruna ışık tutmaktadır. Özel nesneler, Yapıcı işlevi olana kadar hiçbir zaman Çöp Toplanmaz.
Thomas Nadin

3
iTüm örneklere eklendiğinden bahsetmek istiyorum . Bu yüzden tamamen "şeffaf" değildir ve iyine de oynanabilir.
Scott Rippey

18

bu konuda Doug Crockford'un sayfasına bakınız . Bunu, özel değişkenin kapsamına erişebilecek bir şeyle dolaylı olarak yapmanız gerekir.

başka bir örnek:

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

kullanım durumu:

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

47
Bu örnek korkunç bir uygulama gibi görünüyor. Prototip yöntemlerini kullanmanın amacı, her örnek için yeni bir tane oluşturmak zorunda kalmamanızdır. Bunu yine de yapıyorsun. Başka bir yöntem oluşturduğunuz her yöntem için.
Kir

2
@ArmedMonkey Kavram sağlam görünüyor, ancak gösterilen prototip işlevleri önemsiz olduğu için bunun kötü bir örnek olduğu konusunda hemfikir. Prototip işlevleri çok daha uzun 'özel' değişkenlere basit alma / ayarlama erişimi gerektiren işlevler olsaydı bu mantıklı olurdu.
Gözleme

9
Neden bile açığa zahmet _setaracılığıyla set? Neden sadece setbaşlayalım?
Scott Rippey

15

Ben bir Javascript anti-desen olarak "bir yapıcıda bir prototip atama sahip" tanımlamak için iyi bir fikir olacağını düşünüyorum. Bunu düşün. Çok riskli.

Orada ikinci nesnenin (yani b) oluşturulması üzerinde yaptığınız şey, bu prototipi kullanan tüm nesneler için bu prototip işlevini yeniden tanımlamaktır. Bu, örneğinizdeki a nesnesinin değerini etkili bir şekilde sıfırlayacaktır. Paylaşılan bir değişken istiyorsanız ve tüm nesne örneklerini önünüzde oluşturursanız çalışır, ancak çok riskli hisseder.

Son zamanlarda üzerinde çalıştığım bazı Javascript'te bu tam anti-desen nedeniyle bir hata buldum. Oluşturulan belirli nesne üzerinde bir sürükle ve bırak işleyicisi ayarlamaya çalışıyordu, ancak bunun yerine tüm örnekler için yapıyordu. İyi değil.

Doug Crockford'un çözümü en iyisidir.


10

@Kai

Bu işe yaramaz. Yaparsan

var t2 = new TestClass();

o t2.prototypeHellozaman t özel bölümüne erişecek.

@AnglesCrimes

Örnek kod iyi çalışıyor, ancak aslında tüm örnekler tarafından paylaşılan bir "statik" özel üye oluşturur. Morganodların aradığı çözüm olmayabilir.

Şimdiye kadar özel bir karma ve ekstra temizleme işlevleri sunmadan bunu yapmanın kolay ve temiz bir yolunu bulamadım. Özel üye işlevi belirli bir ölçüde simüle edilebilir:

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

Puanlarınızı açıkça anladınız, ancak lütfen kod snippet'inizin ne yapmaya çalıştığını açıklayabilir misiniz?
Vishwanath

privateFootamamen özeldir ve dolayısıyla a new Foo(). Burada yalnızca bar()erişime açık olan herkese açık bir yöntemdir privateFoo. Basit değişkenler ve nesneler için aynı mekanizmayı kullanabilirsiniz, ancak privatesbunların aslında statik olduğunu ve oluşturduğunuz tüm nesneler tarafından paylaşılacağını daima aklınızda bulundurmanız gerekir .
Philzen

6

Evet mümkün. PPF tasarım deseni bunu çözer.

PPF, Özel Prototip İşlevleri anlamına gelir. Temel PPF şu sorunları çözer:

  1. Prototip işlevleri özel bulut sunucusu verilerine erişebilir.
  2. Prototip fonksiyonları özel yapılabilir.

İlk olarak, sadece:

  1. Prototip işlevlerinden erişilebilir olmasını istediğiniz tüm özel bulut sunucusu değişkenlerini ayrı bir veri kabına yerleştirin ve
  2. Parametre olarak tüm prototip işlevlerine veri taşıyıcısına bir başvuru iletin.

Bu kadar basit. Örneğin:

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

...

Hikayenin tamamını buradan okuyun:

PPF Tasarım Deseni


4
Yalnızca bağlantı yanıtı genellikle SO'da kaşlarını çatar. Lütfen bir örnek gösterin.
Corey Adler

Makalenin içinde örnekler var, lütfen buraya bakın
Edward

5
Bununla birlikte, bu sitenin bir noktasında daha sonra çökerse ne olur? Birisinin nasıl bir örnek görmesi gerekiyor? Politika mevcuttur, böylece bir bağlantıdaki değerli herhangi bir şey burada tutulabilir ve bunun bizim kontrolümüz altında olmadığı bir web sitesine güvenmek zorunda kalmazsınız.
Corey Adler

3
@Edward, bağlantınız ilginç bir okuma! Ancak bana öyle geliyor ki, prototip işlevleri kullanarak özel verilere erişmenin başlıca nedeni, her nesnenin aynı ortak işlevlerle bellek harcamasını önlemektir. Açıkladığınız yöntem bu sorunu çözmez, çünkü kamusal kullanım için prototip bir işlevin normal bir ortak işleve sarılması gerekir. Tek bir ortak işlevde birleştirilmiş ppf'leriniz varsa, modelin bellek tasarrufu için yararlı olabileceğini düşünüyorum. Onları başka bir şey için kullanıyor musunuz?
Yemek Filozofu

@ DiningPhilosofer, makalemi takdir ettiğiniz için teşekkür ederim. Evet, haklısınız, yine de örnek işlevlerini kullanıyoruz. Ancak fikir, tüm ağır işleri yapan PPF meslektaşlarını yeniden arayarak mümkün olduğunca hafif hale getirmektir. Sonunda tüm örnekler aynı PPF'leri (elbette sarmalayıcılar aracılığıyla) çağırır, bu nedenle belirli bir bellek tasarrufu beklenebilir. Soru ne kadar. Önemli ölçüde tasarruf bekliyorum.
Edward

5

Bunu aslında Accessor Doğrulaması'nı kullanarak gerçekleştirebilirsiniz :

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

Bu örnek, Prototypal İşlevleri ve Özel Veriler hakkındaki yazımdan geliyor ve burada daha ayrıntılı olarak açıklanıyor.


1
Bu cevap faydalı olamayacak kadar zekidir, ancak IFFE'ye bağlı bir değişkeni gizli bir el sıkışma olarak kullanma yanıtını seviyorum. Bu uygulama yararlı olmak için çok fazla kapanış kullanıyor; prototip tanımlı yöntemlere sahip olmanın amacı, her nesne üzerindeki her yöntem için yeni işlev nesnelerinin oluşturulmasını önlemektir.
greg.kindel

Bu yaklaşım, hangi prototip yöntemlerinin güvenilir hangilerinin güvenilir olmadığını belirlemek için gizli bir anahtar kullanır. Ancak, anahtarı doğrulayan örnektir, bu nedenle anahtarın örneğe gönderilmesi gerekir. Ancak güvenilmeyen kod, sahte bir örnekte güvenilir bir yöntem çağırabilir ve bu da anahtarı çalabilir. Ve bu anahtarla, gerçek örnekler tarafından güvenilen yeni yöntemler yapın. Bu sadece gizlilikten mahremiyettir.
Oriol

4

Geçerli JavaScript, ben olduğunu eminim bir ve tek sahibi olmak için özel durumunu erişilebilir, prototip şey eklemeden, fonksiyonlar halkı için this. Cevap "zayıf harita" örüntüsünü kullanmaktır.

Özetlemek gerekirse: PersonSınıf, anahtarların Kişi örnekleri olduğu ve değerlerin özel depolama için kullanılan düz nesneler olduğu tek bir zayıf haritaya sahiptir.

İşte tamamen işlevsel bir örnek: ( http://jsfiddle.net/ScottRippey/BLNVr/ adresinde oynayın )

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

Dediğim gibi, bu gerçekten 3 parçanın hepsini başarmanın tek yoludur.

Ancak iki uyarı vardır. Birincisi, bu performansa mal olur - özel verilere her eriştiğinizde, örneklerin O(n)bulunduğu bir işlemdir n. Bu nedenle, çok sayıda örneğiniz varsa bunu yapmak istemezsiniz. İkincisi, bir örneği bitirdiğinizde,destroy ; aksi takdirde, örnek ve veriler çöp toplanmaz ve sonunda bir bellek sızıntısı olur.

İşte bu yüzden asıl cevabım "Yapmamalısın" , bağlı kalmak istediğim bir şey.


Bir Kişi örneğini kapsam dışına çıkmadan açıkça yok etmezseniz, zayıf harita ona bir referans tutmaz, bu yüzden bir bellek sızıntısı olur mu? Kişinin diğer örnekleri değişkene erişebildiği ve Kişi'den miras alabileceği gibi korunan bir model buldum . Ekstra işlem dışında herhangi bir dis avantajı olup olmadığından emin değilsiniz (özelliğe erişmek kadar görünmüyor) stackoverflow.com/a/21800194/1641941 Özel / korumalı bir nesneyi döndürmek, arama kodundan bu yana bir acıdır sonra özel / korumalı mutasyona.
HMR

2
@HMR Evet, özel verileri açıkça yok etmeniz gerekiyor. Cevabıma bu uyarıyı ekleyeceğim.
Scott Rippey

3

bindVe callyöntemlerini kullanarak daha basit bir yol var .

Özel değişkenleri bir nesneye ayarlayarak, o nesnenin kapsamından yararlanabilirsiniz.

Misal

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

Bu yöntemin dezavantajları yoktur. Kapsam içeriği etkin bir şekilde geçersiz kılındığından, _privatenesnenin dışına erişiminiz yoktur . Ancak, yine de örnek nesnesinin kapsamına erişim vermek mümkün değildir. Prototip işlevindeki ortak değerlerine thisikinci argüman olarak bindveya callyine de erişime sahip olmak için nesnenin context ( ) yöntemini iletebilirsiniz .

Herkese açık değerlere erişme

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)

2
Neden biri ilk etapta sadece örnek bir yöntem oluşturmak yerine prototip yönteminin bir kopyasını oluşturacaktır?
ezmek

3

Dene!

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

1
Bu caller, katı modda izin verilmeyen uygulamaya bağlı bir uzantı olan güvenir .
Oriol

1

İşte ortaya koyduğum şey.

(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

Bu uygulamadaki temel sorun, her örneklemenin prototiplerini yeniden tanımlamasıdır.


İlginçtir, girişimi gerçekten seviyorum ve aynı şeyi düşünüyordum, ancak prototip işlevini her örneklemede yeniden tanımlamanın oldukça büyük bir sınırlama olduğu konusunda haklısınız. Bunun nedeni sadece CPU döngülerinin boşa gitmesi değildir, ancak daha sonra protokolü değiştirirseniz, bir sonraki örneklemede kurucuda tanımlandığı şekilde orijinal durumuna geri döndürür: /
Niko Bellic

1
Bu sadece prototipleri yeniden tanımlamakla kalmaz, aynı zamanda her örnek için yeni bir kurucu tanımlar. Yani "örnekler" artık aynı sınıfın örnekleri değildir.
Oriol

1

Bunu yapmanın çok basit bir yolu var

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

JavaScript prototipleri altındır.


2
Her yeni örnek oluşturulduğunda yeni bir işlev oluşturacağından, yapıcı işlevinde prototip kullanmamanın daha iyi olduğuna inanıyorum.
whamsicore

@whamsicore Evet doğru, ancak bu durumda gerekli olan her bir nesne için ortak bir kapanış ayarlamamız gerektiğinden önemlidir. Fonksiyon tanımları Kurucuların içinde ikamet ve biz başvurmak zorunda yüzden sebep SharedPrivate.prototypeolarak this.constructor.prototypebu GETP ve SETP birden çok kez yeniden tanımlamak için hiç önemli değil ...
Redu

1

Partiye geç kaldım ama bence katkıda bulunabilirim. İşte şunu kontrol et:

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

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

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

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

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


0

Değişkenleri daha yüksek bir kapsama koyamaz mısınız?

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

4
Ardından değişkenler MyClass'ın tüm örnekleri arasında paylaşılır.
ezmek

0

Doğrudan prototipte değil, bunun gibi yapıcı işlevinde yöntem eklemeyi deneyebilirsiniz:

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!

0

İşte bu sorun için en basit çözümü bulmaya çalışırken karşılaştığım bir şey, belki biri için yararlı olabilir. Javascript için yeniyim, bu yüzden kodla ilgili bazı sorunlar olabilir.

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

0

Bugün aynı soruyla karşılaştım ve Scott Rippey birinci sınıf yanıtını detaylandırdıktan sonra, hem ES5 ile uyumlu hem de verimli çok basit bir çözüm (IMHO) buldum, aynı zamanda isim çatışması güvenli (_private kullanmak güvenli değil) .

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

Ringojs ve nodejs ile test edilmiştir. Fikrinizi okumak için sabırsızlanıyorum.


İşte bir referans: 'Bir adım daha yakın' bölümünü kontrol edin. 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()

Bu nasıl? Özel bir erişimci kullanma. Değişkenleri ayarlamanıza rağmen almanıza izin verir, kullanım durumuna bağlıdır.


Bu mu değil yararlı bir şekilde soruya cevap. neden bunun cevabı olduğuna inanıyorsun? nasıl çalışır? Birisine kodunu herhangi bir bağlam veya anlam olmadan değiştirmesini söylemek , yanlış yaptıklarını öğrenmelerine yardımcı olmaz.
GrumpyCrouton

Sınıfın gizli özel değişkenlerine, sınıfın her bir örneğinde bu gizli değişkeni yaratmak zorunda kalmadan prototipler aracılığıyla erişmenin bir yolunu istedi. Yukarıdaki kod, bunu yapmak için örnek bir yöntemdir. Bu sorunun cevabı nasıl değil?
dylan0150

Sorunun cevabı olmadığını söylemedim. Bunun yararlı bir cevap olmadığını söyledim , çünkü kimsenin öğrenmesine yardımcı olmuyor. Kodunuzu, neden işe yaradığını, neden doğru yolu kullandığını açıklamalısınız. Soru yazarı olsaydım, cevabınızı kabul etmezdim çünkü öğrenmeyi teşvik etmez, bana yanlış yaptığımı ya da verilen kodun ne yaptığını ya da nasıl çalıştığını öğretmez.
GrumpyCrouton

0

Bir çözümüm var, ama bunun kusursuz olmadığından emin değilim.

Çalışması için aşağıdaki yapıyı kullanmanız gerekir:

  1. Tüm özel değişkenleri içeren 1 özel nesne kullanın.
  2. 1 örnek işlevi kullanın.
  3. Yapıcıya ve tüm prototip işlevlerine bir kapatma uygulayın.
  4. Oluşturulan her örnek, tanımlanan kapağın dışında yapılır.

İşte kod:

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

Bunun nasıl çalıştığı, "privateFields" özel değişkenleri nesnesine erişmek için "this.getPrivateFields" örnek işlevini sağlamasıdır, ancak bu işlev yalnızca "privateFields" nesnesini tanımlanan ana kapatma içinde döndürür (ayrıca, bu this.getPrivateFields kullanarak prototip işlevleri de döndürür) msgstr "bu kapatma içinde tanımlanması gerekir).

Çalışma zamanı sırasında üretilen ve tahmin edilmesi zor bir karma, "getPrivateFields" ifadesinin kapanma kapsamı dışında çağrılsa bile "privateFields" nesnesini döndürmeyeceğinden emin olmak için parametre olarak kullanılır.

Dezavantajı, TestClass'ı kapatma dışında daha fazla prototip işleviyle genişletemeyiz.

İşte bazı test kodu:

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: Bu yöntemi kullanarak, özel işlevleri "tanımlamak" da mümkündür.

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

TestClass.prototype.prototypeHello = function () {
    this.privateFunction(hash);
};

0

Bugün bununla oynuyordum ve Sembolleri kullanmadan bulabildiğim tek çözüm buydu. Bu konuda en iyi şey aslında tamamen özel olabilir.

Çözüm, temelde özel bir depolama önbelleği (zayıf bir harita kullanarak) için arabulucu haline gelen, ev yapımı bir modül yükleyiciye dayanır.

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

0

Bu sorulduğu günden bu yana on yıldan fazla zaman geçtiğini biliyorum, ama sadece bu konudaki düşüncemi programcı hayatımda nnci kez koydum ve henüz tamamen beğenip beğenmediğimi bilmediğim olası bir çözüm buldum . Bu metodolojinin daha önce belgelendiğini görmedim, bu yüzden ona "özel / kamu doları kalıbı" veya _ $ / $ kalıbı adını vereceğim .

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

Kavram, bir Interface nesnesi döndüren bir Yapıcı işlevi döndüren bir ClassDefinition işlevi kullanır . Arabirimin tek yöntemi, yapıcı nesnesindeki karşılık gelen işlevi çağırmak için bir bağımsız değişken alan , daha sonra iletilen tüm ek bağımsız değişkenler çağrıda iletilir.$namename

Genel olarak tanımlanmış yardımcı işlevi ClassValues, bir nesnedeki tüm alanları gerektiği gibi saklar . Bunlara _$erişmek için işlevi tanımlar name. Bu kısa bir get / set paterni izler, böylece valuegeçirilirse yeni değişken değeri olarak kullanılır.

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

      return values[name];
    }
  };
};

Genel olarak tanımlanmış fonksiyon Interface, bir nesne ve bir alan Valuesbir dönüş nesnesi _interfacetek fonksiyonlu $olduğu incelerobj parametre sonra adlı bir fonksiyonu bulmak için nameve bunu çalıştırır valuesolarak kapsamına sahip bir nesne. İletilen ek argümanlar $işlev çağrısında aktarılır.

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

Aşağıdaki örnekte, ClassXClassDefinition , Constructorişlev olan sonucuna atanmıştır . Constructorherhangi bir sayıda argüman alabilir. Interfaceyapıcı çağrıldıktan sonra harici kod alır.

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

ConstructorOluşturucu işlev gövdesinde tanımlayabilmenize rağmen, prototipli olmayan işlevlere sahip olmanın bir anlamı yoktur . Tüm fonksiyonlar, kamu doları cinsinden çağrılır this.$("functionName"[, param1[, param2 ...]]). Özel değerlere özel dolar düzeni ile erişilir this._$("valueName"[, replacingValue]);. Şöyle Interfacebir tanıma sahip değildir _$değerleri dış nesneler tarafından erişilemez. Her prototip fonksiyon gövdesi this,values nesneye$ , Oluşturucu kardeş işlevlerini doğrudan çağırırsanız istisnalar alırsınız; _ $ / $ desen ihtiyaçları çok örneklenmekte fonksiyon organları takip edilecek. Örnek kullanımın altında.

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

Ve konsol çıkışı.

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

_ $ / $ Desen tam prototyped sınıflarında değerlerin tam gizlilik sağlar. Bunu kullanacak mıyım, kusurları olup olmadığını bilmiyorum, ama hey, iyi bir bilmeceydi!


0

ES6 Zayıf Haritalar

ES6 tabanlı basit bir desen kullanarak WeakMaps prototip işlevlerinden ulaşılabilir özel üye değişkenleri elde etmek mümkündür .

Not: WeakMaps kullanımı , Çöp Toplayıcı'nın kullanılmayan örnekleri tanımlamasına ve atmasına izin vererek bellek sızıntılarına karşı güvenliği garanti eder .

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

Bu modelin daha ayrıntılı bir açıklamasını burada bulabilirsiniz


-1

Kodunuzdaki 3 şeyi değiştirmeniz gerekir:

  1. Değiştir var privateField = "hello"ilethis.privateField = "hello" .
  2. Prototipte yerini privateFieldilethis.privateField .
  3. Olmayan prototip olarak da yerini privateFieldile this.privateField.

Son kod aşağıdaki gibi olacaktır:

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

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

var t = new TestClass();

t.prototypeHello()

this.privateFieldözel bir alan olmazdı. dışarıdan erişilebilir:t.privateField
V. Rubinetti

-2

Yapıcı tanımı içinde bir prototip ataması kullanabilirsiniz.

Değişken, prototip eklenen yöntem tarafından görülebilir ancak işlevlerin tüm örnekleri aynı SHARED değişkenine erişir.

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

Umarım faydalı olabilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.