문제

프로토 타입 정의 메소드에서 사용할 수있는 "개인"변수 (생성자에 정의 된 변수)를 만들 수있는 방법이 있습니까?

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

이것은 작동합니다 :

t.nonProtoHello()

그러나 이것은 그렇지 않습니다.

t.prototypeHello()

나는 생성자 내부의 내 메소드를 정의하는 데 익숙하지만 몇 가지 이유로 그 방법에서 멀어지고 있습니다.

도움이 되었습니까?

해결책

아니요, 할 수있는 방법이 없습니다. 그것은 본질적으로 반대로 범위를 지정할 것입니다.

생성자 내부에 정의 된 메소드는 모든 함수가 정의 된 범위에 액세스하기 때문에 개인 변수에 액세스 할 수 있습니다.

프로토 타입에 정의 된 메소드는 생성자의 범위 내에서 정의되지 않으며 생성자의 로컬 변수에 액세스 할 수 없습니다.

여전히 개인 변수를 가질 수 있지만 프로토 타입에 정의 된 메소드가 액세스 할 수 있도록하려면 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

// getPrivate() is only created once and instanceof still works
console.log(a.getPrivate === b.getPrivate);
console.log(a instanceof SharedPrivateClass);
console.log(b instanceof SharedPrivateClass);

인스턴스간에 공유되는 이벤트 이름과 같은 일정한 값을 갖기를 원한다면 이것이 적절한 경우가 많이 있습니다. 그러나 본질적으로 그들은 개인 정적 변수처럼 행동합니다.

프로토 타입에 정의 된 메소드 내에서 개인 네임 스페이스의 변수에 대한 액세스가 절대적으로 액세스 해야하는 경우이 패턴을 시도 할 수 있습니다.

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

    // create 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

// private objects are not accessible outside the PrivateNamespaceClass function
console.log(a.p);

// the prototype functions are not re-created for each instance
// and instanceof still works
console.log(a.getPrivateName === b.getPrivateName);
console.log(a instanceof PrivateNamespaceClass);
console.log(b instanceof PrivateNamespaceClass);

나는 이런 방식으로 오류를 보는 사람의 피드백을 좋아합니다.

보다 이것에 대한 Doug Crockford의 페이지. 개인 변수의 범위에 액세스 할 수있는 무언가로 간접적으로 수행해야합니다.

또 다른 예:

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 에서이 정확한 반포에 기인 한 버그를 발견했습니다. 생성중인 특정 객체에 드래그 앤 드롭 핸들러를 설정하려고했지만 대신 모든 인스턴스를 위해 수행하고있었습니다. 안좋다.

Doug Crockford의 솔루션이 최고입니다.

@kai

작동하지 않습니다. 당신이한다면

var t2 = new TestClass();

그 다음에 t2.prototypeHello T의 개인 섹션에 액세스 할 것입니다.

@anglescrimes

샘플 코드는 잘 작동하지만 실제로 모든 인스턴스에서 공유하는 "정적"개인 구성원을 만듭니다. Morgancodes가 찾은 해결책이 아닐 수도 있습니다.

지금까지 개인 해시 및 추가 청소 기능을 도입하지 않고는 쉽고 깨끗한 방법을 찾지 못했습니다. 개인 멤버 기능은 어느 정도 시뮬레이션 할 수 있습니다.

(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 클래스에는 단일 약한 맵이 있는데, 여기서 키는 사람의 인스턴스이고 값은 개인 저장에 사용되는 일반 객체입니다.

다음은 완전히 기능적인 예입니다. 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];
        }
    };
}

내가 말했듯이, 이것은 실제로 세 부분을 모두 달성하는 유일한 방법입니다.

그러나 두 가지 경고가 있습니다. 첫째,이 비용은 성능을 요약합니다. 개인 데이터에 액세스 할 때마다 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;
};

이 메소드는 단점이 없습니다. 스코프 컨텍스트가 효과적으로 재정의되므로 외부에서 액세스 할 수 없습니다. _private 물체. 그러나 인스턴스 객체의 범위에 여전히 액세스하는 것은 불가능하지 않습니다. 객체의 컨텍스트를 전달할 수 있습니다 (this) 두 번째 논쟁으로 bind 또는 call 프로토 타입 기능에서 공개 값에 여전히 액세스 할 수 있습니다.

공개 가치에 액세스

