Приватные свойства в js

292
16 января 2018, 14:24

Я не знаю как правильно сказать, но как сделать свойство, которое было бы не видно внешне, но было бы видно методам установленных через прототип? Вот типа -

var Class = function(){
    var _array = []; // приватное или протектед свойство
};
Class.prototype.method = function(){
    // как обратиться здесь к _array?
};

Возможно такое сделать из коробки?

Answer 1

Средства языка такого, насколько я знаю, не предоставляют. Поэтому javascript-еры договорились, что все свойства которые начинаются со знака нижнего подчеркивания '_' являются приватными. В вашем случае:

var Class = function(){
    this._array = []; // приватное или протектед свойство
};
Class.prototype.method = function(){
    // как обратиться здесь к _array?
    this._array
};
var instance = new Class();
instance._array.push('string') // но мы то знаем, что так делать нельзя

Update

Появилась идея. Шифровать имя свойства и делать его enumerable. Костыль:

var prive = function (that, propName, newValue) {
  var decodeStr = function (str) {
    return Array.prototype.map.call(
      str,
      function (char, i) {
        return String.fromCharCode(str.charCodeAt(i)-5)
      }).join('');
  },
  decodedPropName = decodeStr(propName);
  if (arguments.length >= 3) {
    // set private property
    Object.defineProperty(that, decodedPropName, {
      enumerable: false,
      writable: true,
      value: newValue,
    });
  } else {
    //get private property
    return that[decodedPropName];
  }
}

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

Answer 2

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

(function(){
   var private_method = function(){}; // приватный метод не будет виден снаружи
   var private_property = 'ololo'; // приватное свойство
   var Class = function(){};
   Class.prototype.public_method = function(){ // паблик метод
      var x = private_method(private_property); 
      // приватные свойства и методы можно использовать здесь
   };
   window.Class = Class; // выносим класс в общую область видимости
})();

jsfiddle

Answer 3

Я этот вопрос решал вот так:

(function (exports) {
    /*
     * sample prototype,
     *
     * inherit @boolean if true - return privates object,
     *
     */
    function A(inherit) {
        var privates = { //setup private vars, they could be also changed, added in method or children
            a: 1,
            b: 2,
            c: 3
        };
        //setup public methods which uses privates
        this.aPlus = bindPlus("aPlus", this, privates); //pass method name as string!
        this.aGet = bindPlus("aGet", this, privates);
        if (inherit) {
            return privates;
        }
    }
    A.prototype.aPlus = function () {
        var args = getArgs(arguments),
            //self is "this" here (applied/called too), object reference is still this        
            privates = args.shift(),
            self = args.shift(),
            //function real arguments            
            n = args.shift();
        return privates.a += n;
    };
    A.prototype.aGet = function (n) {
        var args = getArgs(arguments),                
            privates = args.shift(),
            self = args.shift();        
        console.error(this, self,privates);
        return privates.a;
    };
    exports.A = A;
    exports.getArgs = getArgs; //should be hidden somehow, but this is out of the story
    exports.bindPlus = bindPlus;
    //utilites
    function getArgs(arg) {
        return Array.prototype.slice.call(arg);
    }
    //вот здесь в общем-то ядро идеи
    function bindPlus(funct, self, privates) {
        /**
        * if uncomment here and comment same line later,
        * it will be run faster, but it would be impossible 
        * to change prototype after constructor run (tests after ----- could be incorrect)
        */
        //var func=Object.getPrototypeOf(self)[funct].bind(self, privates);
        return function () {                        
            var func=Object.getPrototypeOf(self)[funct].bind(self, privates);
            var args=getArgs(arguments); //this could be changed to speedup, but need to change method itself
                args.unshift(this); //called/applied this
            return func.apply(null, args);
        };
    }
})(this);
//inherited 
function B(inherit) {
    var privates = Object.getPrototypeOf(this).constructor.call(this, true);
    privates.d = 4;
    this.dGet = bindPlus("dGet", this, privates);
    if (inherit) {
        return privates;
    }
}
B.prototype = Object.create(A.prototype);
B.constructor = B;
B.prototype.aGet = function () {
    var args = getArgs(arguments);
        var privates = args.shift(),
        self = args.shift();
    console.warn("B.aGet",self, privates);
    return privates.a;
};
B.prototype.dGet = function () {
    var args = getArgs(arguments),
        privates = args.shift();
        self = args.shift(),
    console.warn("B.dGet", self, privates);
    return privates.d;
};

но это немного тормозно и неудобно, лучшего способа не нашел, чтобы работало.

http://jsfiddle.net/oceog/TJH9Q/

Answer 4

Ну раз тут уже толпа, внесу лепту, так сказать. Вкратце - из глобальной области видимости исключается один объект _this, который и содержит все приватные поля-методы. Плюсы - прозрачность и вроде даже удобство.

var MyClass = function() {
    var _this = {}; // private container
    _this.private1 = 0; // private var 1
    _this.private2 = 1; // private var 2
    _this.incPrivate = function() { // private function
      _this.private1++;
      _this.private2++;
    };
    this.public1 = 2; // public var 1
    this.public2 = 3; // public var 2 
    this.logPrivate = function() { // public function
      console.log(_this);
      return this;
    };
    this.incPublic = function() { // public function
      this.public1++;
      this.public2++;
      return this;
    };
    this.proxyIncPrivate = function() { // public function / proxy
      _this.incPrivate();
      return this;
    };
    this.logPublic = function() { // public function
      console.log(this);
      return this;
    };
}
var MyInstance = new MyClass();

jsfiddle

UPDADE

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

(function() {
    var _class = {
        // доступно в объекте и прототипе,
        // один экземпляр (изменяется отовсюду)
        // недоступно из глобальной области видимости
        privateStatic1: 0,
        privateStatic2: 1,
        privateStaticMethod: function() {
            console.log('called private static method');
        }
    }
    var MyClass = function() {
        var _this = {
            // доступно только из методов объекта,
            // изменяется только объект
            // недоступно из глобала и класса
            private1: 2,
            private2: 3,
            privateMethod: function() {
              console.log('called private method');
            }
        };
        // доступно отовсюду после создания объекта,
        // изменяется только объект
        // недоступно из глобала и класса
        this.public1 = 4;
        this.public2 = 5;
        this.publicMethod = function() {
          console.log('called public method');
        };
    };
    // доступно отовсюду,
    // один экземпляр
    // дублирование для возможности вызова через MyClass без создания объекта
    MyClass.prototype.publicStatic1 = 6;
        MyClass.publicStatic1 = MyClass.prototype.publicStatic1;
    MyClass.prototype.publicStatic2 = 7;
        MyClass.prototype.publicStatic2 = MyClass.prototype.publicStatic2;
    MyClass.prototype.publicStaticMethod = function() {
      console.log('called public static method');
    }
    MyClass.publicStaticMethod = MyClass.prototype.publicStaticMethod;
    window.MyClass = MyClass;
})();

http://jsfiddle.net/ux95ub87/1/ + консольный вывод результатов всех вызовов.

READ ALSO
как показать текст, если на странице сайта не показалось изображение?

как показать текст, если на странице сайта не показалось изображение?

Как показать текст на сайте если по каким то причинам не показалось изображение определенного размера, либо баннер? Знаю что есть некоторые...

230
Как через php достать значение json

Как через php достать значение json

Вот json из которого мне надо достать object-attachments-doc-url

290
как получить аргументы URL?

как получить аргументы URL?

Такая беда, ссылка выглядит вот так <a href=?page=2

348