Приватні методи JavaScript


482

Щоб створити клас 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_fooduse_restroom методів і методів, але не зовні користувачами класу?

Іншими словами, я хочу, щоб моя реалізація методу:

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

Але це не повинно працювати:

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

Як визначити private_stuff як приватний метод, щоб обидва вони справджувались?

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

Відповіді:


403

Ви можете це зробити, але недоліком є ​​те, що він не може бути частиною прототипу:

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
Приховування тонкого покрову всередині перекладача не гарантуватиме конфіденційність для всіх перекладачів. Дивіться code.google.com/p/google-caja/wiki/…
Майк Семюель

51
@mikesamuel - правда, але лише тоді, коли у цих перекладачів є помилки в них :)
jvenema

133
Це добре приватний метод, але він, як правило, використовує набагато більше пам'яті, ніж звичайний метод-прототип, особливо якщо ви створюєте багато цих об'єктів. Для кожного екземпляра об'єкта він створює окрему функцію, пов'язану з об'єктом, а не класом. Також до цього не збирається сміття, поки сам об’єкт не буде знищений.
Аріндам

4
Якщо ви зробите об'єкт, який McDonalds () успадковує від Restaurant (), McDonalds не може перекрити приватні методи, якщо ви заявите їх таким чином. Ну, насправді ви можете, але це не спрацює, якщо якийсь інший метод викликає приватний, він викличе оригінальну версію методу, а також ви не можете викликати батьківський метод. Поки що я не знайшов хорошого способу оголосити приватні методи, які добре працюють із спадщиною. Це та наслідки для продуктивності робить це зовсім не дуже хорошим дизайном. Я б рекомендував робити якийсь префікс для позначення приватних методів, що подобається підкреслити підкресленням.
Гофманн

68
Приватні методи не повинні бути відмінені - вони приватні.
17 з 26

163

Використання функції самовикликання та дзвінка

JavaScript використовує прототипи і не має класів (або методів з цього питання), як об'єктно-орієнтовані мови. Розробнику JavaScript потрібно думати в JavaScript.

Цитата у Вікіпедії:

На відміну від багатьох об'єктно-орієнтованих мов, не існує різниці між визначенням функції та визначенням методу. Скоріше, розрізнення відбувається під час виклику функції; коли функція викликається як метод об'єкта, локальна функція цього ключового слова пов'язана з цим об'єктом для цього виклику.

Рішення за допомогою функції самовикликання та виклику для виклику приватного "методу":

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 з Оператором зв’язку

Оператор прив'язки ::є пропозицією 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

34
Це найкраща відповідь. Переваг приватних методів, жодної барахли.
TaylorMac

1
@TaylorMac За винятком .callчастини.
пішпіш

1
@janje Ху? У цьому питання, f()в JavaScript немає приватного (не на даний момент).
Ів М.

2
@YvesM Суть питання - вибір найкращого шаблону, який його моделює.
пішпіш

1
@changed Який найкращий компроміс - мати приховані функції (не викликає ззовні), приємний синтакс (ні .call) та невелику пам’ять (без функцій у примірнику)? Це навіть існує?
Ciprian Tomoiagă

161

Ви можете імітувати приватні методи на зразок цього:

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


7
Я б також запропонував сайт Дугласа Крокфорда в якості ресурсу щодо приватних / публічних методів та членів javascript.crockford.com/private.html
Джаред

10
Він згадував про це посилання у питанні.
Гульзар Назим

8
Мінусом цього методу є те, що ви не могли мати доступ до приватних даних у ресторані private_stuff (), а інші методи ресторану не можуть викликати private_stuff (). Переваги полягають у тому, що якщо вам не потрібен жоден із умов, про який я вже згадував, ви можете зберегти use_restroom () в прототипі.
17 з 26

6
Це має бути рішенням та прийнятою відповіддю, оскільки автор чітко використовує властивість прототипу.
Габріель Лламас

23
За схемою, запропонованою @georgebrock, всі приватні дані будуть обмінюватися між усіма ресторанними об'єктами. Це схоже на статичні приватні змінні та функції в OOP на основі класу. Я припускаю, що ОП цього не хоче. Для ілюстрації того, що я маю на увазі, я створив jsFiddle .
feklee

