Доступ к частным переменным-членам из функций, определенных прототипом


есть ли способ сделать "частные" переменные (определенные в конструкторе) доступными для методов, определенных прототипом?

TestClass = function(){
    var privateField = "hello";
    this.nonProtoHello = function(){alert(privateField)};
};
TestClass.prototype.prototypeHello = function(){alert(privateField)};

это работает:

t.nonProtoHello()

но это не так:

t.prototypeHello()

Я привык определять свои методы внутри конструктора, но я отхожу от этого по нескольким причинам.

24 171

24 ответа:

нет, это невозможно сделать. Это, по сути, будет охватывать в обратном направлении.

методы, определенные внутри конструктора имеют доступ к приватным переменным, так как все функции имеют доступ к области, в которой они были определены.

методы, определенные на прототипе, не определены в области конструктора и не будут иметь доступа к локальным переменным конструктора.

вы все еще можете иметь частные переменные, но если вы хотите методы определенный на прототипе, чтобы иметь к ним доступ, вы должны определить геттеры и сеттеры на this объект, который является прототипом методов (наряду со всем остальным) будет есть доступ. Например:

function Person(name, secret) {
    // public
    this.name = name;

    // private
    var secret = secret;

    // public methods have access to private members
    this.setSecret = function(s) {
        secret = s;
    }

    this.getSecret = function() {
        return secret;
    }
}

// Must use getters/setters 
Person.prototype.spillSecret = function() { alert(this.getSecret()); };

обновление: с ES6, есть лучший способ:

короче говоря, вы можете использовать новый Symbol для создания частных полей.
Вот отличное описание:https://curiosity-driven.org/private-properties-in-javascript

пример:

var Person = (function() {
    // Only Person can access nameSymbol
    var nameSymbol = Symbol('name');

    function Person(name) {
        this[nameSymbol] = name;
    }

    Person.prototype.getName = function() {
        return this[nameSymbol];
    };

    return Person;
}());

для всех современных браузеров с ES5:

вы можете использовать только закрытие

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

или вы можете использовать только прототипы

в JavaScript прототипное наследование в первую очередь оптимизация. Это позволяет нескольким экземплярам совместно использовать методы прототипа, а не каждый экземпляр, имеющий свои собственные методы.
Недостатком является то, что this - это только вещь, которая отличается друг от друга время вызывается прототипная функция.
Поэтому любые частные поля должны быть доступны через this, что означает, что они будут публичными. Поэтому мы просто придерживаемся соглашений об именах для _private поля.

не утруждайте себя смешиванием затворов с прототипами

Я думаю, что вы не стоит смешайте переменные закрытия с методами прототипа. Вы должны использовать один или другой.

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

что я выбираю?

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

Если вам нужно прототипное наследование -- для наследования, производительности и т. д. -- тогда придерживайтесь соглашения об именах "_private" и не беспокойтесь закрытия.

Я не понимаю, почему разработчики JS так стараются сделать поля действительно частными.

CRAAAAZY но это полностью работает.

var SharedPrivateClass = (function(){ // use immediate function
    // our private data
    var private = "Default";

    // create the constructor
    function SharedPrivateClass () {}

    // add to the prototype
    SharedPrivateClass.prototype.getPrivate = function () {
        // It has access to private vars from the immediate function!
        return private;
    }

    SharedPrivateClass.prototype.setPrivate = function (value) {
        private = value;
    }

    return SharedPrivateClass;
})();

var a = new SharedPrivateClass();
console.log("a:", a.getPrivate()); // "a: Default"

var b = new SharedPrivateClass();
console.log("b:", b.getPrivate()); // "b: Default"

a.setPrivate("foo"); // a Sets private to 'foo'
console.log("a:", a.getPrivate()); // "a: foo"
console.log("b:", b.getPrivate()); // oh no, b.getPrivate() is 'foo'!

console.log(a.hasOwnProperty("getPrivate")); // false. belongs to the prototype
console.log(a.private); // undefined

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

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

