프로토 타입 정의 함수에서 개인 멤버 변수에 액세스


187

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

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

이것은 작동합니다 :

t.nonProtoHello()

그러나 이것은하지 않습니다 :

t.prototypeHello()

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



14
@ecampver,이 질문은 2 년 전에 요청한 것을 제외하고 ....
Pacerier

답변:


191

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

생성자 내에 정의 된 메서드는 모든 함수가 정의 된 범위에 액세스 할 수 있으므로 개인 변수에 액세스 할 수 있습니다.

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

여전히 비공개 변수를 가질 수 있지만 프로토 타입에 정의 된 메소드가 해당 메소드에 액세스 할 수있게 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()); };

14
"반복 범위 지정"은 "friend"키워드가있는 C ++ 기능입니다. 기본적으로 모든 함수는 친숙한 프로토 타입을 정의해야합니다. 슬프게도이 개념은 JS가 아닌 C ++입니다. (
TWiStErRob

1
이 게시물을 즐겨 찾기 목록 맨 위에 추가하고 유지하고 싶습니다.
Donato

2
나는 이것의 요점을 보지 못합니다-당신은 아무것도하지 않는 추상화 계층 만 추가하고 있습니다. secret의 속성을 만들 수도 있습니다 this. JavaScript는 프로토 타입이 '작성 사이트'컨텍스트가 아닌 호출 사이트 컨텍스트에 바인딩되므로 프로토 타입이있는 전용 변수를 지원하지 않습니다.
nicodemus13

1
person.getSecret()그럼 왜 안 해 ?
Fahmi

1
왜 이렇게 많은 투표가 있습니까? 이것은 변수를 private으로 만들지 않습니다. 위에서 언급했듯이 person.getSecret ()을 사용하면 어디서나 해당 개인 변수에 액세스 할 수 있습니다.
alexr101

64

업데이트 : 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는 IS 에만 프로토 타입 함수가 호출 될 때마다 다릅니다 것.
따라서 모든 개인 필드는을 통해 액세스 할 수 있어야합니다. 즉, 공개 필드가 this됩니다. 따라서 우리는 _private필드의 명명 규칙을 고수 합니다.

클로저와 프로토 타입의 혼합을 방해하지 마십시오

클로저 변수를 프로토 타입 메소드와 혼합 해서는 안된다고 생각합니다 . 둘 중 하나를 사용해야합니다.

클로저를 사용하여 개인 변수에 액세스하면 프로토 타입 메서드가 변수에 액세스 할 수 없습니다. 따라서 폐쇄 부를 노출시켜야합니다.this 즉, 공개적으로 공개합니다. 이 방법으로는 얻을 것이 거의 없습니다.

어느 것을 선택합니까?

정말 간단한 객체의 경우 클로저가있는 일반 객체를 사용하십시오.

상속, 성능 등을 위해 프로토 타입 상속이 필요한 경우 "_private"명명 규칙을 따르고 클로저를 신경 쓰지 마십시오.

JS 개발자가 왜 필드를 진정으로 비공개로 만들기 위해 열심히 노력하는지 이해하지 못합니다.


4
안타깝게도 _private프로토 타입 상속을 활용하려는 경우 명명 규칙이 여전히 최상의 솔루션입니다.
호감

1
ES6는 새로운 개념 인을 가지게 Symbol되는데, 이는 개인 필드를 만드는 훌륭한 방법입니다. 여기에 좋은 설명이 있습니다 : curiosity-driven.org/private-properties-in-javascript
Scott Rippey

1
아니요, Symbol클래스 전체를 포괄하는 폐쇄를 유지할 수 있습니다 . 이렇게하면 모든 프로토 타입 메서드가 Symbol을 사용할 수 있지만 클래스 외부에 노출되지는 않습니다.
Scott Rippey

2
링크 된 기사에 " 기호는 개인 이름과 유사하지만 개인 이름과 달리 실제 개인 정보를 제공하지 않습니다 . "라고 표시되어 있습니다. 실제로 인스턴스가있는 경우을 사용하여 심볼을 얻을 수 있습니다 Object.getOwnPropertySymbols. 따라서 이것은 모호한 개인 정보 보호입니다.
Oriol

2
@Oriol 그래, 프라이버시는 엄청나게 모호하다. 여전히 기호를 반복 할 수 있으며를 통해 기호의 목적을 추론 할 수 toString있습니다. 이것은 Java 또는 C #과 다르지 않습니다 ... private 멤버는 리플렉션을 통해 여전히 액세스 할 수 있지만 일반적으로 모호합니다. "JS 개발자가 왜 필드를 진정으로 비공개로 만들기 위해 열심히 노력하는지 이해하지 못합니다."
Scott Rippey

31

내가 이것을 읽을 때, 그것은 어려운 도전처럼 들렸다. 그래서 나는 길을 알아 내기로 결정했다. 내가 생각 해낸 것은 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);

