Частные методы JavaScript - PullRequest
       152

Частные методы JavaScript

435 голосов
/ 11 сентября 2008

Чтобы создать класс JavaScript с открытым методом, я бы сделал что-то вроде:

function Restaurant() {}

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

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

Таким образом, пользователи моего класса могут:

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

Как создать приватный метод, который может вызываться методами buy_food и use_restroom, но не внешне пользователями класса?

Другими словами, я хочу, чтобы моя реализация метода могла выполнять:

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

Но это не должно работать:

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

Как мне определить private_stuff как закрытый метод, чтобы оба они выполнялись?

Я прочитал рецензию Дуга Крокфорда несколько раз, но не похоже, что "частные" методы могут вызываться открытыми методами, а "привилегированные" методы могут вызываться извне.

Ответы [ 29 ]

2 голосов
/ 31 октября 2013

Как насчет этого?

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;

})();

Поиск закрытой переменной невозможен за пределами непосредственной функции. Нет дублирования функций, экономия памяти.

Недостатком является то, что поиск частных переменных неуклюж, privateVars[this.id].cooked смешно вводить. Также есть дополнительная переменная id.

2 голосов
/ 10 декабря 2014

Вот что мне больше всего понравилось в приватных / публичных методах / членах и реализации в javascript:

вот статья: http://www.sefol.com/?p=1090

и вот пример:

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/

2 голосов
/ 02 февраля 2015

Шаблон модуля в большинстве случаев правильный. Но если у вас есть тысячи экземпляров, классы сохраняют память. Если сохранение памяти является проблемой, и ваши объекты содержат небольшое количество личных данных, но имеют много открытых функций, тогда вы захотите, чтобы все открытые функции жили в .prototype для экономии памяти.

Вот что я придумал:

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

Объект priv содержит частные свойства. Он доступен через публичную функцию getPriv(), но эта функция возвращает false, если вы не передадите ей secret, и это известно только внутри основного замыкания.

2 голосов
/ 29 сентября 2008

Если вы хотите, чтобы весь спектр открытых и закрытых функций имел возможность доступа к закрытым функциям для открытых функций, используйте код макета для такого объекта:

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 () {
  };
}
2 голосов
/ 11 сентября 2008

Апофеоз шаблона модуля: Выявление шаблона модуля

Небольшое аккуратное продолжение очень прочного рисунка.

1 голос
/ 18 апреля 2011
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, но немного менее многословно (ИМХО) Есть проблемы с этим? (Я нигде не видел)

edit: я понял, что это несколько бесполезно, поскольку каждая независимая реализация имеет свою собственную копию открытых методов, что подрывает использование прототипа.

1 голос
/ 23 июля 2018

Не будь таким многословным. Это Javascript. Используйте Соглашение об именах .

После нескольких лет работы в классах es6, я недавно начал работать над проектом es5 (используя requireJS, который уже очень многословен). Я перебираю все упомянутые здесь стратегии, и все сводится к тому, что используется соглашение об именах :

  1. В Javascript нет ключевых слов, например private. Другие разработчики, входящие в Javascript, будут знать об этом заранее. Следовательно, простого соглашения об именах более чем достаточно. Простое соглашение по именованию префикса с подчеркиванием решает проблему как закрытых свойств, так и закрытых методов.
  2. Давайте воспользуемся Прототипом по соображениям скорости, но давайте не будем более многословны, чем это. Давайте постараемся, чтобы «класс» es5 выглядел так же близко к тому, что мы могли ожидать в других языках бэкэнда (и рассматривал каждый файл как класс, даже если нам не нужно возвращать экземпляр).
  3. Давайте продемонстрируем более реалистичную ситуацию с модулем (мы будем использовать старые es5 и старые requireJs).

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();
            }
            */
        }
    });
1 голос
/ 25 июня 2015

Обернуть весь код в анонимную функцию: тогда все функции будут частными, ТОЛЬКО функции, прикрепленные к window объекту:

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

Используйте это:

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

FIDDLE

0 голосов
/ 14 августа 2013

Это то, что я разработал:

Требуется один класс кода сахара, который вы можете найти здесь . Также поддерживает защищенные, наследования, виртуальные, статические вещи ...

;( 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 голосов
/ 16 февраля 2018

Я предпочитаю хранить личные данные в ассоциированном WeakMap. Это позволяет вам хранить ваши открытые методы на прототипе, к которому они относятся. Похоже, это наиболее эффективный способ решения этой проблемы для большого количества объектов.

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