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

Чтобы создать класс 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 как частный метод, так что оба они верны?

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

36 ответов

Решение

Вы можете сделать это, но недостатком является то, что он не может быть частью прототипа:

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

Использование самовозвратной функции и вызова

JavaScript использует прототипы и не имеет классов (или методов в этом отношении), таких как объектно-ориентированные языки. Разработчик JavaScript должен думать на JavaScript.

Цитата из Википедии:

В отличие от многих объектно-ориентированных языков, нет различия между определением функции и определением метода. Скорее различие происходит во время вызова функции; когда функция вызывается как метод объекта, локальное ключевое слово this этой функции привязывается к этому объекту для этого вызова.

Решение, использующее функцию самозапуска и функцию вызова для вызова частного "метода":

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

Функция вызова позволяет нам вызывать приватную функцию с соответствующим контекстом (this).


Проще с Node.js

Если вы используете node.js, вам не нужен IIFE, потому что вы можете воспользоваться системой загрузки модулей:

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

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

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

exports.MyObject = MyObject;

Загрузите файл:

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

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


(экспериментальный) ES7 с оператором Bind

Оператор связывания :: является предложением ECMAScript и реализовано в Babel ( этап 0).

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

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

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

Загрузите файл:

import MyObject from './MyObject';

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

Вы можете смоделировать частные методы, как это:

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

Больше информации об этой технике здесь: http://webreflection.blogspot.com/2008/04/natural-javascript-private-methods.html

В этих ситуациях, когда у вас есть общедоступный API, и вам нужны частные и общедоступные методы / свойства, я всегда использую Шаблон модуля. Этот шаблон стал популярным в библиотеке YUI, а подробности можно найти здесь:

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

Это действительно просто и легко для других разработчиков. Для простого примера:

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

Теперь вы можете сделать это с помощью частных методов es10. Вам просто нужно добавить# перед названием метода.

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

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

Вот класс, который я создал, чтобы понять, что Дуглас Крокфорд предложил на своем сайте.

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

}

ES2021 / ES12 - Частные методы

Имена частных методов начинаются с хеша #префикс и доступен только внутри класса, в котором он определен.

class Restaurant {

  // private method
  #private_stuff() {
    console.log("private stuff");
  }

  // public method
  buy_food() {
    this.#private_stuff();
  }

};

const restaurant = new Restaurant();
restaurant.buy_food(); // "private stuff";
restaurant.private_stuff(); // Uncaught TypeError: restaurant.private_stuff is not a function

Это экспериментальное предложение. Ожидается, что версия ECMAScript 2021 будет выпущена в июне 2021 года.

Я придумал это: РЕДАКТИРОВАТЬ: На самом деле, кто-то связал с тем же решением. 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!'

Я думаю, что такие вопросы возникают снова и снова из-за непонимания замыканий. Закрытие - самая важная вещь в JS. Каждый программист JS должен чувствовать суть этого.

1. Прежде всего нам нужно сделать отдельную область (закрытие).

function () {

}

2. В этой области мы можем делать все, что захотим. И никто не узнает об этом.

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. Чтобы мир узнал о нашем ресторанном классе, мы должны вернуть его из закрытия.

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

4. В итоге имеем:

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. Кроме того, этот подход имеет потенциал для наследования и шаблонирования

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

Я надеюсь, что это помогает кому-то лучше понять эту тему

Лично я предпочитаю следующий шаблон для создания классов в JavaScript:

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

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


демонстрация

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

Смотрите также эту скрипку.

Все это закрытие обойдется вам. Убедитесь, что вы протестировали значения скорости, особенно в IE. Вы найдете, что вам лучше с соглашением об именах. Все еще есть много корпоративных веб-пользователей, которые вынуждены использовать IE6...

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

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

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

мой-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();
            }
            */
        }
    });

Возьмите любое из решений, которые следуют частному или привилегированному образцу Крокфорда. Например:

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

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

В любом случае, когда злоумышленник не имеет права "выполнить" в контексте JS, у него нет возможности получить доступ к каким-либо "открытым" или "закрытым" полям или методам. Если у злоумышленника есть такой доступ, он может выполнить следующую однострочную строку:

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

Обратите внимание, что приведенный выше код является общим для всех конструкторов-типов-конфиденциальности. Он потерпит неудачу с некоторыми решениями здесь, но должно быть ясно, что почти все решения, основанные на замыкании, могут быть разбиты таким образом с разными replace() параметры.

После этого выполняется любой объект, созданный с new Foo() будет иметь eval метод, который можно вызвать для возврата или изменения значений или методов, определенных в замыкании конструктора, например:

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

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

На мой взгляд, это в значительной степени делает решение Крокфорда бесполезным. Поскольку "конфиденциальность" легко нарушается, недостатки его решения (сниженная читаемость и удобство обслуживания, сниженная производительность, увеличенная память) делают метод, основанный на прототипах "без конфиденциальности", лучшим выбором.

Я обычно использую ведущие подчеркивания, чтобы отметить __private а также _protected методы и поля (стиль Perl), но идея конфиденциальности в JavaScript просто показывает, как это неправильно понимаемый язык.