이 방법으로 오류가 발생하는 사람의 의견을 듣고 싶습니다.


4
잠재적 인 우려 중 하나는 다른 인스턴스 ID를 사용하여 모든 인스턴스가 다른 인스턴스 전용 var에 액세스 할 수 있다는 것입니다. 반드시 나쁜 일이 ...
밈 H. 라이트

15
모든 생성자 호출시 프로토 타입 함수를 재정의
Lu4

10
@ Lu4 사실인지 잘 모르겠습니다. 생성자는 클로저 내에서 반환됩니다. 프로토 타입 함수가 정의 된 유일한 시점은 바로 호출 된 함수 표현식입니다. 위에서 언급 한 개인 정보 문제는 언뜻보기에 나에게 좋아 보입니다.
guypursey 2013

1
@ MimsH.Wright 다른 언어를 사용 하면 같은 클래스의 다른 객체에 대한 액세스를 허용 하지만 참조 할 때만 가능합니다. 이를 위해 객체 포인터를 키로 사용하는 함수 뒤에 개인 정보를 숨길 수 있습니다 (ID에 따라). 이런 식으로 당신은 당신이 알고있는 객체의 개인 데이터에만 접근 할 수 있습니다. 그러나,이 구현은 이것에 대한 더 깊은 문제를 조명합니다. 생성자 함수가 될 때까지 개인 객체는 가비지 수집되지 않습니다.
토마스 나딘

3
i모든 인스턴스에 추가되었음을 언급하고 싶습니다 . 따라서 완전히 "투명"하지는 않으며 i여전히 변조 될 수 있습니다.
Scott Rippey

18

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

47
이 예제는 끔찍한 연습 인 것 같습니다. 프로토 타입 방법을 사용하는 요점은 모든 인스턴스에 대해 새로운 방법을 만들 필요가 없다는 것입니다. 당신은 어쨌든 그렇게하고 있습니다. 모든 방법에 대해 다른 방법을 만들고 있습니다.
Kir

2
@ArmedMonkey 개념은 건전 해 보이지만, 프로토 타입 함수가 사소한 것이기 때문에 이것이 나쁜 예라는 데 동의했습니다. 프로토 타입 함수가 '개인'변수에 대한 간단한 get / set 액세스를 필요로하는 훨씬 긴 함수 인 경우에는 의미가 있습니다.
팬케이크

9
왜 노출을 _set통해 귀찮게 set합니까? 왜 처음 set부터 이름을 지정하지 않습니까?
Scott Rippey

15

자바 스크립트 안티 패턴으로 "생성자에 프로토 타입 할당을하는 것"을 설명하는 것이 좋습니다. 생각 해봐 너무 위험합니다.

두 번째 객체 (예 : b)를 만들 때 실제로 수행하는 작업은 해당 프로토 타입을 사용하는 모든 객체에 대해 해당 프로토 타입 기능을 재정의하는 것입니다. 이것은 예제에서 객체 a의 값을 효과적으로 재설정합니다. 공유 변수를 원하고 모든 객체 인스턴스를 미리 만들면 효과가 있지만 너무 위험합니다.

