JavaScript özel yöntemleri


482

Genel yöntemle bir JavaScript sınıfı yapmak için şöyle bir şey yaparım:

function Restaurant() {}

Restaurant.prototype.buy_food = function(){
   // something here
}

Restaurant.prototype.use_restroom = function(){
   // something here
}

Bu şekilde sınıfımın kullanıcıları:

var restaurant = new Restaurant();
restaurant.buy_food();
restaurant.use_restroom();

Sınıf yöntemleri tarafından harici olarak değil , buy_foodve use_restroomyöntemleri tarafından çağrılabilen özel bir yöntemi nasıl oluştururum ?

Başka bir deyişle, yöntem uygulamamın şunları yapabilmesini istiyorum:

Restaurant.prototype.use_restroom = function() {
   this.private_stuff();
}

Ama bu işe yaramaz:

var r = new Restaurant();
r.private_stuff();

private_stuffHer ikisinin de geçerli olması için nasıl özel bir yöntem olarak tanımlayabilirim ?

Doug Crockford'un yazısını birkaç kez okudum ama "özel" yöntemler genel yöntemlerle çağrılabilir ve "ayrıcalıklı" yöntemler dışarıdan çağrılabilir gibi görünmüyor.

Yanıtlar:


403

Bunu yapabilirsiniz, ancak olumsuz tarafı prototipin bir parçası olamamasıdır:

function Restaurant() {
    var myPrivateVar;

    var private_stuff = function() {  // Only visible inside Restaurant()
        myPrivateVar = "I can set this here!";
    }

    this.use_restroom = function() {  // use_restroom is visible to all
        private_stuff();
    }

    this.buy_food = function() {   // buy_food is visible to all
        private_stuff();
    }
}

9
Kapakların içindeki kapakları gizlemek, tüm tercümanların gizliliğini garanti etmez. Bkz. Code.google.com/p/google-caja/wiki/…
Mike Samuel

51
@mikesamuel - doğru, ama sadece bu çevirmenlerde böcek olduğunda :)
jvenema

133
Bu özel bir yöntemdir, ancak özellikle bu nesnelerin çoğunu oluşturuyorsanız, normal bir prototip yönteminden çok daha fazla bellek kullanmaya eğilimlidir. Her nesne örneği için, sınıfa değil, nesneye bağlı ayrı bir işlev oluşturur. Ayrıca, nesnenin kendisi yok edilene kadar çöp toplanmaz.
Arindam

4
Bir nesne yaparsanız McDonalds (), Restaurant () 'dan devralınırsa, bu şekilde bildirirseniz McDonalds özel yöntemleri geçersiz kılamaz. Aslında bunu yapabilirsiniz, ancak başka bir yöntem özel olarak çağırırsa çalışmaz, yöntemin orijinal sürümünü çağırır ve üst yöntemi de çağıramazsınız. Şimdiye kadar kalıtımla iyi çalışan özel yöntemler beyan etmenin iyi bir yolunu bulamadım. Bu ve performans sonuçları bunu hiç de iyi bir tasarım deseni yapmıyor. Özel yöntemleri belirtmek, altını çizmeyi seven bir tür önek kullanmanızı öneririm.
Hoffmann

68
Özel yöntemlerin geçersiz kılınması gerekmez - özeldir.
17 of 26

163

Kendi kendine çağırma işlevini ve çağrıyı kullanma

JavaScript prototipler kullanır ve Nesneye Dayalı diller gibi sınıfları (veya bu konudaki yöntemlerini) içermez. Bir JavaScript geliştiricisinin JavaScript'te düşünmesi gerekir.

Wikipedia teklifi:

Nesneye yönelik birçok dilden farklı olarak, işlev tanımı ile yöntem tanımı arasında bir ayrım yoktur. Aksine, ayrım fonksiyon çağrısı sırasında meydana gelir; bir işlev bir nesnenin yöntemi olarak çağrıldığında, işlevin yerel bu anahtar sözcüğü o çağırma için o nesneye bağlanır.

Özel "yöntem" i çağırmak için kendi kendini çağırma işlevi ve çağrı işlevi kullanan çözüm :

var MyObject = (function () {

    // Constructor
    function MyObject (foo) {
        this._foo = foo;
    }

    function privateFun (prefix) {
        return prefix + this._foo;
    }

    MyObject.prototype.publicFun = function () {
        return privateFun.call(this, '>>');
    }

    return MyObject;
})();


var myObject = new MyObject('bar');
myObject.publicFun();      // Returns '>>bar'
myObject.privateFun('>>'); // ReferenceError: private is not defined

Çağrı fonksiyonu bize uygun bağlamıyla özel işlevi çağırmak için izin verir ( this).


Node.js ile daha basit

Node.js kullanıyorsanız , modül yükleme sisteminden yararlanabileceğiniz için IIFE'ye ihtiyacınız yoktur :

function MyObject (foo) {
    this._foo = foo;
}

function privateFun (prefix) {
    return prefix + this._foo;
}

MyObject.prototype.publicFun = function () {
    return privateFun.call(this, '>>');
}

exports.MyObject = MyObject;

Dosyayı yükleyin:

var MyObject = require('./MyObject').MyObject;

var myObject = new MyObject('bar');
myObject.publicFun();      // Returns '>>bar'
myObject.privateFun('>>'); // ReferenceError: private is not defined


(deneysel) Bind Operatörü ile ES7

Bağlama operatörü ::ECMAScript olan teklifi ve Babil uygulanan ( evre 0 ).

export default class MyObject {
  constructor (foo) {
    this._foo = foo;
  }

  publicFun () {
    return this::privateFun('>>');
  }
}

function privateFun (prefix) {
  return prefix + this._foo;
}

Dosyayı yükleyin:

import MyObject from './MyObject';

let myObject = new MyObject('bar');
myObject.publicFun();      // Returns '>>bar'
myObject.privateFun('>>'); // TypeError: myObject.privateFun is not a function