Поэтому я не согласен с Крокфордом, за исключением его первого предложения.

Так как же получить настоящую конфиденциальность в JS? Поместите все, что требуется для приватности на стороне сервера, и используйте JS для выполнения вызовов AJAX.

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

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

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

Вот что мне больше всего понравилось в отношении приватных / публичных методов / членов и создания экземпляров в 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/

Оберните весь код в анонимную функцию: тогда все функции будут частными, ТОЛЬКО функции, прикрепленные к 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

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

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

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

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

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

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

Я предпочитаю хранить личные данные в связанном 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};

2021 ЗДЕСЬ!

Этот полифилл эффективно скрывает ваши частные свойства и методы, возвращающие undefined, когда вы пытаетесь прочитать свое частное свойство, и TypeError, когда вы пытаетесь выполнить свой частный метод, тем самым эффективно делая их оба ЧАСТНЫМИ снаружи, но предоставляя вам доступ к ним с помощью ваших общедоступных методов. .

Если вы проверите это, то увидите, что это очень легко реализовать. По большей части вам не нужно делать ничего необычного, например, использовать прокси-объекты, функции подчеркивания (_myprivate), геттеры или сеттеры. Ничего подобного. Единственное, что требуется, - это разместить в своем конструкторе такой фрагмент кода, который предназначен для того, чтобы вы могли открыть свой общедоступный интерфейс внешнему миру.

      ((self) => ({
      pubProp: self.pubProp,
      // More public properties to export HERE
      // ...
      pubMethod: self.pubMethod.bind(self)
      // More public mehods to export HERE
      // Be sure bind each of them to self!!!
      // ... 
 }))(self);

В приведенном выше коде происходит волшебство. Это IIFE, который возвращает объект только с теми свойствами и методами, которые вы хотите предоставить и привязанными к контексту объекта, который был создан первым.

Вы по-прежнему можете получить доступ к своим скрытым свойствам и методам, но только через свои общедоступные методы, как и должно быть в ООП.
Считайте эту часть кода своим module.exports

Кстати, это без использования последнего дополнения ES6 "#" к языку.

Проверить мою суть

Уродливое решение, но оно работает:

function Class(cb) {
    const self = {};

    const constructor = (fn) => {
        func = fn;    
    };

    const addPrivate = (fnName, obj) => {
        self[fnName] = obj;
    }

    const addPublic = (fnName, obj) => {
        this[fnName] = obj;
        self[fnName] = obj;
        func.prototype[fnName] = obj;
    }
    
    cb(constructor, addPrivate, addPublic, self);
    return func;
}

const test = new Class((constructor, private, public, self) => {
    constructor(function (test) {
        console.log(test)
    });
    public('test', 'yay');
    private('qwe', 'nay');
    private('no', () => {
        return 'hello'
    })
    public('asd', () => {
        return 'this is public'
    })
    public('hello', () => {
        return self.qwe + self.no() + self.asd()
    })
})
const asd = new test('qweqwe');
console.log(asd.hello());

Старый вопрос, но это довольно простая задача, которую можно правильно решить с помощью основного JS... без абстракции класса ES6. На самом деле, насколько я могу судить, абстракция класса даже не решает эту проблему.

Мы можем выполнить эту работу как с помощью старой доброй функции конструктора, так и, что еще лучше, с помощью. Сначала займемся конструктором. По сути, это будет решение, аналогичное ответу Джорджброка, который подвергается критике, потому что все рестораны, созданные Restaurantконструктор будет иметь такие же частные методы. Я постараюсь преодолеть это ограничение.

Теперь очевидно, что мы не можем получить доступ menu извне, но мы можем легко переименовать name собственность ресторана.

Это также можно сделать с помощью Object.create() в этом случае мы пропускаем функцию конструктора и просто делаем как var rest = Object.create(prototypeFactory(menu)) и добавьте свойство name в rest объект впоследствии как rest.name = name.

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

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

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

Я знаю, что уже слишком поздно, но как насчет этого?

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

В общем, я временно добавил приватный объект _. Вы должны открыть конфиденциальность в "Power-constructor" для метода. Если вы вызовете метод из прототипа, вы сможете перезаписать метод prototype

  • Сделайте публичный метод доступным в "Power-constructor": (ctx - контекст объекта)

    ctx.test = GD.Fabric.open('test', GD.Test.prototype, ctx, _); // is a private object
    
  • Теперь у меня есть эта openPrivacy:

    GD.Fabric.openPrivacy = function(func, clss, ctx, _) {
        return function() {
            ctx._ = _;
            var res = clss[func].apply(ctx, arguments);
            ctx._ = null;
            return res;
        };
    };
    
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

Я создал новый инструмент, который позволит вам иметь настоящие личные методы на прототипе https://github.com/TremayneChrist/ProtectJS

Пример:

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

На этот вопрос уже есть много ответов, но ничто не соответствовало моим потребностям. Поэтому я придумала собственное решение, надеюсь, оно кому-нибудь пригодится:

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

Как вы можете видеть, эта система работает при использовании этого типа классов в JavaScript. Насколько я понял, ни один из методов, упомянутых выше, не сделал.

Другие вопросы по тегам