programing

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

telebox 2023. 7. 31. 21:18
반응형

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

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

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

효과:

t.nonProtoHello()

하지만 이것은 그렇지 않습니다.

t.prototypeHello()

저는 생성자 내부에서 제 방식을 정의하는 데 익숙하지만 몇 가지 이유로 인해 이 방식에서 벗어나고 있습니다.

아니요, 방법이 없어요.그것은 본질적으로 역으로 범위를 좁힐 것입니다.

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

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

변수는 할 수 를 Getter와 Setter에 .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 개발자들이 진정으로 사적인 분야를 만들기 위해 그렇게 노력하는지 이해할 수 없습니다.

제가 이 글을 읽었을 때, 그것은 어려운 도전처럼 들렸기 때문에 저는 방법을 찾기로 결심했습니다.제가 생각해낸 것은 CRAAAZY였지만 완전히 효과가 있습니다.

먼저 클래스를 즉시 함수로 정의하여 해당 함수의 일부 개인 속성에 액세스할 수 있도록 했습니다.이렇게 하면 일부 개인 데이터를 얻을 수 있지만 개인 데이터를 설정하려고 하면 모든 개체가 동일한 값을 공유한다는 것을 곧 알게 됩니다.

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

이 방법으로 오류를 발견한 사람들의 피드백을 받고 싶습니다.

더그 크록포드의 페이지를 보세요.개인 변수의 범위에 접근할 수 있는 것으로 간접적으로 해야 합니다.

다른 예:

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의 값이 효과적으로 재설정됩니다.공유 변수를 원하는 경우와 모든 개체 인스턴스를 미리 생성할 수 있지만 너무 위험하다고 생각되는 경우에 사용할 수 있습니다.

제가 최근에 작업한 자바스크립트에 버그가 있다는 것을 발견했는데, 이는 바로 이 안티패턴 때문입니다.생성되는 특정 개체에 끌어서 놓기 핸들러를 설정하려고 했지만 모든 인스턴스에 대해 설정하고 있었습니다.좋지 않습니다.

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는 Private Prototype Functions의 약자입니다.기본 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());

이 예는 프로토팔 기능 및 개인 데이터에 대한 제 게시물에서 나온 것으로, 여기에서 더 자세히 설명되어 있습니다.

현재 자바스크립트에서, 저는 개인 상태를 가질 수 있는 단 하나의 방법이 있으며, 공개적인 것을 추가하지 않고 프로토타입 기능에서 액세스할 수 있습니다.this정답은 "약한 지도" 패턴을 사용하는 것입니다.

요약하자면: ThePerson클래스에는 키가 사용자의 인스턴스이고 값은 개인 저장소에 사용되는 일반 개체인 단일 약한 맵이 있습니다.

다음은 완전한 기능의 예입니다. (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];
        }
    };
}

제가 말씀드린 것처럼, 이것이 세 부분을 모두 성취하는 유일한 방법입니다.

하지만 두 가지 경고가 있습니다.이 저하됩니다. 데이터에 액세스할 때마다 첫째, 개데에액할때성저마다능니하됩이다세스터이인▁firsts다▁an▁perform저니하ance▁--',됩성▁every▁costs입니다.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

저는 이 방법을 접근자 패턴이라고 부릅니다.기본적인 아이디어는 폐쇄 내부가 있는 폐쇄를 사용하고 가 있는 경우에만 액세스할 수 있는 개인 개체(생성자)를 생성한다는 것입니다.

만약 당신이 관심이 있다면, 당신은 제 기사에서 이것에 대해 더 읽을 수 있습니다.이 방법을 사용하면 폐쇄 외부에서 액세스할 수 없는 개체별 속성을 만들 수 있습니다.따라서 생성자 또는 프로토타입에서 사용할 수 있지만 다른 곳에서는 사용할 수 없습니다.이 방법은 어디에서도 사용된 것을 본 적이 없지만, 정말 강력하다고 생각합니다.

변수를 더 높은 범위에 둘 수는 없습니까?

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

제가 이 문제에 대한 가장 간단한 해결책을 찾으면서 생각해 낸 것이 있습니다. 아마도 누군가에게 유용할 수도 있습니다.자바스크립트가 처음이라 코드에 문제가 있을 수 있습니다.

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

저는 오늘 똑같은 질문에 직면했고 스콧 리피 1급 대응에 대해 자세히 설명한 후 ES5와 호환되고 효율적인 매우 간단한 해결책(IMHO)을 생각해냈습니다(_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 인스턴스(instance) 함수를 사용합니다.
  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);
};

오늘 이것을 가지고 놀고 있었는데 이것이 심볼을 사용하지 않고 찾을 수 있는 유일한 해결책이었습니다.이것의 가장 좋은 점은 사실 모두가 완전히 사적일 수 있다는 것입니다.

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

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

이 질문을 받은 지 10년이 넘었다는 것을 알지만, 저는 프로그래머 생활에서 n번째로 이것에 대해 생각을 해보았고, 제가 아직 완전히 마음에 들지 않는 가능한 해결책을 찾았습니다.저는 이 방법론이 문서화된 적이 없기 때문에 "개인/공공 달러 패턴" 또는 _$/$ 패턴으로 명명하겠습니다.

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

이 개념은 인터페이스 개체를 반환하는 생성자 함수를 반환하는 ClassDefinition 함수를 사용합니다.인터페이스의 유일한 방법은$ 것은는을 것.name객체에서 , 가 된 경우 " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "" " " " " " " " " " " " " "name호출에서 통과됩니다.

기능은 다음과 .ClassValues필요에 따라 모든 필드를 개체에 저장합니다.그것은 정의합니다._$로 접근하는 name이는 짧은 get/set 패턴을 따르므로 다음과 같습니다.value를 통과하면 새 변수 값으로 사용됩니다.

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

      return values[name];
    }
  };
};

으로 정의된 함수는 과 .Interface와 객를가니다갑져와체를 사용합니다.Values▁▁return_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 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. 를 바꿉니다.var privateField = "hello"와 함께this.privateField = "hello".
  2. 에서 프토타서교체를 합니다.privateField와 함께this.privateField.
  3. 이 아닌 에도 프토타이아교다체니합도에우경입닌로다▁in▁replace니교합▁the▁also체▁nonotype프를 대체합니다.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()

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

변수는 추가된 프로토타입 메서드에 표시되지만 함수의 모든 인스턴스는 동일한 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();

저는 이것이 유용하기를 바랍니다.

언급URL : https://stackoverflow.com/questions/436120/accessing-private-member-variables-from-prototype-defined-functions

반응형