프로토타입 정의 함수에서 개인 멤버 변수 액세스
프로토타입 정의 방법에서 "개인" 변수(생성자에 정의된 변수)를 사용할 수 있는 방법이 있습니까?
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는 다음과 같은 문제를 해결합니다.
- 프로토타입 기능은 개인 인스턴스 데이터에 액세스합니다.
- 프로토타입 기능은 비공개로 설정할 수 있습니다.
첫 번째로, 그냥:
- 프로토타입 함수에서 액세스할 수 있는 모든 개인 인스턴스 변수를 별도의 데이터 컨테이너에 넣습니다.
- 데이터 컨테이너에 대한 참조를 모든 프로토타입 함수에 매개 변수로 전달합니다.
그렇게 간단하다.예:
// 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;
}
...
자세한 내용은 여기를 참조하십시오.
실제로 액세스 또는 확인을 사용하여 이를 수행할 수 있습니다.
(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 인스턴스(instance) 함수를 사용합니다.
- 생성자와 모든 프로토타입 기능에 클로저를 적용합니다.
- 생성된 인스턴스는 정의된 폐쇄 외부에서 수행됩니다.
코드는 다음과 같습니다.
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;
}());
이 패턴에 대한 자세한 설명은 여기에서 확인할 수 있습니다.
코드에서 세 가지를 변경해야 합니다.
- 를 바꿉니다.
var privateField = "hello"와 함께this.privateField = "hello". - 에서 프토타서교체를 합니다.
privateField와 함께this.privateField. - 이 아닌 에도 프토타이아교다체니합도에우경입닌로다▁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
'programing' 카테고리의 다른 글
| iOS 6 기기로 원격 디버깅하는 동안 Safari에 "No Inspectable Applications"(검사 가능한 응용 프로그램 없음)가 표시되는 이유는 무엇입니까? (0) | 2023.07.31 |
|---|---|
| Delphi 응용 프로그램을 테스트하는 가장 좋은 방법 (0) | 2023.07.31 |
| 자바스크립트 코드를 *순서대로* 실행하는 방법 (0) | 2023.07.31 |
| Why can't I set a nullable int to null in a ternary if statement? (0) | 2023.07.31 |
| 웹 파일 구조에서 공급업체는 무엇을 의미합니까? (0) | 2023.07.26 |