34
Bu en iyi cevap. Özel yöntemlerin faydaları, önemsiz değil.
TaylorMac

1
@TaylorMac Parça hariç .call.
pishpish

1
@janje Huh? Sorunun konusu bu, f()javascript'te özel bir şey yok (şu anda değil).
Yves

2
@YvesM. Sorunun amacı, onu simüle eden en iyi modeli seçmektir.
pishpish

1
@changed gizli fonksiyonlara (dışarıdan çağrılamaz), güzel sintax'a (no .call) ve küçük belleğe (örnekte fonksiyon yok ) sahip olmak için en iyi uzlaşma nedir? Bu var mı?
Ciprian Tomoiagă

161

Bunun gibi özel yöntemleri simüle edebilirsiniz:

function Restaurant() {
}

Restaurant.prototype = (function() {
    var private_stuff = function() {
        // Private code here
    };

    return {

        constructor:Restaurant,

        use_restroom:function() {
            private_stuff();
        }

    };
})();

var r = new Restaurant();

// This will work:
r.use_restroom();

// This will cause an error:
r.private_stuff();

Bu teknik hakkında daha fazla bilgiyi burada bulabilirsiniz: http://webreflection.blogspot.com/2008/04/natural-javascript-private-methods.html


7
Ayrıca özel / kamusal yöntem ve üyeler üzerinde bir kaynak olarak Douglas Crockford sitesini öneririm javascript.crockford.com/private.html
Jared

10
Soruda bu bağlantıdan bahsetti.
Gulzar Nazim

8
Bu yöntemin dezavantajı, Restorandaki private_stuff () özel verilerine erişememeniz ve diğer Restaurant yöntemleri private_stuff () yöntemini çağıramaz. Yukarıdakiler, az önce bahsettiğim koşullardan herhangi birine ihtiyacınız yoksa, prototipte use_restroom () tutabilirsiniz.
17 of 26

6
Bu çözüm ve kabul edilen cevap olmalıdır çünkü yazar açıkça prototip özelliğini kullanıyor.
Gabriel Llamas

23
@Georgebrock tarafından önerilen kalıpla, tüm özel veriler tüm restoran nesneleri arasında paylaşılacak . Bu, statik özel değişkenlere ve sınıf tabanlı OOP işlevlerine benzer. OP olmadığını varsayıyorum değil bunu istiyor. Ne demek istediğimi göstermek için bir jsFiddle oluşturdum .
feklee

35

Herkese açık bir API'nız olduğunda ve özel ve genel yöntemler / özellikler istediğinizde, her zaman Modül Modelini kullanırım. Bu desen YUI kütüphanesinde popüler hale getirildi ve detaylar burada bulunabilir:

http://yuiblog.com/blog/2007/06/12/module-pattern/

Diğer geliştiricilerin anlaması gerçekten basit ve kolaydır. Basit bir örnek için:

var MYLIB = function() {  
    var aPrivateProperty = true;
    var aPrivateMethod = function() {
        // some code here...
    };
    return {
        aPublicMethod : function() {
            aPrivateMethod(); // okay
            // some code here...
        },
        aPublicProperty : true
    };  
}();

MYLIB.aPrivateMethod() // not okay
MYLIB.aPublicMethod() // okay

bu tür bir şey bir IDE'nin otomatik tamamlanmasıyla algılanmaz :(
Upvote

19
Ama bu bir sınıf değil, bu yüzden farklı durumlarla bunun 2 "örneğine" sahip olamazsınız.
DevAntoine

() kısmını kaldırırsanız bir "sınıfınız" olur. en azından farklı durumlarla farklı örnekleri başlatabileceğiniz yerde. modül desen oldukça bellek yoğun olsa da ...
oligofren

@DevAntoine 26 cevaptan 17 yorumu için bakınız. JavaScript'te genişletilebilir sınıflar ve özel yöntemler kolayca el ele gitmez. Bu durumda benim önerim miras üzerine kompozisyon ile gitmek olacaktır. Ekteki beton nesneyle aynı yöntemlerle genişletilebilir bir prototip oluşturun. Daha sonra prototipinizin içinde, beton nesnenizde yöntemleri ne zaman arayacağınıza karar verebilirsiniz.

Ortak değişkenleri şu şekilde özel işlevlerden çağırmanın bir dezavantajı var mı aPrivateMethod = function() { MYLIB.aPublicProperty}?
Hanna

21

İşte Douglas Crockford's'un sitesinde Özel Üyeler JavaScript'te önerilerini anlamak için oluşturduğum sınıf

function Employee(id, name) { //Constructor
    //Public member variables
    this.id = id;
    this.name = name;
    //Private member variables
    var fName;
    var lName;
    var that = this;
    //By convention, we create a private variable 'that'. This is used to     
    //make the object available to the private methods. 

    //Private function
    function setFName(pfname) {
        fName = pfname;
        alert('setFName called');
    }
    //Privileged function
    this.setLName = function (plName, pfname) {
        lName = plName;  //Has access to private variables
        setFName(pfname); //Has access to private function
        alert('setLName called ' + this.id); //Has access to member variables
    }
    //Another privileged member has access to both member variables and private variables
    //Note access of this.dataOfBirth created by public member setDateOfBirth
    this.toString = function () {
        return 'toString called ' + this.id + ' ' + this.name + ' ' + fName + ' ' + lName + ' ' + this.dataOfBirth; 
    }
}
//Public function has access to member variable and can create on too but does not have access to private variable
Employee.prototype.setDateOfBirth = function (dob) {
    alert('setDateOfBirth called ' + this.id);
    this.dataOfBirth = dob;   //Creates new public member note this is accessed by toString
    //alert(fName); //Does not have access to private member
}
$(document).ready()
{
    var employee = new Employee(5, 'Shyam'); //Create a new object and initialize it with constructor
    employee.setLName('Bhaskar', 'Ram');  //Call privileged function
    employee.setDateOfBirth('1/1/2000');  //Call public function
    employee.id = 9;                     //Set up member value
    //employee.setFName('Ram');  //can not call Private Privileged method
    alert(employee.toString());  //See the changed object

}

