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

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

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는 IS 에만 프로토 타입 함수가 호출 될 때마다 다릅니다 것.
따라서 모든 개인 필드는을 통해 액세스 할 수 있어야합니다. 즉, 공개 필드가 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


답변

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

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

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

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


답변

@ 카이

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

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


답변

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