function TestClass (value) {
    var _private = {
        value: value
    };

    this.message = "Hello, ";

    this.getMessage = TestClass.prototype.getMessage.bind(_private, this);

}

TestClass.prototype.getMessage = function(_public) {

    // Can still access passed in arguments
    // e.g. – test.getValues('foo'), 'foo' is the 2nd argument to the method
    console.log([].slice.call(arguments, 1));
    return _public.message + this.value;
};

var test = new TestClass("World");
test.getMessage(1, 2, 3); // [1, 2, 3]         (console.log)
                          // => "Hello, World" (return value)

test.message = "Greetings, ";
test.getMessage(); // []                    (console.log)
                   // => "Greetings, World" (return 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 $1,795._");
console.log(o1.getP()); // And it's only for $1,795._

자바 스크립트 프로토 타입은 황금입니다.

나는 파티에 늦었지만 기여할 수 있다고 생각합니다. 여기에서 확인하십시오.

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

이 방법을 호출합니다 액세서 패턴. 필수 아이디어는 우리에게 a 폐쇄, ㅏ 열쇠 폐쇄 내부에서 우리는 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();

나는 오늘 똑같은 질문에 직면했고 Scott Rippey 일류 응답에 대해 자세히 설명한 후, ES5와 효율적이고 효율적인 매우 간단한 솔루션 (IMHO)을 생각해 냈습니다. .

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

이 방법은 인스턴스 기능 "this.getPrivateFields"를 제공한다는 것입니다. "privatefields"개인 변수 객체에 액세스 할 수 있지만이 기능은 정의 된 기본 클로저 내부의 "개인 필드"객체 만 리턴합니다 (또한 "this.getPrivateFields를 사용한 프로토 타입 함수. "이 폐쇄 내부에서 정의해야합니다).

런타임 중에 생성되고 추측하기 어려운 해시는 "getPrivateFields"가 클로저 범위를 벗어나더라도 "개인 필드"객체를 반환하지 않도록하기 위해 매개 변수로 사용됩니다.

단점은 폐쇄 외부에서 더 많은 프로토 타입 기능으로 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);

편집 :이 방법을 사용하면 개인 기능을 "정의"할 수도 있습니다.

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

나는 이것이 요청 된 이래로 1 년이 넘었는지 알고 있지만, 나는 프로그래머의 삶에서 N-Ther 시간 동안 이것에 대해 내 생각을했고, 내가 아직 완전히 좋아하는지 모르는 가능한 해결책을 찾았습니다. . 이 방법론을 전에 보지 못했기 때문에 "개인/공공 달러 패턴"또는 _ $ / $ 패턴.

var ownFunctionResult = this.$("functionName"[, arg1[, arg2 ...]]);
var ownFieldValue = this._$("fieldName"[, newValue]);

var objectFunctionResult = objectX.$("functionName"[, arg1[, arg2 ...]]);

//Throws an exception. objectX._$ is not defined
var objectFieldValue = objectX._$("fieldName"[, newValue]);

이 개념은 a를 사용합니다 ClassDefinition 반환하는 함수 a 건설자 반환하는 함수 상호 작용 물체. 인터페이스의 유일한 방법은입니다 $ a name 생성자 객체에서 해당 함수를 호출하는 인수, 후에 통과 된 추가 인수 name 호출에 전달됩니다.

전 세계적으로 정의 된 도우미 기능 ClassValues 모든 필드를 저장합니다 an 필요에 따라 물체. 그것은 정의합니다 _$ 액세스 할 수있는 기능 name. 이것은 짧은 get/set 패턴을 따릅니다. value 전달되면 새로운 변수 값으로 사용됩니다.

var ClassValues = function (values) {
  return {
    _$: function _$(name, value) {
      if (arguments.length > 1) {
        values[name] = value;
      }

      return values[name];
    }
  };
};

전 세계적으로 정의 된 기능 Interface 물체와 a Values 반환 할 대상 _interface 하나의 단일 함수로 $ 검사합니다 obj 매개 변수의 이름을 따서 명명 된 함수를 찾으려면 name 그리고 그것을 호출합니다 values 로서 범위 물체. 추가 논쟁이 전달되었습니다 $ 함수 호출에 전달됩니다.

var Interface = function (obj, values, className) {
  var _interface = {
    $: function $(name) {
      if (typeof(obj[name]) === "function") {
        return obj[name].apply(values, Array.prototype.splice.call(arguments, 1));
      }

      throw className + "." + name + " is not a function.";
    }
  };

  //Give values access to the interface.
  values.$ = _interface.$;

  return _interface;
};