5
Bu = bu oldukça yaygın bir desen, yukarıda bahsedilen Crockford tarafından "Javascript: İyi parçalar"
kitabında popüler hale getirilmiş

8
thatthisişlevler farklı bir nesneye bağlı olduğunda, kapsam belirleme sorunlarını önlemek için kullanılır . Burada, depoladığınız thisiçinde thatve asla hiç yapmıyorum aynı olan tekrar kullanmadan. Sen değişmelidir thisile thatgenelinde tüm Constructoriç fonksiyonları (değil yöntemler beyanı). Bir employeeşekilde applydüzenlenir veya calldüzenlenirse, bu yöntemler thisyanlış bağlandığından atabilir .
Maroshii

Ayrıca, her örnek verimsiz özel işlevlerin tam bir kopyasına sahip olacaktır. Genel yöntemlerin özel sınıfa erişemediği gerçeğinin yanı sıra, dart'a geçmek istememe neden oluyor. Ne yazık ki angulardart süper beta.
Ray Foss

Buradaki "yapıcı" yöntemi nerede? Bir sınıfın somutlaştırıldığı zaman normal olarak yapıcı yönteminde yürütülecek mantığı nereye koyacağım?
BadHorsie

13

Ben bunu ifade ettim: EDIT: Aslında, birisi özdeş bir çözüme bağladı. Duh!

var Car = function() {
}

Car.prototype = (function() {
    var hotWire = function() {
        // Private code *with* access to public properties through 'this'
        alert( this.drive() ); // Alerts 'Vroom!'
    }

    return {
        steal: function() {
            hotWire.call( this ); // Call a private method
        },
        drive: function() {
            return 'Vroom!';
        }
    };
})();

var getAwayVechile = new Car();

hotWire(); // Not allowed
getAwayVechile.hotWire(); // Not allowed
getAwayVechile.steal(); // Alerts 'Vroom!'

1
Bu güzel bir teknik, ancak kurucunuzdaki parametrelere nasıl izin verirsiniz? Örneğin hız var getAwayVehicle = new Car(100);nerede 100ve hızı uyarmak istiyorsunuz. Teşekkürler!
Jason