var PrivateNamespaceClass = (function(){  // immediate function
    var instance = 0, // counts the number of instances
        defaultName = "Default Name",  
        p = []; // an array of private objects

    // careate the constructor
    function PrivateNamespaceClass () {
        // Increment the instance count and save it to the instance. 
        // This will become your key to your private space.
        this.i = instance++; 

        // Create a new object in the private space.
        p[this.i] = {};
        // Define properties or methods in the private space.
        p[this.i].name = defaultName;

        console.log("New instance " + this.i);        
    }
    PrivateNamespaceClass.prototype.getPrivateName = function () {
        // It has access to the private space and it's children!
        return p[this.i].name;
    }
    PrivateNamespaceClass.prototype.setPrivateName = function (value) {
        // Because you use the instance number assigned to the object (this.i)
        // as a key, the values set will not change in other instances.
        p[this.i].name = value;
        return "Set " + p[this.i].name;
    }

    return PrivateNamespaceClass;
})();

var a = new PrivateNamespaceClass();
console.log(a.getPrivateName()); // Default Name

var b = new PrivateNamespaceClass();
console.log(b.getPrivateName()); // Default Name

console.log(a.setPrivateName("A"));
console.log(b.setPrivateName("B"));
console.log(a.getPrivateName()); // A
console.log(b.getPrivateName()); // B

console.log(a.privateNamespace); // undefined

Я хотел бы получить некоторые отзывы от тех, кто видит ошибку с этим способом делать это.

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

еще пример:

Incrementer = function(init) {
  var counter = init || 0;  // "counter" is a private variable
  this._increment = function() { return counter++; }
  this._set = function(x) { counter = x; }
}
Incrementer.prototype.increment = function() { return this._increment(); }
Incrementer.prototype.set = function(x) { return this._set(x); }

использовать случае:

js>i = new Incrementer(100);
[object Object]
js>i.increment()
100
js>i.increment()
101
js>i.increment()
102
js>i.increment()
103
js>i.set(-44)
js>i.increment()
-44
js>i.increment()
-43
js>i.increment()
-42

Я предлагаю, вероятно, было бы неплохо описать "наличие прототипа назначения в конструкторе" как анти-шаблон Javascript. Думать об этом. Это слишком рискованно.

то, что вы на самом деле делаете там при создании второго объекта (т. е. b), переопределяет эту функцию прототипа для всех объектов, которые используют этот прототип. Это позволит эффективно сбросить значение для объекта a в вашем примере. Он будет работать, если вы хотите общую переменную, и если вам случится создать все экземпляры объекта спереди, но это слишком рискованно.

Я нашел ошибку в некоторых Javascript, над которыми я работал недавно, что было связано с этим точным анти-шаблоном. Он пытался установить обработчик перетаскивания на конкретный создаваемый объект, но вместо этого делал это для всех экземпляров. Не хороший.

решение Дуга Крокфорда является лучшим.

@Kai

- Это не сработает. Если вы это сделаете

var t2 = new TestClass();

затем t2.prototypeHello будут получать доступ к закрытой части "Т".

@AnglesCrimes

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

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

(function() {
    function Foo() { ... }
    Foo.prototype.bar = function() {
       privateFoo.call(this, blah);
    };
    function privateFoo(blah) { 
        // scoped to the instance by passing this to call 
    }

    window.Foo = Foo;
}());

Да, это возможно. Шаблон дизайна PPF просто решает эту проблему.

PPF обозначает частные функции прототипа. Основные PPF решает эти проблемы:

  1. функции прототипа получают доступ к данным частного экземпляра.
  2. функции прототипа можно сделать приватными.

для первой, так:

  1. Поместите все частные переменные экземпляра, которые вы хотите получить из функций прототипа, в отдельный контейнер данных, и
  2. передайте ссылку на контейнер данных всем функциям прототипа в качестве параметра.

Это очень просто. Например:

// Helper class to store private data.
function Data() {};

// Object constructor
function Point(x, y)
{
  // container for private vars: all private vars go here
  // we want x, y be changeable via methods only
  var data = new Data;
  data.x = x;
  data.y = y;

  ...
}

// Prototype functions now have access to private instance data
Point.prototype.getX = function(data)
{
  return data.x;
}

Point.prototype.getY = function(data)
{
  return data.y;
}

...

Читайте полную историю здесь:

шаблон дизайна PPF

вы действительно можете достичь этого с помощью Проверка Доступа:

(function(key, global) {
  // Creates a private data accessor function.
  function _(pData) {
    return function(aKey) {
      return aKey === key && pData;
    };
  }

  // Private data accessor verifier.  Verifies by making sure that the string
  // version of the function looks normal and that the toString function hasn't
  // been modified.  NOTE:  Verification can be duped if the rogue code replaces
  // Function.prototype.toString before this closure executes.
  function $(me) {
    if(me._ + '' == _asString && me._.toString === _toString) {
      return me._(key);
    }
  }
  var _asString = _({}) + '', _toString = _.toString;

  // Creates a Person class.
  var PersonPrototype = (global.Person = function(firstName, lastName) {
    this._ = _({
      firstName : firstName,
      lastName : lastName
    });
  }).prototype;
  PersonPrototype.getName = function() {
    var pData = $(this);
    return pData.firstName + ' ' + pData.lastName;
  };
  PersonPrototype.setFirstName = function(firstName) {
    var pData = $(this);
    pData.firstName = firstName;
    return this;
  };
  PersonPrototype.setLastName = function(lastName) {
    var pData = $(this);
    pData.lastName = lastName;
    return this;
  };
})({}, this);

var chris = new Person('Chris', 'West');
alert(chris.setFirstName('Christopher').setLastName('Webber').getName());

этот пример из моего поста о Прототипные Функции И Личные Данные и объясняется там более подробно.

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

подводя итог: The Person класс имеет одну слабую карту, где ключи являются экземплярами Person, а значения-простыми объектами, которые используются для частное хранение.