최근에 작업하고있는 일부 Javascript 에서이 정확한 안티 패턴으로 인한 버그를 발견했습니다. 작성중인 특정 오브젝트에 끌어서 놓기 핸들러를 설정하려고 시도했지만 대신 모든 인스턴스에 대해 수행했습니다. 안좋다.

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


10

@ 카이

작동하지 않습니다. 당신이 할 경우

var t2 = new TestClass();

그런 다음 t2.prototypeHellot의 개인 섹션에 액세스합니다.

안 서연

샘플 코드는 정상적으로 작동하지만 실제로는 모든 인스턴스가 공유하는 "정적"비공개 멤버를 만듭니다. 모건 코드가 찾은 솔루션이 아닐 수도 있습니다.

지금까지 개인 해시와 추가 정리 기능을 도입하지 않고도 쉽고 깔끔한 방법을 찾지 못했습니다. 개인 멤버 함수는 다음과 같이 어느 정도 시뮬레이션 할 수 있습니다.

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

요점을 명확하게 이해했지만 코드 스 니펫이 무엇을하려고하는지 설명해 주시겠습니까?
Vishwanath

privateFoo를 얻을 때 완전히 비공개이므로 보이지 않습니다 new Foo(). 에 bar()액세스 할 수있는 공개 메소드 만 있습니다 privateFoo. 간단한 변수와 객체에 대해 동일한 메커니즘을 사용할 수 있지만 항상 privates정적이며 사용자가 만든 모든 객체가 공유 한다는 점을 명심해야합니다 .
Philzen

6

네 가능합니다. 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 디자인 패턴


4
링크 전용 답변은 일반적으로 SO에 찡그립니다. 예를 보여주세요.
Corey Adler

이 기사에는 예제가 들어 있으므로 여기를 참조하십시오
Edward

5
하지만 나중에 해당 사이트에서 다운되면 어떻게됩니까? 그러면 누군가는 어떻게 예를보아야합니까? 정책은 링크의 가치있는 모든 것을 여기에 보관할 수 있도록되어 있으며 이것이 우리가 통제하지 않는 웹 사이트에 의존 할 필요는 없습니다.
Corey Adler

3
@ 에드워드, 당신의 링크는 흥미로운 읽을 거리입니다! 그러나 프로토 타입 기능을 사용하여 개인 데이터에 액세스하는 주된 이유는 모든 객체가 동일한 공용 기능으로 메모리를 낭비하지 않도록하기위한 것입니다. 공용 메소드의 경우 프로토 타입 함수를 일반 공용 함수로 랩핑해야하므로 설명하는 방법으로이 문제점을 해결할 수 없습니다. 단일 공용 함수에 결합 된 많은 ppf가있는 경우 패턴이 메모리를 절약하는 데 유용 할 수 있습니다. 다른 용도로 사용합니까?
Dining 철학자

@DiningPhilosofer, 내 기사를 주셔서 감사합니다. 네, 그렇습니다. 우리는 여전히 인스턴스 함수를 사용합니다. 그러나 아이디어는 모든 무거운 작업을 수행하는 PPF 대응 업체를 다시 호출하여 가능한 한 경량화하는 것입니다. 결국 모든 인스턴스는 물론 래퍼를 통해 동일한 PPF를 호출하므로 특정 메모리 절약이 예상 될 수 있습니다. 문제는 얼마입니다. 상당한 절약을 기대합니다.
에드워드

5

액세서 검증 을 사용하여 실제로이를 달성 할 수 있습니다 .

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

이 예제는 Prototypal Functions & Private Data 에 대한 내 게시물에서 왔으며 더 자세히 설명되어 있습니다.


1
이 답변은 너무 "영리한"것은 유용하지 않지만 IFFE 바운드 변수를 비밀 핸드 셰이크로 사용하는 답변이 마음에 듭니다. 이 구현은 너무 많은 클로저를 사용하여 유용하지 않습니다. 프로토 타입에서 정의 된 메소드를 갖는 요점은 각 오브젝트의 각 메소드에 대해 새 함수 오브젝트를 구성하지 못하게하는 것입니다.
greg.kindel

