Частные методы 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_foodи, use_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();
    }
}
17 из 26
источник
9
Сокрытие тонкости в закрытии не гарантирует конфиденциальность для всех переводчиков. См. Code.google.com/p/google-caja/wiki/…
Майк Сэмюэль,
51
@mikesamuel - правда, но только когда у этих переводчиков есть ошибки :)
jvenema
133
Это частный метод, но он будет занимать гораздо больше памяти, чем обычный метод-прототип, особенно если вы создаете много этих объектов. Для каждого экземпляра объекта создается отдельная функция, связанная с объектом, а не с классом. Кроме того, это не собирает мусор, пока сам объект не будет уничтожен.
Ариндам
4
Если вы создаете объект, который McDonalds () наследует от Restaurant (), McDonalds не сможет переопределить частные методы, если вы объявите их таким образом. Ну, на самом деле вы можете, но он не будет работать, если какой-то другой метод вызовет приватный, он вызовет оригинальную версию метода, и вы также не можете вызвать родительский метод. До сих пор я не нашел хорошего способа объявить частные методы, которые хорошо работают с наследованием. Это и влияние на производительность делают этот шаблон дизайна не очень хорошим. Я бы порекомендовал сделать какой-то префикс для обозначения приватных методов, например, с подчеркиванием.
Хоффманн
68
Частные методы не должны быть переопределены - они являются частными.
17 из 26
163

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

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

call позволяет нам вызывать приватную функцию с соответствующим context ( 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
Ив М.
источник
34
Это лучший ответ. Преимущества частных методов, без мусора.
TaylorMac
1
@TaylorMac За исключением .callчасти.
фишпиш
1
@janje А? В том-то и дело, что f()в javascript нет приватного (не на данный момент).
Ив М.
2
@YvesM. Суть вопроса заключается в выборе лучшего шаблона, который имитирует его.
pishpish
1
@ Изменен, что является лучшим компромиссом, чтобы иметь скрытые функции (не подлежащие вызову извне), хороший синтаксис (нет .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

georgebrock
источник
7
Я также хотел бы предложить сайт Дугласа Крокфорда в качестве ресурса по частным / общедоступным методам и членам javascript.crockford.com/private.html
Jared
10
Он упомянул эту ссылку в вопросе.
Гульзар Назим
8
Недостатком этого метода является то, что вы не можете иметь private_stuff () для доступа к другим частным данным в Restaurant, а другие методы Restaurant не могут вызывать private_stuff (). Плюс в том, что если вам не нужно ни одно из условий, которые я только что упомянул, вы можете оставить use_restroom () в прототипе.
17 из 26
6
Это должно быть решением и принятым ответом, потому что автор явно использует свойство prototype.
Габриэль Ламас
23
С помощью шаблона, предложенного @georgebrock, все личные данные будут доступны всем объектам ресторана. Это похоже на статические частные переменные и функции в ООП на основе классов. Я предполагаю, что ОП не хочет этого. Чтобы проиллюстрировать, что я имею в виду, я создал jsFiddle .
Фекли
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: хорошие части»
oligofren
8
thatиспользуется вместо того, thisчтобы избежать проблем с областью видимости, когда функции связаны с другим объектом. Здесь вы храните thisв thatи никогда не использовать его снова , которая является такой же , как не делать этого вообще. Вы должны изменить thisсо thatвсей Constructorвнутренней функции (не методы декларации). Если employeeэто applyed или called каким-то образом, эти методы могут thisвызвать, так как будут неправильно связаны.
Мароший
Также каждый экземпляр будет иметь полную копию закрытых функций, неэффективно. Это в дополнение к тому факту, что публичные методы не могут получить доступ к частным классам, заставляет меня хотеть переключиться на dart. К сожалению, ангулардарт супер бета.
Рэй Фосс
Где в этом метод "конструктор"? Куда бы я положил логику, которая обычно выполнялась бы в методе конструктора класса при его создании?
BadHorsie
13

Я придумал это: РЕДАКТИРОВАТЬ: На самом деле, кто-то связал с тем же решением. 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
Это хорошая техника, но как вы позволите параметры в вашем конструкторе? Например, var getAwayVehicle = new Car(100);где 100скорость, и вы хотите предупредить скорость. Спасибо!
Джейсон
1
Разобрался, может иметь var Car = function(speed) { this.speed = speed; }и `return {constructor: Car, ...`
Джейсон
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
Hudon
7
Не работает Переменная name здесь действует как статическая переменная и используется всеми экземплярами Restaurant. Вот jsbin: jsbin.com/oqewUWa/2/edit?js,output
Шах
это хорошая попытка, но, как указал Шитал, переменная имени глючит.
oligofren
2
добавление моего 2c здесь, чтобы подтвердить, что это не работает, выглядит неплохо, но, как указано выше, «имя» будет служить статической переменной, то есть общим для всех экземпляров
Пол Кэрролл
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
Кого это волнует, серьезно?
сейчас
17
Те 9%, которые все еще используют IE6, не заботятся о скорости, оптимизации и всех современных функциях HTML5. Так что затворы ничего не будут стоить.
Габриэль Ламас
6
Сейчас оно составляет 0,5% (август 2012 г.). W3schools.com/browsers/browsers_explorer.asp
Лоренцо Полидори,
7
@LorenzoPolidori w3schools users! == корпоративные веб-пользователи;]
WynandB
Соглашение об именах (например, добавление подчеркивания) - это путь. Код легче поддерживать, а методы все еще определены в прототипе. Хотя в настоящее время ... я просто помечаю метод как частный в машинописи.
Дэвид Шеррет
5

Не будь таким многословным. Это 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();
            }
            */
        }
    });