вот полностью функциональный пример: (играть в http://jsfiddle.net/ScottRippey/BLNVr/)

var Person = (function() {
    var _ = weakMap();
    // Now, _(this) returns an object, used for private storage.
    var Person = function(first, last) {
        // Assign private storage:
        _(this).firstName = first;
        _(this).lastName = last;
    }
    Person.prototype = {
        fullName: function() {
            // Retrieve private storage:
            return _(this).firstName + _(this).lastName;
        },
        firstName: function() {
            return _(this).firstName;
        },
        destroy: function() {
            // Free up the private storage:
            _(this, true);
        }
    };
    return Person;
})();

function weakMap() {
    var instances=[], values=[];
    return function(instance, destroy) {
        var index = instances.indexOf(instance);
        if (destroy) {
            // Delete the private state:
            instances.splice(index, 1);
            return values.splice(index, 1)[0];
        } else if (index === -1) {
            // Create the private state:
            instances.push(instance);
            values.push({});
            return values[values.length - 1];
        } else {
            // Return the private state:
            return values[index];
        }
    };
}

как я уже сказал, Это действительно единственный способ достичь всех 3-х частей.

есть два предостережения. Во-первых, это стоит производительности-каждый раз, когда вы получаете доступ к личным данным, это O(n) операции, где n - количество экземпляров. Так что вы не захотите этого делать, если у вас есть большое количество экземпляры. Во-вторых, когда вы закончите с экземпляром, вы должны позвонить destroy; в противном случае экземпляр и данные не будут собраны в мусор, и вы получите утечку памяти.

и вот почему мой оригинальный ответ, "ты не должен", это то, что я хочу придерживаться.

есть более простой способ, используя использование bind и call методы.

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

пример

function TestClass (value) {
    // The private value(s)
    var _private = {
        value: value
    };

    // `bind` creates a copy of `getValue` when the object is instantiated
    this.getValue = TestClass.prototype.getValue.bind(_private);

    // Use `call` in another function if the prototype method will possibly change
    this.getValueDynamic = function() {
        return TestClass.prototype.getValue.call(_private);
    };
};

TestClass.prototype.getValue = function() {
    return this.value;
};

этот метод не лишен недостатков. Поскольку контекст области фактически переопределяется, у вас нет доступа за пределами

попробуйте!

    function Potatoe(size) {
    var _image = new Image();
    _image.src = 'potatoe_'+size+'.png';
    function getImage() {
        if (getImage.caller == null || getImage.caller.owner != Potatoe.prototype)
            throw new Error('This is a private property.');
        return _image;
    }
    Object.defineProperty(this,'image',{
        configurable: false,
        enumerable: false,
        get : getImage          
    });
    Object.defineProperty(this,'size',{
        writable: false,
        configurable: false,
        enumerable: true,
        value : size            
    });
}
Potatoe.prototype.draw = function(ctx,x,y) {
    //ctx.drawImage(this.image,x,y);
    console.log(this.image);
}
Potatoe.prototype.draw.owner = Potatoe.prototype;

var pot = new Potatoe(32);
console.log('Potatoe size: '+pot.size);
try {
    console.log('Potatoe image: '+pot.image);
} catch(e) {
    console.log('Oops: '+e);
}
pot.draw();

вот что я придумал.

(function () {
    var staticVar = 0;
    var yrObj = function () {
        var private = {"a":1,"b":2};
        var MyObj = function () {
            private.a += staticVar;
            staticVar++;
        };
        MyObj.prototype = {
            "test" : function () {
                console.log(private.a);
            }
        };

        return new MyObj;
    };
    window.YrObj = yrObj;
}());

var obj1 = new YrObj;
var obj2 = new YrObj;
obj1.test(); // 1
obj2.test(); // 2

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

есть очень простой способ сделать это

function SharedPrivate(){
  var private = "secret";
  this.constructor.prototype.getP = function(){return private}
  this.constructor.prototype.setP = function(v){ private = v;}
}

var o1 = new SharedPrivate();
var o2 = new SharedPrivate();

console.log(o1.getP()); // secret
console.log(o2.getP()); // secret
o1.setP("Pentax Full Frame K1 is on sale..!");
console.log(o1.getP()); // Pentax Full Frame K1 is on sale..!
console.log(o2.getP()); // Pentax Full Frame K1 is on sale..!
o2.setP("And it's only for ,795._");
console.log(o1.getP()); // And it's only for ,795._

прототипы JavaScript являются золотыми.

Я опаздываю на вечеринку, но я думаю, что могу внести свой вклад. Вот, проверьте это:

// 1. Create closure
var SomeClass = function() {
  // 2. Create `key` inside a closure
  var key = {};
  // Function to create private storage
  var private = function() {
    var obj = {};
    // return Function to access private storage using `key`
    return function(testkey) {
      if(key === testkey) return obj;
      // If `key` is wrong, then storage cannot be accessed
      console.error('Cannot access private properties');
      return undefined;
    };
  };
  var SomeClass = function() {
    // 3. Create private storage
    this._ = private();
    // 4. Access private storage using the `key`
    this._(key).priv_prop = 200;
  };
  SomeClass.prototype.test = function() {
    console.log(this._(key).priv_prop); // Using property from prototype
  };
  return SomeClass;
}();

// Can access private property from within prototype
var instance = new SomeClass();
instance.test(); // `200` logged

// Cannot access private property from outside of the closure
var wrong_key = {};
instance._(wrong_key); // undefined; error logged

Я называю этот метод accessor pattern. Основная идея заключается в том, что у нас есть закрытие, a ключ внутри закрытия, и мы создаем частный объект (в конструкторе), которые могут быть доступны только если у вас есть ключ.

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

вот что я придумал, пытаясь найти наиболее простое решение этой проблемы, возможно, это может быть полезным кому-то. Я новичок в javascript, так что вполне могут быть некоторые проблемы с кодом.

// pseudo-class definition scope
(function () {

    // this is used to identify 'friend' functions defined within this scope,
    // while not being able to forge valid parameter for GetContext() 
    // to gain 'private' access from outside
    var _scope = new (function () { })();
    // -----------------------------------------------------------------

    // pseudo-class definition
    this.Something = function (x) {

        // 'private' members are wrapped into context object,
        // it can be also created with a function
        var _ctx = Object.seal({

            // actual private members
            Name: null,
            Number: null,

            Somefunc: function () {
                console.log('Something(' + this.Name + ').Somefunc(): number = ' + this.Number);
            }
        });
        // -----------------------------------------------------------------

        // function below needs to be defined in every class
        // to allow limited access from prototype
        this.GetContext = function (scope) {

            if (scope !== _scope) throw 'access';
            return _ctx;
        }
        // -----------------------------------------------------------------

        {
            // initialization code, if any
            _ctx.Name = (x !== 'undefined') ? x : 'default';
            _ctx.Number = 0;

            Object.freeze(this);
        }
    }
    // -----------------------------------------------------------------

    // prototype is defined only once
    this.Something.prototype = Object.freeze({

        // public accessors for 'private' field
        get Number() { return this.GetContext(_scope).Number; },
        set Number(v) { this.GetContext(_scope).Number = v; },

        // public function making use of some private fields
        Test: function () {

            var _ctx = this.GetContext(_scope);
            // access 'private' field
            console.log('Something(' + _ctx.Name + ').Test(): ' + _ctx.Number);
            // call 'private' func
            _ctx.Somefunc();
        }
    });
    // -----------------------------------------------------------------

    // wrap is used to hide _scope value and group definitions
}).call(this);

function _A(cond) { if (cond !== true) throw new Error('assert failed'); }
// -----------------------------------------------------------------

function test_smth() {

    console.clear();

    var smth1 = new Something('first'),
      smth2 = new Something('second');

    //_A(false);
    _A(smth1.Test === smth2.Test);

    smth1.Number = 3;
    smth2.Number = 5;
    console.log('smth1.Number: ' + smth1.Number + ', smth2.Number: ' + smth2.Number);

    smth1.Number = 2;
    smth2.Number = 6;

    smth1.Test();
    smth2.Test();

    try {
        var ctx = smth1.GetContext();
    } catch (err) {
        console.log('error: ' + err);
    }
}

test_smth();

сегодня я столкнулся с тем же вопросом, и после разработки первоклассного ответа Скотта Риппи я придумал очень простое решение (IMHO), которое совместимо с ES5 и эффективно, а также безопасно для имен (использование _private кажется небезопасным).

/*jslint white: true, plusplus: true */

 /*global console */

var a, TestClass = (function(){
    "use strict";
    function PrefixedCounter (prefix) {
        var counter = 0;
        this.count = function () {
            return prefix + (++counter);
        };
    }
    var TestClass = (function(){
        var cls, pc = new PrefixedCounter("_TestClass_priv_")
        , privateField = pc.count()
        ;
        cls = function(){
            this[privateField] = "hello";
            this.nonProtoHello = function(){
                console.log(this[privateField]);
            };
        };
        cls.prototype.prototypeHello = function(){
            console.log(this[privateField]);
        };
        return cls;
    }());
    return TestClass;
}());

a = new TestClass();
a.nonProtoHello();
a.prototypeHello();

проверено с помощью ringojs и nodejs. Мне не терпится узнать ваше мнение.

var getParams = function(_func) {
  res = _func.toString().split('function (')[1].split(')')[0].split(',')
  return res
}

function TestClass(){

  var private = {hidden: 'secret'}
  //clever magic accessor thing goes here
  if ( !(this instanceof arguments.callee) ) {
    for (var key in arguments) {
      if (typeof arguments[key] == 'function') {
        var keys = getParams(arguments[key])
        var params = []
        for (var i = 0; i <= keys.length; i++) {
          if (private[keys[i]] != undefined) {
            params.push(private[keys[i]])
          }
        }
        arguments[key].apply(null,params)
      }
    }
  }
}


TestClass.prototype.test = function(){
  var _hidden; //variable I want to get
  TestClass(function(hidden) {_hidden = hidden}) //invoke magic to get
};

new TestClass().test()

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

у меня есть одно решение, но я не уверен, что она без изъянов.

чтобы он работал, вы должны использовать следующую структуру:

  1. используйте 1 частный объект, содержащий все частные переменные.
  2. использовать 1 экземпляр функции.
  3. применить замыкание к конструктору и всем функциям прототипа.
  4. любой созданный экземпляр выполняется вне определенного закрытия.

вот код:

var TestClass = 
(function () {
    // difficult to be guessed.
    var hash = Math.round(Math.random() * Math.pow(10, 13) + + new Date());
    var TestClass = function () {
        var privateFields = {
            field1: 1,
            field2: 2
        };
        this.getPrivateFields = function (hashed) {
            if(hashed !== hash) {
                throw "Cannot access private fields outside of object.";
                // or return null;
            }
            return privateFields;
        };
    };

    TestClass.prototype.prototypeHello = function () {
        var privateFields = this.getPrivateFields(hash);
        privateFields.field1 = Math.round(Math.random() * 100);
        privateFields.field2 = Math.round(Math.random() * 100);
    };

    TestClass.prototype.logField1 = function () {
        var privateFields = this.getPrivateFields(hash);
        console.log(privateFields.field1);
    };

    TestClass.prototype.logField2 = function () {
        var privateFields = this.getPrivateFields(hash);
        console.log(privateFields.field2);
    };

    return TestClass;
})();

как это работает в том, что он предоставляет функцию экземпляра "это.getPrivateFields "для доступа к объекту частных переменных "privateFields", но эта функция будет возвращать только объект" privateFields "внутри основного закрытия, определенного (также функции прототипа, использующие" this.getPrivateFields " должны быть определены внутри этого закрытия).

хэш, созданный во время выполнения и трудно угадать, используется в качестве параметров, чтобы убедиться, что даже если "getPrivateFields" вызывается вне области действия закрытие не вернет объект "privateFields".

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

вот некоторые тестовый код:

var t1 = new TestClass();
console.log('Initial t1 field1 is: ');
t1.logField1();
console.log('Initial t1 field2 is: ');
t1.logField2();
t1.prototypeHello();
console.log('t1 field1 is now: ');
t1.logField1();
console.log('t1 field2 is now: ');
t1.logField2();
var t2 = new TestClass();
console.log('Initial t2 field1 is: ');
t2.logField1();
console.log('Initial t2 field2 is: ');
t2.logField2();
t2.prototypeHello();
console.log('t2 field1 is now: ');
t2.logField1();
console.log('t2 field2 is now: ');
t2.logField2();

console.log('t1 field1 stays: ');
t1.logField1();
console.log('t1 field2 stays: ');
t1.logField2();

t1.getPrivateFields(11233);

EDIT: используя этот метод, также можно "определить" частные функции.

TestClass.prototype.privateFunction = function (hashed) {
    if(hashed !== hash) {
        throw "Cannot access private function.";
    }
};

TestClass.prototype.prototypeHello = function () {
    this.privateFunction(hash);
};

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

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

   const loader = (function() {
        function ModuleLoader() {}

    //Static, accessible only if truly needed through obj.constructor.modules
    //Can also be made completely private by removing the ModuleLoader prefix.
    ModuleLoader.modulesLoaded = 0;
    ModuleLoader.modules = {}

    ModuleLoader.prototype.define = function(moduleName, dModule) {
        if (moduleName in ModuleLoader.modules) throw new Error('Error, duplicate module');

        const module = ModuleLoader.modules[moduleName] = {}

        module.context = {
            __moduleName: moduleName,
            exports: {}
        }

        //Weak map with instance as the key, when the created instance is garbage collected or goes out of scope this will be cleaned up.
        module._private = {
            private_sections: new WeakMap(),
            instances: []
        };

        function private(action, instance) {
            switch (action) {
                case "create":
                    if (module._private.private_sections.has(instance)) throw new Error('Cannot create private store twice on the same instance! check calls to create.')
                    module._private.instances.push(instance);
                    module._private.private_sections.set(instance, {});
                    break;
                case "delete":
                    const index = module._private.instances.indexOf(instance);
                    if (index == -1) throw new Error('Invalid state');
                    module._private.instances.slice(index, 1);
                    return module._private.private_sections.delete(instance);
                    break;
                case "get":
                    return module._private.private_sections.get(instance);
                    break;
                default:
                    throw new Error('Invalid action');
                    break;
            }
        }

        dModule.call(module.context, private);
        ModuleLoader.modulesLoaded++;
    }

    ModuleLoader.prototype.remove = function(moduleName) {
        if (!moduleName in (ModuleLoader.modules)) return;

        /*
            Clean up as best we can.
        */
        const module = ModuleLoader.modules[moduleName];
        module.context.__moduleName = null;
        module.context.exports = null;
        module.cotext = null;
        module._private.instances.forEach(function(instance) { module._private.private_sections.delete(instance) });
        for (let i = 0; i < module._private.instances.length; i++) {
            module._private.instances[i] = undefined;
        }
        module._private.instances = undefined;
        module._private = null;
        delete ModuleLoader.modules[moduleName];
        ModuleLoader.modulesLoaded -= 1;
    }


    ModuleLoader.prototype.require = function(moduleName) {
        if (!(moduleName in ModuleLoader.modules)) throw new Error('Module does not exist');

        return ModuleLoader.modules[moduleName].context.exports;
    }



     return new ModuleLoader();
    })();

    loader.define('MyModule', function(private_store) {
        function MyClass() {
            //Creates the private storage facility. Called once in constructor.
            private_store("create", this);


            //Retrieve the private storage object from the storage facility.
            private_store("get", this).no = 1;
        }

        MyClass.prototype.incrementPrivateVar = function() {
            private_store("get", this).no += 1;
        }

        MyClass.prototype.getPrivateVar = function() {
            return private_store("get", this).no;
        }

        this.exports = MyClass;
    })

    //Get whatever is exported from MyModule
    const MyClass = loader.require('MyModule');

    //Create a new instance of `MyClass`
    const myClass = new MyClass();

    //Create another instance of `MyClass`
    const myClass2 = new MyClass();

    //print out current private vars
    console.log('pVar = ' + myClass.getPrivateVar())
    console.log('pVar2 = ' + myClass2.getPrivateVar())

    //Increment it
    myClass.incrementPrivateVar()

    //Print out to see if one affected the other or shared
    console.log('pVar after increment = ' + myClass.getPrivateVar())
    console.log('pVar after increment on other class = ' + myClass2.getPrivateVar())

    //Clean up.
    loader.remove('MyModule')

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

Я думаю, что это должно быть что-то вроде этого:

// your class
function TestClass() {

    // your field
    this.privateField = "hello";

    // create a local reference to this, 
    // because 'this' in the wrapper function will not be a TestClass
    var self = this;

    // implement the wrapper function and pass the 'self' to the static version
    this.showHello = () => TestClass.showHello(self);
}

// implement the 'big/complex' functionallity static. The wrapper will pass the instance
TestClass.showHello = function(self) {
    alert(self.privateField); 
}

использование:

var t = new TestClass();
t.showHello();

вы даже можете вызвать статический метод:

TestClass.showHello(t);

вам нужно изменить 3 вещи в ваш код:

  1. заменить var privateField = "hello" С this.privateField = "hello".
  2. в прототипе заменить privateField С this.privateField.
  3. в не-прототипе также замените privateField С this.privateField.

окончательный код будет следующим:

TestClass = function(){
    this.privateField = "hello";
    this.nonProtoHello = function(){alert(this.privateField)};
}

TestClass.prototype.prototypeHello = function(){alert(this.privateField)};

var t = new TestClass();

t.prototypeHello()

не можете ли вы поместить переменные в более высокую область?

(function () {
    var privateVariable = true;

    var MyClass = function () {
        if (privateVariable) console.log('readable from private scope!');
    };

    MyClass.prototype.publicMethod = function () {
        if (privateVariable) console.log('readable from public scope!');
    };
}))();

вы также можете попробовать добавить метод не непосредственно на прототип, а на функцию конструктора, как это:

var MyArray = function() {
    var array = [];

    this.add = MyArray.add.bind(null, array);
    this.getAll = MyArray.getAll.bind(null, array);
}

MyArray.add = function(array, item) {
    array.push(item);
}
MyArray.getAll = function(array) {
    return array;
}

var myArray1 = new MyArray();
myArray1.add("some item 1");
console.log(myArray1.getAll()); // ['some item 1']
var myArray2 = new MyArray();
myArray2.add("some item 2");
console.log(myArray2.getAll()); // ['some item 2']
console.log(myArray1.getAll()); // ['some item 2'] - FINE!

вы можете использовать назначение прототипа в определении конструктора.

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

function A()
{
  var sharedVar = 0;
  this.local = "";

  A.prototype.increment = function(lval)
  {    
    if (lval) this.local = lval;    
    alert((++sharedVar) + " while this.p is still " + this.local);
  }
}

var a = new A();
var b = new A();    
a.increment("I belong to a");
b.increment("I belong to b");
a.increment();
b.increment();

Я надеюсь, что это может быть полезно.