이 방법은 비밀 키를 사용하여 신뢰할 수있는 프로토 타입 방법과 신뢰할 수없는 프로토 타입 방법을 식별합니다. 그러나 키의 유효성을 검사하는 것은 인스턴스이므로 키를 인스턴스로 보내야합니다. 그러나 신뢰할 수없는 코드는 가짜 인스턴스에서 신뢰할 수있는 메서드를 호출하여 키를 훔칠 수 있습니다. 그리고이 키를 사용하여 실제 인스턴스에서 신뢰할 수있는 새로운 방법을 만듭니다. 따라서 이것은 모호한 개인 정보 보호입니다.
Oriol

4

현재 자바 스크립트, 나는이 있음을 상당히 확신 하나하나 개 가하는 방식으로 개인 상태 에서 액세스, 프로토 타입 아무것도 추가하지 않고, 기능을 대중this . 답은 "약한지도"패턴을 사용하는 것입니다.

요약하자면 : 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];
        }
    };
}

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

그러나 두 가지 경고가 있습니다. 첫째, 비용 성능 - 당신이 개인 데이터에 액세스 할 때마다, 그것은의 O(n)작업, n인스턴스의 수이다는. 따라서 인스턴스 수가 많은 경우에는이 작업을 수행하지 않을 것입니다. 둘째, 인스턴스가 끝나면 전화해야합니다.destroy . 그렇지 않으면 인스턴스와 데이터가 가비지 수집되지 않으며 메모리 누수가 발생합니다.

이것이 바로 나의 원래의 대답 인 "You should't '" 가 제가 고집하고 싶은 이유 입니다.


범위를 벗어나기 전에 Person 인스턴스를 명시 적으로 삭제하지 않으면 weakmap이 인스턴스를 참조하지 않아 메모리 누수가 발생합니까? Person의 다른 인스턴스가 변수에 액세스 할 수 있고 Person에서 상속 된 인스턴스가 액세스 할 수 있으므로 protected 패턴을 생각해 냈습니다 . 그냥 추가 처리 이외의 창피 이점이있는 경우하지 않도록합니다 (음부에 액세스하는만큼 보이지 않는) 그래서 그것을 밖으로 바이올린을 stackoverflow.com/a/21800194/1641941 / 개인 보호 오브젝트 반환 코드를 호출 이후 고통 그런 다음 개인 / 보호 대상을 변경할 수 있습니다.
HMR

2
@HMR 예, 개인 데이터를 명시 적으로 파기해야합니다. 나는이 경고를 내 대답에 추가 할 것입니다.
Scott Rippey

3

사용 bindcall방법 을 활용하여 더 간단한 방법이 있습니다 .

개인 변수를 객체로 설정하면 해당 객체의 범위를 활용할 수 있습니다.

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)

2
처음에 인스턴스화 된 메소드를 작성하는 대신 프로토 타입 메소드의 사본을 작성하는 이유는 무엇입니까?
호감

3

시도 해봐!

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

1
이는 caller엄격 모드에서는 허용되지 않는 구현 종속 확장 인에 의존합니다.
Oriol

1

여기에 내가 생각해 낸 것이 있습니다.