1
Anladım, sahip olabilir var Car = function(speed) { this.speed = speed; }ve `` dönüş {yapıcı: Araba, ... ''
Jason

11

Kapanışların anlaşılmaması nedeniyle bu tür sorular tekrar tekrar ortaya çıkıyor. JS'de kapanış en önemli şeydir. Her JS programcısı bunun özünü hissetmek zorundadır.

1. Her şeyden önce ayrı bir kapsam (kapatma) yapmamız gerekiyor.

function () {

}

2. Bu alanda istediğimizi yapabiliriz. Ve kimse bunu bilmeyecek.

function () {
    var name,
        secretSkills = {
            pizza: function () { return new Pizza() },
            sushi: function () { return new Sushi() }
        }

    function Restaurant(_name) {
        name = _name
    }
    Restaurant.prototype.getFood = function (name) {
        return name in secretSkills ? secretSkills[name]() : null
    }
}

3. Dünya bizim restoran sınıfı hakkında bilmek, biz kapanıştan geri dönmek zorunda.

var Restaurant = (function () {
    // Restaurant definition
    return Restaurant
})()

4. sonunda, biz var:

var Restaurant = (function () {
    var name,
        secretSkills = {
            pizza: function () { return new Pizza() },
            sushi: function () { return new Sushi() }
        }

    function Restaurant(_name) {
        name = _name
    }
    Restaurant.prototype.getFood = function (name) {
        return name in secretSkills ? secretSkills[name]() : null
    }
    return Restaurant
})()

5. Ayrıca, bu yaklaşım kalıtım ve şablon oluşturma potansiyeline sahiptir

// Abstract class
function AbstractRestaurant(skills) {
    var name
    function Restaurant(_name) {
        name = _name
    }
    Restaurant.prototype.getFood = function (name) {
        return skills && name in skills ? skills[name]() : null
    }
    return Restaurant
}

// Concrete classes
SushiRestaurant = AbstractRestaurant({ 
    sushi: function() { return new Sushi() } 
})

PizzaRestaurant = AbstractRestaurant({ 
    pizza: function() { return new Pizza() } 
})

var r1 = new SushiRestaurant('Yo! Sushi'),
    r2 = new PizzaRestaurant('Dominos Pizza')

r1.getFood('sushi')
r2.getFood('pizza')

Umarım bu birisinin bu konuyu daha iyi anlamasına yardımcı olur


2
4. noktada sahip olduklarınız muhteşem! Bence burada prototip üzerindeki yöntemleri kullanarak performans / bellek kazanımlarını elde ettiğiniz tek cevap budur ve bu genel yöntemlerin özel üyelere tam erişimi vardır. +1
Hudon

7
Çalışmıyor. Buradaki ad değişkeni statik değişken gibi davranır ve tüm Restaurant örnekleri tarafından paylaşılır. İşte jsbin: jsbin.com/oqewUWa/2/edit?js,output
Shah

iyi bir deneme, ama Shital'in işaret ettiği gibi, isim değişkeni buggy.
oligofren

2
2c'yi buraya teyit etmek için buraya eklemek işe yaramıyor, güzel görünüyor, ancak yukarıda belirtildiği gibi "ad" ın belirtildiği gibi statik bir değişken olarak işlev görecektir
Paul Carroll

10

Şahsen, JavaScript'te sınıf oluşturmak için aşağıdaki kalıbı tercih ediyorum:

var myClass = (function() {
    // Private class properties go here

    var blueprint = function() {
        // Private instance properties go here
        ...
    };

    blueprint.prototype = { 
        // Public class properties go here
        ...
    };

    return  {
         // Public class properties go here
        create : function() { return new blueprint(); }
        ...
    };
})();

Gördüğünüz gibi, her biri genel ve özel olabilen hem sınıf özelliklerini hem de örnek özelliklerini tanımlamanıza olanak tanır.


gösteri

var Restaurant = function() {
    var totalfoodcount = 0;        // Private class property
    var totalrestroomcount  = 0;   // Private class property
    
    var Restaurant = function(name){
        var foodcount = 0;         // Private instance property
        var restroomcount  = 0;    // Private instance property
        
        this.name = name
        
        this.incrementFoodCount = function() {
            foodcount++;
            totalfoodcount++;
            this.printStatus();
        };
        this.incrementRestroomCount = function() {
            restroomcount++;
            totalrestroomcount++;
            this.printStatus();
        };
        this.getRestroomCount = function() {
            return restroomcount;
        },
        this.getFoodCount = function() {
            return foodcount;
        }
    };
   
    Restaurant.prototype = {
        name : '',
        buy_food : function(){
           this.incrementFoodCount();
        },
        use_restroom : function(){
           this.incrementRestroomCount();
        },
        getTotalRestroomCount : function() {
            return totalrestroomcount;
        },
        getTotalFoodCount : function() {
            return totalfoodcount;
        },
        printStatus : function() {
           document.body.innerHTML
               += '<h3>Buying food at '+this.name+'</h3>'
               + '<ul>' 
               + '<li>Restroom count at ' + this.name + ' : '+ this.getRestroomCount() + '</li>'
               + '<li>Food count at ' + this.name + ' : ' + this.getFoodCount() + '</li>'
               + '<li>Total restroom count : '+ this.getTotalRestroomCount() + '</li>'
               + '<li>Total food count : '+ this.getTotalFoodCount() + '</li>'
               + '</ul>';
        }
    };

    return  { // Singleton public properties
        create : function(name) {
            return new Restaurant(name);
        },
        printStatus : function() {
          document.body.innerHTML
              += '<hr />'
              + '<h3>Overview</h3>'
              + '<ul>' 
              + '<li>Total restroom count : '+ Restaurant.prototype.getTotalRestroomCount() + '</li>'
              + '<li>Total food count : '+ Restaurant.prototype.getTotalFoodCount() + '</li>'
              + '</ul>'
              + '<hr />';
        }
    };
}();

var Wendys = Restaurant.create("Wendy's");
var McDonalds = Restaurant.create("McDonald's");
var KFC = Restaurant.create("KFC");
var BurgerKing = Restaurant.create("Burger King");

Restaurant.printStatus();

Wendys.buy_food();
Wendys.use_restroom();
KFC.use_restroom();
KFC.use_restroom();
Wendys.use_restroom();
McDonalds.buy_food();
BurgerKing.buy_food();

Restaurant.printStatus();

BurgerKing.buy_food();
Wendys.use_restroom();
McDonalds.buy_food();
KFC.buy_food();
Wendys.buy_food();
BurgerKing.buy_food();
McDonalds.buy_food();

Restaurant.printStatus();

Ayrıca bkz bu Fiddle .


Bu beni ES6 sınıfları kullanmak ve çok transpiles görmek istiyorum yapar
sheriffderek

9

Tüm bu kapanış size mal olacak. Özellikle IE'de hız sonuçlarını test ettiğinizden emin olun. Bir adlandırma kuralıyla daha iyi olduğunuzu göreceksiniz. Orada hala IE6 kullanmak zorunda kurumsal web kullanıcıları bir sürü var ...


34
Kimin umurunda?
nowayyy

17
Hala IE6 kullanan% 9, hız, optimizasyon ve tüm modern HTML5 özelliklerini umursamıyor. Yani kapanışların maliyeti olmayacak.
Gabriel Llamas


7
@LorenzoPolidori w3schools kullanıcıları! == kurumsal web kullanıcıları;]
WynandB

Bir adlandırma kuralı (ör: bir alt çizgi eklemek) gidilecek yoldur. Kodun bakımı daha kolaydır ve yöntemler hala prototipte tanımlanmıştır. Bugünlerde ... Ben sadece yöntemi daktiloda özel olarak işaretlemek.
David Sherret

5

Bu kadar ayrıntılı olma. Javascript. Bir Adlandırma Sözleşmesi kullanın .