아래 샘플에서 ClassX 결과에 할당됩니다 ClassDefinition, 그것이 Constructor 기능. Constructor 많은 논쟁을받을 수 있습니다. Interface 생성자를 호출 한 후 외부 코드가 얻는 것입니다.

var ClassX = (function ClassDefinition () {
  var Constructor = function Constructor (valA) {
    return Interface(this, ClassValues({ valA: valA }), "ClassX");
  };

  Constructor.prototype.getValA = function getValA() {
    //private value access pattern to get current value.
    return this._$("valA");
  };

  Constructor.prototype.setValA = function setValA(valA) {
    //private value access pattern to set new value.
    this._$("valA", valA);
  };

  Constructor.prototype.isValAValid = function isValAValid(validMessage, invalidMessage) {
    //interface access pattern to call object function.
    var valA = this.$("getValA");

    //timesAccessed was not defined in constructor but can be added later...
    var timesAccessed = this._$("timesAccessed");

    if (timesAccessed) {
      timesAccessed = timesAccessed + 1;
    } else {
      timesAccessed = 1;
    }

    this._$("timesAccessed", timesAccessed);

    if (valA) {
      return "valA is " + validMessage + ".";
    }

    return "valA is " + invalidMessage + ".";
  };

  return Constructor;
}());

비 프로토 타입 기능을 갖는 것은 아무런 의미가 없습니다 Constructor, 생성자 기능 본문에서 정의 할 수 있지만. 모든 기능은 다음과 함께 호출됩니다 공개 달러 패턴 this.$("functionName"[, param1[, param2 ...]]). 개인 값은 개인 달러 패턴 this._$("valueName"[, replacingValue]);. 처럼 Interface 정의가 없습니다 _$, 외부 객체는 값에 액세스 할 수 없습니다. 각 프로토 타입 기능 본문이기 때문에 this 로 설정됩니다 values 기능의 물체 $, 생성자 형제 자매 기능을 직접 호출하면 예외가 나타납니다. 그만큼 _ $ / $ 패턴 프로토 타입 기능 본문에서도 따라야합니다. 샘플 사용량 아래.

var classX1 = new ClassX();
console.log("classX1." + classX1.$("isValAValid", "valid", "invalid"));
console.log("classX1.valA: " + classX1.$("getValA"));
classX1.$("setValA", "v1");
console.log("classX1." + classX1.$("isValAValid", "valid", "invalid"));
var classX2 = new ClassX("v2");
console.log("classX1.valA: " + classX1.$("getValA"));
console.log("classX2.valA: " + classX2.$("getValA"));
//This will throw an exception
//classX1._$("valA");

그리고 콘솔 출력.

classX1.valA is invalid.
classX1.valA: undefined
classX1.valA is valid.
classX1.valA: v1
classX2.valA: v2

그만큼 _ $ / $ 패턴 완전 보호 된 클래스에서 값의 전체 개인 정보를 허용합니다. 나는 이것을 사용할 것인지, 결함이 있는지 모르겠지만, 좋은 퍼즐이었다!

ES6 약점

간단한 패턴을 사용하여 ES6 약점 얻을 수 있습니다 프로토 타입 기능에서 도달 할 수있는 개인 회원 변수.

참고 : 약점의 사용은 보장됩니다 메모리 누출에 대한 안전, 쓰레기 수집기가 사용하지 않은 인스턴스를 식별하고 버리도록함으로써.

// Create a private scope using an Immediately 
// Invoked Function Expression...
let Person = (function() {

    // Create the WeakMap that will hold each  
    // Instance collection's of private data
    let privateData = new WeakMap();
    
    // Declare the Constructor :
    function Person(name) {
        // Insert the private data in the WeakMap,
        // using 'this' as a unique acces Key
        privateData.set(this, { name: name });
    }
    
    // Declare a prototype method 
    Person.prototype.getName = function() {
        // Because 'privateData' is in the same 
        // scope, it's contents can be retrieved...
        // by using  again 'this' , as  the acces key 
        return privateData.get(this).name;
    };

    // return the Constructor
    return Person;
}());

이 패턴에 대한 자세한 설명을 찾을 수 있습니다. 여기

변수를 더 높은 범위에 넣을 수 없습니까?

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

코드에서 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 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();

이것이 유용 할 수 있기를 바랍니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top