(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

이 구현의 주요 문제는 모든 인스턴스에서 프로토 타입을 재정의한다는 것입니다.


흥미롭게도 나는 시도를 좋아하고 똑같은 것을 생각하고 있었지만 모든 인스턴스화에서 프로토 타입 기능을 재정의하는 것은 상당히 큰 한계입니다. 이것은 CPU주기가 낭비 된 것이 아니라 나중에 프로 토토 이프를 변경하면 다음 인스턴스 생성시 생성자에 정의 된대로 원래 상태로 "재설정"됩니다. : /
Niko Bellic

1
이것은 프로토 타입을 재정의했을뿐만 아니라 각 인스턴스에 대한 새로운 생성자를 정의합니다. 따라서 "인스턴스"는 더 이상 같은 클래스의 인스턴스가 아닙니다.
Oriol

1

이 작업을 수행하는 매우 간단한 방법이 있습니다

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

JavaScript 프로토 타입은 황금색입니다.


2
새 인스턴스가 생성 될 때마다 새 함수를 생성하므로 생성자 함수에서 프로토 타입을 사용하지 않는 것이 좋습니다.
whamsicore

@whamsicore 네, 맞습니다. 그러나이 경우 인스턴스화 된 모든 단일 객체에 대해 공유 클로저를 정렬해야하므로 필수적입니다. 즉, 함수 정의는 생성자 내부에있는 이유이고 우리는을 참조해야 SharedPrivate.prototype로서 this.constructor.prototype그것은 ... GETP과 SETP 여러 번 다시 정의 할 수 별거 아니
Redu

1

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

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

이 메서드 접근 자 패턴이라고 합니다. 근본적인 아이디어는 우리가 가지고있다 폐쇄 하는 폐쇄 내부를, 그리고 우리는 만들 개인 개체를 당신이있는 경우에만 액세스 할 수 (생성자) 키를 .

관심이 있으시면 내 기사 에서 이에 대해 더 많이 읽을 수 있습니다 . 이 방법을 사용하면 클로저 외부에서 액세스 할 수없는 객체 별 속성을 만들 수 있습니다. 따라서 생성자 또는 프로토 타입에서 사용할 수 있지만 다른 곳에서는 사용할 수 없습니다. 나는이 방법이 어디서나 사용되는 것을 보지 못했지만 실제로 강력하다고 생각합니다.


0

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

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

4
그런 다음 MyClass의 모든 인스턴스간에 변수가 공유됩니다.
호감

0

프로토 타입이 아닌 다음과 같이 생성자 함수에 직접 메소드를 추가 할 수도 있습니다.

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!

0

이 문제에 대한 가장 간단한 해결책을 찾는 동안 내가 생각해 낸 것이 있습니다. 아마도 누군가에게 유용 할 수 있습니다. 나는 자바 스크립트를 처음 사용하므로 코드에 문제가있을 수 있습니다.

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

0

나는 오늘 똑같은 질문에 직면했으며 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로 테스트되었습니다. 난 당신의 의견을 읽고 싶어요.


다음은 참조입니다 : '한 걸음 더 가까이'섹션을 확인하십시오. philipwalton.com/articles/…
jimasun

0
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()

어때? 개인 접근자를 사용합니다. 유스 케이스에 따라 변수를 설정하지 않아도 변수를 가져올 수 있습니다.


이것은 유용한 방법으로 질문에 대답 하지 않습니다 . 왜 이것이 답이라고 믿습니까? 어떻게 작동합니까? 컨텍스트 나 의미없이 코드변경하도록 누군가에게 지시하면 자신이 잘못한 것을 배우는 데 도움이되지 않습니다.
GrumpyCrouton

그는 클래스의 모든 단일 인스턴스에서 숨겨진 변수를 만들지 않고도 프로토 타입을 통해 클래스의 숨겨진 개인 변수에 액세스하는 방법을 원했습니다. 위의 코드는이를 수행하는 예제 방법입니다. 그 질문에 어떻게 대답하지 않습니까?
dylan0150

나는 그것이 질문에 대한 답이 아니라고 말하지 않았다. 나는 그것이 배우는 데 도움이되지 않기 때문에 유용한 답변 이 아니라고 말했다 . 코드, 작동하는지, 올바른 방법인지 설명해야합니다. 질문 작성자라면 학습을 장려하지 않기 때문에 귀하의 답변을 수락하지 않을 것입니다. 내가 뭘 잘못하고 있는지 또는 주어진 코드가 무엇을하는지 또는 어떻게 작동하는지 가르쳐주지 않습니다.
GrumpyCrouton

0

하나의 솔루션이 있지만 결함이 없는지 확실하지 않습니다.

그것이 작동하려면 다음 구조를 사용해야합니다.

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

이것이 작동하는 방법은 "privateFields"개인 변수 객체에 액세스하기 위해 인스턴스 함수 "this.getPrivateFields"를 제공하지만이 함수는 정의 된 기본 클로저 내에서 "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);

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

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

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

0

오늘이 문제를 해결하고 있었으며 이것이 Symbols를 사용하지 않고 찾을 수있는 유일한 솔루션이었습니다. 이것에 대한 가장 좋은 점은 실제로 모두 완전히 비공개 일 수 있다는 것입니다.

이 솔루션은 기본적으로 개인용 스토리지 캐시 (약한 맵 사용)의 중재자가되는 자체 개발 모듈 로더를 기반으로합니다.

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

0

나는 이것이 요청 된 지 10 년이 넘었 음을 알고 있지만 프로그래머 생활에서 n 번째로 이것에 대해 생각하고 아직 완전히 좋아하는지 알 수없는 해결책을 찾았습니다. . 이 방법론은 이전에 문서화되지 않았으므로 "private / public dollar pattern"또는 _ $ / $ pattern으로 이름을 지정 합니다.

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

이 개념은 Interface 객체 를 반환하는 생성자 함수를 반환하는 ClassDefinition 함수를 사용 합니다. 인터페이스의 유일한 메소드는 생성자 객체에서 해당 함수를 호출 하는 인수를 수신하는 것입니다.$namename 입니다. 에 전달 된 는 호출에서 전달됩니다.

세계적으로 정의 도우미 함수는 ClassValues모든 필드에 저장 필요에 따라 개체를. 로 액세스 하는 기능을 정의합니다 . 이것은 짧은 get / set 패턴을 따르므로 전달되면 새 변수 값으로 사용됩니다._$namevalue

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

      return values[name];
    }
  };
};