prograhammer
источник
2
Следует отметить, что ни язык Javascript, ни какой-либо типичный хост браузера не определяют какие-либо объекты, которые полагаются на соглашения об именах для «скрытия» частного состояния, поэтому, хотя вы можете быть правы, что разработчики поймут эту концепцию, это все равно приводит к ОО подход к ОО программированию.
богатый ремер
Могу ли я попросить хороший отзыв в создании такого? В этом примере есть части, которые являются новыми для меня. И define, и constructorсама структура. Хотя я в основном согласен с ответом, я начал работать с JS с большим влиянием ООП и даже слишком рано переключился на TS, так как у меня был опыт работы с C #. Я думаю, что я должен отучиться от всего этого и понять парадигму прототипирования / процедур. (одобрено, кстати)
Холодный Цербер
1
@ColdCerberus этот фрагмент кода использует es5. Вы можете увидеть полную картину этого подхода здесь: gist.github.com/jonnyreeves/2474026 . Но имейте в виду, что вы захотите воспользоваться этим подходом и обновить его до классов es6 : googlechrome.github.io/samples/classes-es6 и es6 (синтаксис импорта / экспорта): hackernoon.com/…
prograhammer
5

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

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

  getPrivateMessage() {
    return #privateMethod();
  }
}
D-Marc
источник
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.

Fozi
источник
Это серьезная проблема, которая должна быть более известна. Есть ли «защита» от этой атаки?
Джеймс
@ Джеймс Ни о чем я не знаю, я думаю, что это природа зверя. Как я уже говорил, вы можете перенести функциональность на сервер, где он работает в защищенной среде. Однако в моем ответе я хотел бы остановиться на том, что решение Крокфорда не помогает, излишне усложняет код и скрывает необходимость что-то с этим делать.
Фози
Если пользователь вводит секретный пароль, он не может сделать это на стороне сервера. В какой-то момент пароль будет в «приватной» переменной. Так может ли злоумышленник это прочитать? Я доверяю своему коду, и в любом случае мои домашние стандарты не разрешают eval (). Злоумышленником может быть какой-то вредоносный сторонний плагин JavaScript или библиотека, которую я не проверил должным образом - так что да, мне нужно проверить это. Злоумышленник также может быть чем-то вроде рекламы на стороне, которая не должна взаимодействовать с моим кодом. Я защищаюсь от этого, оборачивая весь мой код в анонимный, (function () {allMyStuff}());чтобы не показывать ничего глобального.
Джеймс
@James Это уже OT, если вы хотите продолжить это, пожалуйста, откройте новый вопрос. Да, злоумышленник может прочитать пароль. Из твоей "приватной" переменной. Или из DOM. Или он может заменить AJAX API. Или он заменяет вашу страницу чем-то другим. Если он не может сделать ничего из вышеперечисленного, тогда нет необходимости в 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 () {
  };
}
domgblackwell
источник
Может кто-нибудь сказать мне, почему за это проголосовали? Выглядит хорошо для меня.
Томасруттер
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/

low_rents
источник
у этого подхода есть одно важное условие: если вы создали 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();

FIDDLE

Абденнур ТУМИ
источник
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

Частные функции не могут получить доступ к общедоступным переменным, используя шаблон модуля

Dooma
источник
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

РЕДАКТИРОВАТЬ

Ах, вот в чем суть: что означает «var this = this»; значит в JavaScript?

Так что Кроки действительно был не прав с его объяснением ... но с его кодом, так что он все еще отличный парень. :))

отметка
источник
0

В общем, я временно добавил приватный объект _. Вы должны открыть конфиденциальность в «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;
        };
    };
Андреас Дибалла
источник
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

snowkid
источник
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

Вы должны поместить замыкание вокруг вашей фактической функции-конструктора, где вы можете определить свои частные методы. Чтобы изменить данные экземпляров с помощью этих закрытых методов, вы должны передать им «this» вместе с ними, либо в качестве аргумента функции, либо путем вызова этой функции с помощью .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__);
Flex Elektro Deimling
источник
На самом деле, это не работает. У каждого new Restaurantбудет свой собственный restaurantконструктор, и «прототип» будет полностью нарушен.
Берги
@Bergi. На самом деле, вы правы. Это сработало бы, но также потребовало бы ресурсов (это так называется?). Я отредактировал свой ответ на этот счет.
Flex Elektro Deimling
Спасибо за обновления. Не знаю, как назвать предыдущую версию (но «ошибка» :-)
Bergi
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. Насколько я понял, ни один из методов, упомянутых выше, не сделал.

thegunmaster
источник
1
Любопытно: действительно ли вам нужно было выставить функцию, но сделать ее недоступной во время выполнения, а не скрывать ее от внешних абонентов, как это делают все / большинство других ответов? Если так, то почему? Что вы считаете преимуществом этого подхода? Мне кажется, что это просто ненужная нагрузка на производительность, неясный API и, возможно, это обязательно вызовет адский отладок, но я всегда открыт для новых перспектив ...
JHH
2
@JHH, если честно, я оглядываюсь назад на это. Затраты обычно вообще не стоят, хотя для меня это не имело большого значения, так как я не часто звонил в эти классы. Причина, по которой я сделал это, заключается в том, что он относительно чист в том, как вы пишете и вызываете функции. К тому времени я не понимал символы и тому подобное, но теперь, когда я это понимаю, я думаю, что в целом это хороший способ использования классов. Я рассматриваю удаление этого ответа все вместе. Я написал несколько глупых ответов, но вы живете и учитесь.
мастер пистолета
Спасибо за ответ! Я не был уверен, что я что-то не так понял. Но да, мы все живем и учимся!
JHH
0

Посмотрите этот ответ для чистого и простого «классного» решения с приватным и общедоступным интерфейсом и поддержкой композиции.

kofifus
источник