Es6 derslerinde yıllarca çalıştıktan sonra, kısa bir süre önce bir es5 projesi üzerinde çalışmaya başladım (zaten çok ayrıntılı olan requirJS'yi kullanarak). Burada bahsettiğim tüm stratejileri defalarca gördüm ve her şey temelde bir adlandırma kuralı kullanmak için :

  1. Javascript gibi kapsam anahtar kelimeleri içermiyor private. Javascript'e giren diğer geliştiriciler bunu önceden bilecek. Bu nedenle, basit bir adlandırma kuralı yeterlidir. Alt çizgi ile önek eklemenin basit bir adlandırma kuralı, hem özel mülklerin hem de özel yöntemlerin sorununu çözer.
  2. Prototipten hız nedenleriyle yararlanalım, ama bundan daha ayrıntılı olmayalım. Es5 "sınıfını" diğer arka uç dillerinde bekleyebileceğimiz kadar yakından tutmaya çalışalım (ve bir örneği döndürmemiz gerekmese bile her dosyayı sınıf olarak ele alalım).
  3. Daha gerçekçi bir modül durumu ile gösterelim (eski es5 ve eski requirJ'leri kullanacağız).

my-tooltip.js

    define([
        'tooltip'
    ],
    function(
        tooltip
    ){

        function MyTooltip() {
            // Later, if needed, we can remove the underscore on some
            // of these (make public) and allow clients of our class
            // to set them.
            this._selector = "#my-tooltip"
            this._template = 'Hello from inside my tooltip!';
            this._initTooltip();
        }

        MyTooltip.prototype = {
            constructor: MyTooltip,

            _initTooltip: function () {
                new tooltip.tooltip(this._selector, {
                    content: this._template,
                    closeOnClick: true,
                    closeButton: true
                });
            }
        }

        return {
            init: function init() {
               new MyTooltip();  // <-- Our constructor adds our tooltip to the DOM so not much we need to do after instantiation.
            }

            // You could instead return a new instantiation, 
            // if later you do more with this class.
            /* 
            create: function create() {
               return new MyTooltip();
            }
            */
        }
    });

2
Ne Javascript dili ne de herhangi bir tipik tarayıcı ana bilgisayarının, özel durumu "gizlemek" için adlandırma kurallarına dayanan herhangi bir nesne tanımlayacağına dikkat edilmelidir, bu nedenle geliştiricilerin kavramı kavrayacağı konusunda haklı olabilirsiniz, ancak yine de çok OO programlamaya OO yaklaşımı.
zengin hatırlatıcı

Bunu yaparken iyi bir referans isteyebilir miyim? Bu örnekte benim için yeni olan kısımlar var. defineVe constructorve yapının kendisi. Çoğunlukla cevap konusunda hemfikir olsam da, JS ile çok fazla OOP etkisi ile çalışmaya başladım ve C # 'a önceden deneyimim olduğundan TS'ye çok erken atladım. Bence bu şeyleri öğrenmem ve prototipleme / prosedür paradigmasını anlamam gerekiyor. (upvoted, btw)
Soğuk Cerberus

1
@ColdCerberus bu kod pasajı es5 kullanıyor. Bu yaklaşımın tam bir resmini burada görebilirsiniz: gist.github.com/jonnyreeves/2474026 . Ancak unutmayın, bu yaklaşımı almak ve es6 sınıflarını kullanarak güncellemek isteyeceksiniz: googlechrome.github.io/samples/classes-es6 ve es6 modülleri (içe aktarma / dışa aktarım sözdizimi): hackernoon.com/…
prograhammer

5

Bunu şimdi es10 özel yöntemleriyle yapabilirsiniz . Sadece #yöntem adından önce a eklemeniz gerekir .

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

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

2
Ancak bu aşama 3. aşama ve henüz resmi olarak dilin bir parçası değil.
misterhtmlcss

3

Crockford'un özel veya ayrıcalıklı modelini takip eden çözümlerden herhangi birini alın . Örneğin:

function Foo(x) {
    var y = 5;
    var bar = function() {
        return y * x;
    };

    this.public = function(z) {
        return bar() + x * z;
    };
}

Saldırganın JS bağlamında "yürütme" hakkı olmadığı her durumda, "genel" veya "özel" alanlara veya yöntemlere erişme yolu yoktur. Saldırganın bu erişime sahip olması durumunda, bu tek astarı yürütebilir:

eval("Foo = " + Foo.toString().replace(
    /{/, "{ this.eval = function(code) { return eval(code); }; "
));

Yukarıdaki kodun tüm yapıcı türü-gizlilik için genel olduğunu unutmayın. Buradaki bazı çözümlerle başarısız olacaktır, ancak kapak tabanlı çözümlerin hemen hemen hepsinin farklı replace()parametrelerle bu şekilde kırılabileceği açık olmalıdır .

Bu yürütüldükten sonra, ile oluşturulan herhangi bir nesne, kurucunun kapanışında tanımlanan değerleri veya yöntemleri döndürmek veya değiştirmek için çağrılabilecek new Foo()bir evalyönteme sahip olacaktır, örneğin:

f = new Foo(99);
f.eval("x");
f.eval("y");
f.eval("x = 8");

Bununla ilgili görebildiğim tek sorun, tek bir örneğin olduğu ve yükte oluşturulduğu durumlarda işe yaramayacağı. Ancak o zaman bir prototip tanımlamak için hiçbir neden yoktur ve bu durumda saldırgan, aynı parametreleri geçirmenin bir yolu olduğu sürece (örn. Sabit veya kullanılabilir değerlerden hesaplanır) yapıcı yerine nesneyi yeniden oluşturabilir.

Benim düşünceme göre, bu Crockford'un çözümünü neredeyse işe yaramaz hale getiriyor."Mahremiyet" çözümünün dezavantajları (azaltılmış okunabilirlik ve bakım kolaylığı, düşük performans, artan bellek) "gizlilik yok" prototip tabanlı yöntemi daha iyi bir seçim haline getirir.

Ben genellikle işaret __privateve _protectedyöntemleri ve alanları (Perl tarzı) işaretlemek için önde gelen alt çizgiler kullanın, ancak JavaScript gizlilik fikri nasıl sadece yanlış anlaşılmış bir dil gösterir.

Bu yüzden ilk cümlesi dışında Crockford'a katılmıyorum .

Peki JS'de gerçek gizliliği nasıl elde edersiniz? Özel olması gereken her şeyi sunucu tarafına koyun ve AJAX aramaları yapmak için JS kullanın.


Bu daha iyi bilinmesi gereken ciddi bir konudur. Bu saldırıya karşı bir 'savunma' var mı?
James

@James Bildiğim hiçbir şey, bence canavarın doğası. İşaret ettiğim gibi, işlevselliği korumalı bir ortamda çalıştığı sunucuya taşıyabilirsiniz. Cevabımda üstesinden gelmek istediğim nokta, Crockford'un çözümünün yardımcı olmadığı, gereksiz yere kodu karmaşıklaştırdığı ve bu konuda bir şeyler yapma gereğini gizlediği.
Fozi

Bir kullanıcı gizli bir şifre girerse, bu sunucu tarafını yapamaz. Bir noktada şifre bir 'private' var. Bir saldırgan bunu okuyabilir mi? Koduma güveniyorum ve yine de evim standartları eval () 'a izin vermiyor. Saldırgan, düzgün kontrol etmediğim bazı üçüncü taraf JavaScript eklentisi veya kitaplığı olabilir - bu yüzden, evet, bunları kontrol etmem gerekiyor. Saldırgan, kodumla etkileşime girmemesi gereken yan tarafta bir reklam gibi de olabilir. Ben (function () {allMyStuff}());küresel bir şey ortaya çıkarmak için anonim tüm kodunu sararak buna karşı korumak .
James

@James Bu OT oluyor, buna devam etmek istiyorsanız lütfen yeni bir soru açın. Evet, bir saldırgan şifreyi okuyabilir. "Özel" değişkeninizden. Veya DOM'dan. Veya AJAX API'sını değiştirebilir. Veya sayfanızı başka bir şeyle değiştirir. Eğer yukarıdakilerden herhangi birini yapamazsa, JS değişkenlerinizi de okuyamayacağı için JS "gizliliğine" gerek yoktur. Mesele şu ki, Crockford'un herkesin şu anda kullandığı “çözümü” bu soruna yardımcı olmuyor.
Fozi

Sahte kod gizleme bu saldırıya karşı zayıf bir savunma olabilir inanıyorum - sabit bir isme sahip fonksiyona bağlı olamaz zaman fonksiyon gövdesi değiştirmek daha zor; f.eval('nameOfVariable')ne olduğunu bilmiyorsanız yapmak daha zor 'nameOfVariable'...
Gershom


2

Genel işlevlerin özel işlevlere erişme özelliğine sahip genel ve özel işlevlerin tamamını istiyorsanız, böyle bir nesnenin düzen kodunu:

function MyObject(arg1, arg2, ...) {
  //constructor code using constructor arguments...
  //create/access public variables as 
  // this.var1 = foo;

  //private variables

  var v1;
  var v2;

  //private functions
  function privateOne() {
  }

  function privateTwon() {
  }

  //public functions

  MyObject.prototype.publicOne = function () {
  };

  MyObject.prototype.publicTwo = function () {
  };
}

Birisi bana bunun neden oylandığını söyleyebilir mi? Bana iyi görünüyor.
thomasrutter

10
Her yaptığınızda new MyObject, prototipi MyObjectaynı değerlerle değiştirilir.
bpierre

2
-1. Asla kurucunun .prototypeiçine atama .
Bergi

2
var TestClass = function( ) {

    var privateProperty = 42;

    function privateMethod( ) {
        alert( "privateMethod, " + privateProperty );
    }

    this.public = {
        constructor: TestClass,

        publicProperty: 88,
        publicMethod: function( ) {
            alert( "publicMethod" );
            privateMethod( );
        }
    };
};
TestClass.prototype = new TestClass( ).public;


var myTestClass = new TestClass( );

alert( myTestClass.publicProperty );
myTestClass.publicMethod( );

alert( myTestClass.privateMethod || "no privateMethod" );

Georgebrock'a benzer ama biraz daha az ayrıntılı (IMHO) Bu şekilde yapmakta herhangi bir problem var mı? (Hiç bir yerde görmedim)

edit: Ben her bağımsız örnekleme kamu yöntemlerinin kendi bir kopyasını vardır, bu nedenle prototip kullanımı zarar bu tür işe yaramaz olduğunu fark etti.


2

Javascript özel / kamu yöntemleri / üyeleri ve örnekleme ile ilgili şimdiye kadar en çok keyif aldım:

işte makale: http://www.sefol.com/?p=1090

ve işte örnek:

var Person = (function () {

    //Immediately returns an anonymous function which builds our modules 
    return function (name, location) {

        alert("createPerson called with " + name);

        var localPrivateVar = name;

        var localPublicVar = "A public variable";

        var localPublicFunction = function () {
            alert("PUBLIC Func called, private var is :" + localPrivateVar)
        };

        var localPrivateFunction = function () {
            alert("PRIVATE Func called ")
        };

        var setName = function (name) {

            localPrivateVar = name;

        }

        return {

            publicVar: localPublicVar,

            location: location,

            publicFunction: localPublicFunction,

            setName: setName

        }

    }
})();


//Request a Person instance - should print "createPerson called with ben"
var x = Person("ben", "germany");

//Request a Person instance - should print "createPerson called with candide"
var y = Person("candide", "belgium");

//Prints "ben"
x.publicFunction();

//Prints "candide"
y.publicFunction();

//Now call a public function which sets the value of a private variable in the x instance
x.setName("Ben 2");

//Shouldn't have changed this : prints "candide"
y.publicFunction();

//Should have changed this : prints "Ben 2"
x.publicFunction();

JSFiddle: http://jsfiddle.net/northkildonan/kopj3dt3/1/


Bu yaklaşımın önemli bir kaygısı vardır - 2 nesne oluşturduysanız, bellekte 2 aynı yöntem olacaktır (örneğin PublicFunction) 1000 nesne tüm belleğinizi yiyecektir.
Artem G

2

Modül düzeni çoğu durumda doğrudur. Ancak binlerce örneğiniz varsa, sınıflar hafızadan tasarruf eder. Belleği kaydetmek bir endişe kaynağıysa ve nesneleriniz az miktarda özel veri içeriyorsa, ancak çok sayıda genel işlev içeriyorsa, tüm genel işlevlerin belleği korumak için .prototype'de yaşamasını istersiniz.

Ben geldi budur:

var MyClass = (function () {
    var secret = {}; // You can only getPriv() if you know this
    function MyClass() {
        var that = this, priv = {
            foo: 0 // ... and other private values
        };
        that.getPriv = function (proof) {
            return (proof === secret) && priv;
        };
    }
    MyClass.prototype.inc = function () {
        var priv = this.getPriv(secret);
        priv.foo += 1;
        return priv.foo;
    };
    return MyClass;
}());
var x = new MyClass();
x.inc(); // 1
x.inc(); // 2

Nesne privözel özellikler içeriyor. Genel işlev aracılığıyla erişilebilir getPriv(), ancak bu işlev falsesiz onu geçmedikçe geri döner secretve bu yalnızca ana kapatma içinde bilinir.


Bu, korunan üyeleri simüle eder, ondan devralınan türler de korunan üyelere erişebilir. Bu kalıbı da özel
olana

2

Peki buna ne dersin?

var Restaurant = (function() {

 var _id = 0;
 var privateVars = [];

 function Restaurant(name) {
     this.id = ++_id;
     this.name = name;
     privateVars[this.id] = {
         cooked: []
     };
 }

 Restaurant.prototype.cook = function (food) {
     privateVars[this.id].cooked.push(food);
 }

 return Restaurant;

})();

Anında fonksiyonun kapsamı dışında özel değişken araması mümkün değildir. Hafızadan tasarruf eden fonksiyonların tekrarı yoktur.

Dezavantajı, özel değişkenlerin aramanın tıknaz privateVars[this.id].cookedolması yazım için saçma olmasıdır. Fazladan bir "id" değişkeni de vardır.


Bu bırakacaktır Restaurantolarak undefinedanonim işlevinden şey dönen değil çünkü.
user4815162342

Nerede ve nasıl? Oluşturulan Restorana yapılan başvurunun kaybolduğu varsayılırsa, privateVars'ın sahibine bir referansı olmaz. Referans grafik asikliktir. Neyi kaçırıyorum?
Evan Leis

Aslında yöntemlerin yanı sıra özel mülkleri destekleyen tek cevap budur. Yanıtta sadece iki konu zaten belirtilmiştir.
pishpish

Bir bellek sızıntısı görüyorum: bir örneği Restaurantçöp toplandığında, değerleri içeride kalır privateVars. Bu durumda A WeakMapiçin iyi bir yedek olabilir Array.
Gershom

2

Tüm kodu Anonim İşlev'e sarın: Daha sonra, tüm işlevler özel olacaktır, SADECE windownesneye bağlı işlevler :

(function(w,nameSpacePrivate){
     w.Person=function(name){
         this.name=name;   
         return this;
     };

     w.Person.prototype.profilePublic=function(){
          return nameSpacePrivate.profile.call(this);
     };  

     nameSpacePrivate.profile=function(){
       return 'My name is '+this.name;
     };

})(window,{});

Bunu kullan :

  var abdennour=new Person('Abdennour');
  abdennour.profilePublic();

VAKTİNİ BOŞA HARCAMAK


1

Özel verileri bir ilişkilendirmede saklamayı tercih ederim WeakMap. Bu, genel yöntemlerinizi ait oldukları prototip üzerinde tutmanıza olanak tanır. Bu, çok sayıda nesne için bu sorunu çözmenin en etkili yolu gibi görünüyor.

const data = new WeakMap();

function Foo(value) {
    data.set(this, {value});
}

// public method accessing private value
Foo.prototype.accessValue = function() {
    return data.get(this).value;
}

// private 'method' accessing private value
function accessValue(foo) {
    return data.get(foo).value;
}

export {Foo};

0

Özel işlevler modül deseni kullanarak ortak değişkenlere erişemez


0

Herkes buraya kendi kodunu gönderdiğinden, ben de yapacağım ...

Ben Crockford seviyorum çünkü o Javascript gerçek nesne odaklı desenler tanıttı. Ama aynı zamanda yeni bir yanlış anlama, "o" olanı buldu.

Öyleyse neden "bu = bu" kullanıyor? Özel işlevlerle hiçbir ilgisi yoktur. İç işlevlerle ilgili!

Çünkü Crockford'a göre bu hata kodu:

Function Foo( ) {
    this.bar = 0; 
    var foobar=function( ) {
        alert(this.bar);
    }
} 

Bunu yapmayı önerdi:

Function Foo( ) {
    this.bar = 0;
    that = this; 
    var foobar=function( ) {
        alert(that.bar);
    }
}

Dediğim gibi, Crockford'un bu ve bunun hakkındaki açıklaması yanlış olduğundan eminim (ancak kodu kesinlikle doğrudur). Yoksa sadece kodunu kimin kopyaladığını bilmek için Javascript dünyasını kandırıyor muydu? Bilmiyorum ... Ben tarayıcı geek değilim; D

DÜZENLE

Ah, hepsi bununla ilgilidir: 'var that = this;' JavaScript demek?

Bu yüzden Crockie açıklamasında gerçekten yanlıştı .... ama koduyla doğru, o yüzden hala harika bir adam. :))


0

Genel olarak nesneye geçici _ nesnesini ekledim. Yöntemin gizliliğini "Power-buildtor" da açmanız gerekir. Yöntemi prototipten çağırırsanız, prototip yönteminin üzerine yazabilirsiniz

  • Genel bir yöntemi "Güç yapıcısı" nda erişilebilir yapın: (ctx nesne bağlamıdır)

    ctx.test = GD.Fabric.open('test', GD.Test.prototype, ctx, _); // is a private object
  • Şimdi bu açık var

    GD.Fabric.openPrivacy = function(func, clss, ctx, _) {
        return function() {
            ctx._ = _;
            var res = clss[func].apply(ctx, arguments);
            ctx._ = null;
            return res;
        };
    };

0

Ben de bunu yaptım:

Burada bulabileceğiniz bir sınıf şeker kodu gerekir . Ayrıca korumalı, miras, sanal, statik şeyleri destekler ...

;( function class_Restaurant( namespace )
{
    'use strict';

    if( namespace[ "Restaurant" ] ) return    // protect against double inclusions

        namespace.Restaurant = Restaurant
    var Static               = TidBits.OoJs.setupClass( namespace, "Restaurant" )


    // constructor
    //
    function Restaurant()
    {
        this.toilets = 3

        this.Private( private_stuff )

        return this.Public( buy_food, use_restroom )
    }

    function private_stuff(){ console.log( "There are", this.toilets, "toilets available") }

    function buy_food     (){ return "food"        }
    function use_restroom (){ this.private_stuff() }

})( window )


var chinese = new Restaurant

console.log( chinese.buy_food()      );  // output: food
console.log( chinese.use_restroom()  );  // output: There are 3 toilets available
console.log( chinese.toilets         );  // output: undefined
console.log( chinese.private_stuff() );  // output: undefined

// and throws: TypeError: Object #<Restaurant> has no method 'private_stuff'

0
Class({  
    Namespace:ABC,  
    Name:"ClassL2",  
    Bases:[ABC.ClassTop],  
    Private:{  
        m_var:2  
    },  
    Protected:{  
        proval:2,  
        fight:Property(function(){  
            this.m_var--;  
            console.log("ClassL2::fight (m_var)" +this.m_var);  
        },[Property.Type.Virtual])  
    },  
    Public:{  
        Fight:function(){  
            console.log("ClassL2::Fight (m_var)"+this.m_var);  
            this.fight();  
        }  
    }  
});  

https://github.com/nooning/JSClass


0

Https://github.com/TremayneChrist/ProtectJS prototipinde gerçek özel yöntemlere sahip olmanıza izin vermek için yeni bir araç oluşturdum

Misal:

var MyObject = (function () {

  // Create the object
  function MyObject() {}

  // Add methods to the prototype
  MyObject.prototype = {

    // This is our public method
    public: function () {
      console.log('PUBLIC method has been called');
    },

    // This is our private method, using (_)
    _private: function () {
      console.log('PRIVATE method has been called');
    }
  }

  return protect(MyObject);

})();

// Create an instance of the object
var mo = new MyObject();

// Call its methods
mo.public(); // Pass
mo._private(); // Fail

1
Nasıl çalıştığını açıklayabilir misiniz lütfen? Nasıl / nerede olabilir aradığınız _privateyöntemi?
Bergi

0

Özel yöntemlerinizi tanımlayabileceğiniz gerçek yapıcı işlevinizin etrafına bir kapak koymanız gerekir. Bu özel yöntemlerle örneklerin verilerini değiştirmek için, onlara işlev argümanı olarak veya bu işlevi .apply (this) ile çağırarak onlara "this" vermeniz gerekir:

var Restaurant = (function(){
    var private_buy_food = function(that){
        that.data.soldFood = true;
    }
    var private_take_a_shit = function(){
        this.data.isdirty = true;   
    }
    // New Closure
    function restaurant()
    {
        this.data = {
            isdirty : false,
            soldFood: false,
        };
    }

    restaurant.prototype.buy_food = function()
    {
       private_buy_food(this);
    }
    restaurant.prototype.use_restroom = function()
    {
       private_take_a_shit.call(this);
    }
    return restaurant;
})()

// TEST:

var McDonalds = new Restaurant();
McDonalds.buy_food();
McDonalds.use_restroom();
console.log(McDonalds);
console.log(McDonalds.__proto__);

Aslında işe yaramıyor. Her new Restaurantbirinin kendine ait bir restaurantkuralı vardır ve "prototip" tamamen istismar edilir.
Bergi

@Bergi. Aslında haklısın. Çalışır ama aynı zamanda bir kaynak domuz olurdu (buna böyle denir mi?). Bu konuda cevabımı düzenledim.
Flex Elektro Deimling

Güncelleme için teşekkürler. Önceki sürüme ne diyeceğimi bilmiyorum (ama "hata" :-)
Bergi

0

Biraz geç olduğunu biliyorum ama buna ne dersin?

var obj = function(){
    var pr = "private";
    var prt = Object.getPrototypeOf(this);
    if(!prt.hasOwnProperty("showPrivate")){
        prt.showPrivate = function(){
            console.log(pr);
        }
    }    
}

var i = new obj();
i.showPrivate();
console.log(i.hasOwnProperty("pr"));

0

Bu soruya zaten birçok cevap var, ama hiçbir şey ihtiyaçlarımı karşılamıyordu. Bu yüzden kendi çözümümü buldum, umarım birisi için yararlıdır:

function calledPrivate(){
    var stack = new Error().stack.toString().split("\n");
    function getClass(line){
        var i = line.indexOf(" ");
        var i2 = line.indexOf(".");
        return line.substring(i,i2);
    }
    return getClass(stack[2])==getClass(stack[3]);
}

class Obj{
    privateMethode(){
        if(calledPrivate()){
            console.log("your code goes here");
        }
    }
    publicMethode(){
        this.privateMethode();
    }
}

var obj = new Obj();
obj.publicMethode(); //logs "your code goes here"
obj.privateMethode(); //does nothing

Gördüğünüz gibi bu sistem javascript bu tür sınıfları kullanırken çalışır. Ben anladım kadarıyla yukarıda yorumlanan yöntemlerin hiçbiri yaptı.


1
Meraklı: Gerçekten fonksiyonu ortaya çıkarmak ama diğer tüm cevaplar / çoğu gibi dış arayanlardan gizlemek yerine, çalışma zamanında bir op yapmak için ihtiyacın mıydı? Öyleyse neden? Bu yaklaşımın yararı olarak neyi düşünüyorsunuz? Bana göre, bu sadece gereksiz bir performans yükü, belirsiz bir API ve muhtemelen hata ayıklama cehennemine bağlı gibi görünüyor, ama her zaman yeni bakış açılarına açığım ...
JHH

2
@JHH dürüst olmak gerekirse, geriye baktığımda hemen hemen yüzünü kuruyorum. Tepegöz genellikle buna değmez, ancak benim için bu sınıflara çok fazla çağrı yapmadığım için çok önemli değildi. Bunu bu şekilde yapmamın nedeni, işlevleri yazma ve çağırma biçiminde nispeten temiz olmasıdır. O zamana kadar sembolleri ve böyle şeyleri anlamadım, ama şimdi anladığım kadarıyla, genellikle sınıfları kullanırken gitmenin yolu olduğunu düşünüyorum. Bu yanıtı hep birlikte kaldırmayı düşünüyorum. Birkaç aptalca cevap gönderdim, ama hey, yaşıyor ve öğreniyorsun.
thegunmaster

Geri dönüşünüz için teşekkür ederiz! Bir şeyi yanlış anladığımdan emin değildim. Ama evet, hepimiz yaşıyor ve öğreniyoruz!
JHH

0

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

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.