전역 적으로 정의 된 함수 Interface는 개체와 Values개체를 사용 하여 매개 변수 뒤에 이름이 지정된 함수를 찾아 검사 하여 범위가 지정된 개체 로 호출하는 _interface단일 함수 하나 를 반환 합니다. 전달 된 추가 인수 는 함수 호출시 전달됩니다.$objnamevalues$

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은 IS, 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대한 정의가 없으므로 _$외부 객체에서 값에 액세스 할 수 없습니다. 프로토 타입 화 된 각 함수 본문이 function thisvalues객체로 설정되어 있으므로 $생성자 형제 함수를 직접 호출하면 예외가 발생합니다. _ $ / $ 패턴 의 요구가 너무 프로토 타입을 함수 몸에 따라야합니다. 아래 샘플 사용량.

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

_ $는 / $ 패턴은 완벽하게 프로토 타입 클래스의 값의 전체 개인 정보 보호를 할 수 있습니다. 내가 이것을 사용할 것인지, 결함이 있는지는 모르겠지만 좋은 퍼즐이었습니다!


0

ES6 약한지도

ES6 기반의 간단한 패턴을 사용하여 WeakMaps프로토 타입 함수에서 접근 할 수있는 개인 멤버 변수 를 얻을 수 있습니다 .

참고 : WeakMaps를 사용 하면 가비지 콜렉터가 사용되지 않은 인스턴스를 식별하고 버릴 수 있도록하여 메모리 누수에 대한 안전을 보장 합니다.

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

이 패턴에 대한 자세한 설명은 여기를 참조하십시오.


-1

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

this.privateField개인 필드가 아닙니다. 그것은 외부에서 액세스 할 수 있습니다 :t.privateField
V. Rubinetti

-2

생성자 정의 내에서 프로토 타입 할당을 사용할 수 있습니다.

변수는 프로토 타입 추가 메소드에 표시되지만 함수의 모든 인스턴스는 동일한 SHARED 변수에 액세스합니다.

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

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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.