35

У таких ситуаціях, коли у вас є загальнодоступний 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

подібні речі не будуть виявлені автозаповненням IDE :(
Натисніть Upvote

19
Але це не клас, тому ви не можете мати 2 "екземпляри" цього з різними станами.
DevAntoine

видаліть () частину, і у вас є "клас". принаймні там, де можна інстанціювати різні інстанції з різними станами. шаблон модуля досить інтенсивний, хоча ...
oligofren

@DevAntoine Подивіться на коментарі до відповіді 17 з 26. У JavaScript-розширюваних класах та приватних методах не легко йти рука об руку. Моя пропозиція в цьому випадку полягає в тому, щоб перейти зі складом над спадщиною. Створіть розширюваний прототип тими ж методами, що і доданий бетонний об'єкт. Тоді внутрішньо у вашому прототипі ви можете вирішити, коли слід викликати методи вашого конкретного об'єкта.

Чи є якийсь мінус виклику загальнодоступних змінних з приватних функцій на зразок aPrivateMethod = function() { MYLIB.aPublicProperty}:?
Ганна

21

Ось клас, який я створив, щоб зрозуміти, що запропонував Дуглас Крокфорд на своєму веб-сайті приватних членів у JavaScript

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
Це = це досить поширена закономірність, яку популяризував вищезгаданий Крокфорд у своїй книзі "Javascript: хороші частини"
олігофрен

8
thatвикористовується замість того, thisщоб уникнути проблем із масштабуванням, коли функції пов'язані з іншим об'єктом. Тут ви зберігаєте thisв thatі ніколи не використовувати його знову , яка є такою ж , як не робити цього взагалі. Ви повинні змінюватись thisз thatусіма Constructorвнутрішніми функціями (не декларуванням методів). Якщо employeeце applyед або callед яким - то чином ці методи можуть кинути , так як thisбудуть неправильно пов'язані.
Марошій

Крім того, кожен екземпляр матиме повну копію приватних функцій, неефективну. Це, на додаток до того, що публічні методи не можуть отримати доступ до приватних класів, змушує мене перейти на dart. На жаль, angulardart - це супер бета.
Рей Фосс

Де в цьому метод "конструктор"? Де я б розмістив логіку, яка, як правило, виконується в конструкторському методі класу, коли він створений?
BadHorsie

13

Я придумав це: EDIT: Насправді хтось пов’язав ідентичне рішення. Да!

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
Це приємна методика, але як би ви дозволили параметри у своєму конструкторі? Наприклад, var getAwayVehicle = new Car(100);де 100швидкість, і ви хочете попередити швидкість. Дякую!
Джейсон

1
Зрозумів це, може мати var Car = function(speed) { this.speed = speed; }і `повернутися {конструктор: Автомобіль, ... '
Джейсон

11

Я думаю, такі питання виникають знову і знову через нерозуміння закриттів. Закривання - це найважливіше в 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')

Я сподіваюся, що це допоможе комусь краще зрозуміти цю тему


2
Те, що ви маєте в пункті 4, - дивовижне! Я думаю, що це єдина відповідь з усіх тих, хто тут, де ви отримуєте як збільшення продуктивності / пам’яті за допомогою методів прототипу І ці публічні методи мають повний доступ до приватних членів. +1
Хадон

7
Це не працює. Змінна назви тут діє як статична змінна і її поділяють усі екземпляри ресторану. Ось jsbin: jsbin.com/oqewUWa/2/edit?js,output
Shital Shah

це гарна спроба, але, як зазначив Shital, змінна назва - помилка.
олігофрен

2
додавання мого 2с сюди, щоб підтвердити це не працює, виглядає добре, але, як було зазначено вище, "ім'я" буде служити статичною змінною, тобто поділяється для всіх примірників
Пол Керролл

10

Особисто я віддаю перевагу такій схемі створення класів у 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();

Дивіться також цю скрипку .


Це змушує мене використовувати класи es6 і бачити, що це також перекладає
sheriffderek

9

Все це закриття обійдеться вам. Переконайтесь, що ви протестуєте наслідки швидкості, особливо в IE. Ви побачите, що вам краще погодитись з умовами іменування. Є ще багато корпоративних користувачів Інтернету, які змушені використовувати IE6 ...


34
Кого турбує, серйозно?
nowayyy

17
Ті 9%, які все ще використовують IE6, не хвилюються щодо швидкості, оптимізації та всіх сучасних функцій HTML5. Тож закриття нічого не коштуватиме.
Габріель Лламас


7
@LorenzoPolidori w3schools користувачів! == корпоративних веб-користувачів;]
WynandB

Конвенція про іменування (наприклад: попередження підкреслення) - це шлях. Код простіший у обслуговуванні, а методи все ще визначені на прототипі. На сьогоднішній день ... Я просто позначу метод як приватний в машинописі.
Девід Шеррет

5

Не будь настільки багатослівним. Це Javascript. Використовуйте Конвенцію про іменування .

Після років роботи в класах es6 я нещодавно розпочав роботу над проектом es5 (використовуючи RequJS, який вже дуже докладно виглядає). Я постійно і знову перераховував усі стратегії, згадані тут, і все це зводиться до використання конвенції про іменування :

  1. У JavaScript немає таких ключових слів private . Інші розробники, що входять у Javascript, будуть знати це заздалегідь. Тому простого узгодження імен більш ніж достатньо. Просте узгодження імен префіксації з підкресленням вирішує проблему як приватних властивостей, так і приватних методів.
  2. Давайте скористаємося Прототипом з міркувань швидкості, але давайте не зможемо отримати більше багатослівного, ніж цього. Давайте спробуємо тримати "клас" es5 так само уважно, як ми можемо очікувати в інших мовах резервного копіювання (і розглянемо кожен файл як клас, навіть якщо нам не потрібно повертати екземпляр).
  3. Продемонструємо більш реалістичну ситуацію з модулем (ми будемо використовувати старі es5 та старі RequJs).

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
Слід зазначити, що ні мова Javascript, ні будь-який типовий хостинг браузера не визначають жодних об’єктів, які покладаються на імена конвенцій для "приховування" приватного стану, тому, хоча ви можете мати рацію, що розробники зрозуміють цю концепцію, це все одно призводить до дуже не Підхід ОО до програмування ОО.
багатий ремер

Чи можу я попросити гарного посилання на виготовлення таких? На цьому прикладі є деталі, які є для мене новими. Сама defineта constructorструктура. Хоча я погоджуюсь здебільшого на відповідь, я почав працювати з JS з великим впливом на ООП і навіть занадто рано підскочив до ТС, оскільки мав попередній досвід роботи на C #. Я думаю, що мені доведеться вивчити ці матеріали та зрозуміти парадигму прототипу / процедур. (upvoted, btw)
Cold Cerberus

1
@ColdCerberus У цьому фрагменті коду використовується es5. Повну картину такого підходу ви можете побачити тут: gist.github.com/jonnyreeves/2474026 . Але майте на увазі, ви захочете скористатися цим підходом і оновити його за допомогою класів es6 : googlechrome.github.io/samples/classes-es6 та es6 модулів (синтаксис імпорту / експорту): hackernoon.com/…
programhammer

5

Це можна зробити зараз приватними методами es10 . Вам потрібно просто додати назву #перед назвою методу.

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

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

2
За винятком цього є етап 3 і ще офіційно не є частиною мови.
misterhtmlcss

3

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

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-дзвінків.


Це серйозне питання, яке має бути більш відомим. Чи є "захист" від цієї атаки?
Джеймс

@James Жоден, про який я знаю, я думаю, що це природа звіра. Як я зазначив, ви можете перемістити функціональність на сервер, де він працює в захищеному середовищі. Справа, яку я хотів подолати у своїй відповіді, - це те, що рішення Крокфорда не допомагає, зайво ускладнює код і приховує необхідність щось робити.
Фозі

Якщо користувач вводить секретний пароль, він не може це зробити на стороні сервера. У якийсь момент пароль буде в "приватному" варі. То чи може зловмисник це прочитати? Я довіряю своєму коду, і в будь-якому випадку мої домашні стандарти не дозволяють eval (). Зловмисник може бути зловмисним додатком JavaScript або бібліотекою, яку я не перевірив належним чином - так, так, мені потрібно це перевірити. Зловмисник також може бути чимось на зразок реклами збоку, яка не повинна взаємодіяти з моїм кодом. Я захищаю від цього, загортаючи весь свій код в анонімний, (function () {allMyStuff}());щоб не піддавати нічого глобальному.
Джеймс

@James Це отримує ОТ, якщо ви хочете продовжити це, будь ласка, відкрийте нове запитання. Так, зловмисник може прочитати пароль. З вашої "приватної" змінної. Або від DOM. Або він може замінити API AJAX. Або він замінює вашу сторінку чимось іншим. Якщо він не може зробити що-небудь з перерахованого вище, тоді немає необхідності в "конфіденційності" JS, оскільки він також не може прочитати жодну з ваших змінних JS. Справа в тому, що «рішення» Крокфорда, яке зараз використовують усі, не допомагає в цій проблемі.
Фозі

Я вважаю, що псевдослучайне затухання коду може бути слабким захистом від цієї атаки - складніше змінювати тіло функції, коли ви не можете залежати від функції, яка має фіксовану назву; важче зробити, f.eval('nameOfVariable')коли ти не знаєш, що 'nameOfVariable'таке ...
Гершом,


2

Якщо ви хочете, щоб повний спектр публічних та приватних функцій мав можливість публічним функціям отримувати доступ до приватних функцій, кодуйте макет для такого об’єкта:

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

Може хтось скаже мені, чому це було проголошено? Мені добре виглядає.
thomasrutter

10
Кожен раз, коли ви робите це new MyObject, прототип протоколу MyObjectзамінюється однаковими значеннями.
bpierre

2
-1. Ніколи не призначайте .prototypeконструктор всередині.
Бергі

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, але трохи менш багатослівний (ІМХО) Якісь проблеми з цим способом? (Я його ніде не бачив)

редагувати: Я зрозумів, що це свого роду марно, оскільки кожна незалежна інстанція має власну копію публічних методів, тим самим підриваючи використання прототипу.


2

Ось що мені найбільше сподобалось щодо приватних / публічних методів / членів та інстанції в 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 об'єкти, у пам'яті залишиться 2 однакові метри (PublicFunction для напр.) 1000 об'єктів з'їдуть всю вашу пам'ять.
Артем Г

2

Шаблон модуля в більшості випадків правильний. Але якщо у вас є тисячі примірників, класи зберігають пам’ять. Якщо збереження пам’яті викликає занепокоєння, а ваші об’єкти містять невелику кількість приватних даних, але мають багато публічних функцій, тоді ви хочете, щоб усі публічні функції жили в .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передаєте її , і це відомо лише всередині основного закриття.


Це імітує захищені члени, типи, що успадковуються від нього, можуть отримати доступ до захищених членів. Я віддаю перевагу цій схемі і над приватною
HMR

2

Як що до цього?

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


Це залишиться Restaurantтак, undefinedтому що ви нічого не повертаєте з анонімної функції.
user4815162342

Де і як? Якщо припустити, що посилання на створений Ресторан втрачена, privateVars не матиме посилання на свого власника. Опорний графік є ациклічним. Що я пропускаю?
Еван Лейс

Насправді це єдина відповідь, яка крім методів підтримує приватні властивості. Єдині два питання вже зазначаються у відповіді.
пішпіш

Я бачу витік пам'яті: коли екземпляр Restaurantзібраний сміття, його значення залишаються в межах privateVars. А WeakMapможе бути хорошою заміною для цього Arrayв цьому випадку.
Гершом

2

Оберніть увесь код в анонімну функцію: тоді всі функції будуть приватними, ТІЛЬКИ функції додані до 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();

ПІДТВОРЕННЯ


1

Я вважаю за краще зберігати приватні дані в пов'язаних 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};

0

Приватні функції не можуть отримати доступ до загальнодоступних змінних за допомогою шаблону модуля


0

Оскільки всі публікували тут свій код, я теж буду це робити ...

Мені подобається Крокфорд, тому що він представив реальні об'єктно-орієнтовані моделі в Javascript. Але він також придумав нове непорозуміння, "те".

То чому він використовує "що = це"? Це взагалі не має нічого спільного з приватними функціями. Це стосується внутрішніх функцій!

Тому що, за словами Крокфорда, це баггі-код:

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

Тому він запропонував зробити це:

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

Тому, як я вже говорив, я цілком впевнений, що Крокфорд помилився своїм поясненням щодо цього і цього (але його код, безумовно, правильний). Або він просто обманював світ Javascript, щоб знати, хто копіює його код? Я не знаю ... Я не видовище браузера; D

EDIT

Ах, ось про що йдеться: Що означає 'var що = це;' означає в JavaScript?

Тож Кроккі справді помилявся зі своїм поясненням .... але правильно зі своїм кодом, тож він все ще чудовий хлопець. :))


0

Загалом я до об’єкту тимчасово додав приватний Object _. Ви повинні відкрито конфіденційність відкрити в "Power-конструктор" для методу. Якщо ви викликаєте метод з прототипу, ви зможете перезаписати прототип-метод

  • Зробити загальнодоступний метод доступним у "Power-конструкторі": (ctx - це об'єктний контекст)

    ctx.test = GD.Fabric.open('test', GD.Test.prototype, ctx, _); // is a private object
  • Тепер у мене є ця відкрита приватність:

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

0

Це те, що я розробив:

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

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

Приклад:

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
Чи можете ви пояснити, як це працює? Як / де можна викликати _privateметод?
Бергі

0

Ви повинні закрити навколо фактичної функції конструктора, де ви можете визначити свої приватні методи. Щоб змінити дані про екземпляри за допомогою цих приватних методів, ви повинні надати їм це "це", або як аргумент функції, або викликавши цю функцію за допомогою .apply (this):

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

Насправді це не працює. У кожного new Restaurantбуде свій restaurantконструктор, і "прототипом" повністю зловживають.
Бергі

@Bergi. Насправді ти маєш рацію. Він би працював, але також був би кобашкою з ресурсами (це називається так?). Я змінив свою відповідь щодо цього.
Flex Elektro Deimling

Дякуємо за оновлення. Поняття не маю, як називати попередню версію (але "помилка" :-)
Бергі

0

Я знаю, що трохи пізно, але як щодо цього?

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

На це питання вже є багато відповідей, але нічого не відповідало моїм потребам. Тож я придумав власне рішення, сподіваюся, що це комусь корисно:

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. Наскільки я зрозумів, жоден із коментарів, описаних вище, не робив.


1
Цікаво: Чи справді ваша потреба була фактично розкрити функцію, але зробити її безрезультатною під час виконання - а не приховувати її від зовнішніх абонентів, як це роблять усі / більшість інших відповідей? Якщо так, то чому? Що ви вважаєте користю такого підходу? Мені це здається просто непотрібним накладним рівнем продуктивності, незрозумілим API та, мабуть, мабуть призведе до налагодження пекла, але я завжди відкритий до нових перспектив ...
JHH

2
@JHH чесно кажучи, я дуже сильно стикаюся з обличчям, коли озираюся на це. Накладні витрати взагалі не варті цього, хоча для мене це не мало великого значення, оскільки я не робив багато дзвінків на ці заняття. Причина, що я зробив це таким чином, полягає лише в тому, що вона є відносно чистою в тому, як ви пишете та викликаєте функції. Я до цього часу не розумів символів та подібних значень, але тепер, коли це роблю, я думаю, що, як правило, це шлях до використання занять. Я розглядаю можливість усунути цю відповідь разом. Я розмістив кілька дурних відповідей, але ей, ти живеш і вчишся.
thegunmaster

Дякуємо за відгук! Я не був впевнений, чи щось зрозумів неправильно. Але так, ми всі живемо і вчимося!
JHH

0

Дивіться цю відповідь для чистого та простого "класового" рішення з приватним та загальнодоступним інтерфейсом та підтримкою композиції

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.