-
Notifications
You must be signed in to change notification settings - Fork 113
private and protected are like static and super #90
Comments
@d8corp how would that work accessing a field on another object - ie, not |
Another objects should not to have access to private and protected fields because they are PRIVATE. If another object will have an access to private fields then they are not private. |
This proposal is “class private”, so code in the class can access private data on any instance of itself. One important and common use case for this is to be able to compare two objects by their private data; another is to brand-check an object using a private field to see if it’s truly an instance of that class. In other words, it’s private the only way JS ever has privacy - by lexical position in the code. |
I think protected field should be available in each instance classes and private field should be available only in current class. This example for understand how it works: (() => {
const protectedFields = new WeakMap()
const privateMarker = Symbol('private')
const X = (() => {
function X () {
protectedFields.set(this, {
protectedFieldX: 'protected field x'
})
X[privateMarker].set(this, {
privateFieldX: 'private field x'
})
}
X[privateMarker] = new WeakMap()
X.prototype.test1 = function () {
const $protected = protectedFields.get(this)
const $private = X[privateMarker].get(this)
console.log('X protectedFieldX', this, $protected.protectedFieldX)
console.log('X privateFieldX', this, $private.privateFieldX)
}
return X
})()
const Y = (ext => {
function Y () {
ext.apply(this)
protectedFields.set(this, Object.assign({}, protectedFields.get(this), {
protectedFieldY: 'protected field y'
}))
Y[privateMarker].set(this, {
privateFieldY: 'private field y'
})
}
Y[privateMarker] = new WeakMap()
Y.prototype.test2 = function () {
const $protected = protectedFields.get(this)
const $private = Y[privateMarker].get(this)
console.log('Y protectedFieldX', this, $protected.protectedFieldX)
console.log('Y protectedFieldY', this, $protected.protectedFieldY)
console.log('Y privateFieldX', this, $private.privateFieldX)
console.log('Y privateFieldY', this, $private.privateFieldY)
}
Y.prototype.__proto__ = ext.prototype
return Y
})(X)
const x = new X()
const y = new Y()
console.log('-- x.test1 --')
x.test1()
console.log('-- y.test1 --')
y.test1()
console.log('-- y.test2 --')
y.test2()
})() it can look like class X {
protected protectedFieldX = 'protected field x'
private privateFieldX = 'private field x'
test1 () {
console.log('X protectedFieldX', this, protected.protectedFieldX)
console.log('X privateFieldX', this, private.privateFieldX)
}
}
class Y extends X {
protected protectedFieldY = 'protected field y'
private privateFieldY = 'private field y'
test2 () {
console.log('Y protectedFieldX', this, protected.protectedFieldX)
console.log('Y protectedFieldY', this, protected.protectedFieldY)
console.log('Y privateFieldX', this, private.privateFieldX)
console.log('Y privateFieldY', this, private.privateFieldY)
}
}
const x = new X()
const y = new Y()
console.log('-- x.test1 --')
x.test1()
console.log('-- y.test1 --')
y.test1()
console.log('-- y.test2 --')
y.test2() Use protectedFields and privateMarker to compare this objects |
@d8corp since symbols are always fully public, in your implementation, what you've called "private" is just a normal public property, that exposes the WeakMap you call "protected" - making it also fully public. |
I just give you an approximate example how to transpile it. You can make it as you wish inside an engine. I don't understand what's a problem to use it. // imagine protectedProps and privateProps are inside js engine and you haven't an access them
const protectedProps = new WeakMap()
const privateProps = new WeakMap()
class X {
constructor () {
// imagine js engine does it instead of "protected protectedX = 'protected x'" in body of this class
protectedProps.set(this, {
protectedX: 'protected x'
})
// imagine js engine does it instead of "private privateX = 'private x'" in body of this class
privateProps.get(X).add(this, {
privateX: 'private x'
})
}
test () {
// imagine js engine gives you access to "protected" instead of $protected
const $protected = protectedProps.get(this)
// imagine js engine gives you access to "private" instead of $private
const $private = privateProps.get(X).get(this)
}
}
// this is in js engine
privateProps.set(X, new WeakMap()) |
Related tc39/proposal-private-methods#28
This might look like class A {
// uses the private keyword to create private fields, methods, getters etc.
private value = 'string' // [1]
private method () {} // [2]
// uses the static keyword to create static fields, methods, getters etc
static value = 'string' // [3]
static method () {} // [4]
// uses neiher to create instance fields, methods, getters etc
value = 'string' // [5]
method () {} // [6]
constructor() {
// invoke instance method
this['method'](this.value) // [7]
// use private.name or private['name'] to access private fields, methods, getters etc.
private['method'](private['value']) // [8]
// all of the following invocations are equivalent to the previous
private.method(private.value)
private(this)['method'](private(this)['value'])
private(this).method(private(this).value)
// assign private values
private.length = 1 // [9]
private['length'] = 1
static['method'](static['value']) // [10]
static.method(static.value)
}
} If it where possible to introduce private symbols or something akin to private symbols this could be compiled to/equivalent to the following prototype-based constructor approach. var PrivateSymbolForValue = Symbol.private('value')
var PrivateSymbolForMethod = Symbol.private('method')
var PrivateSymbolForLength = Symbol.private('length')
function A () {
this[PrivateSymbolForValue] = 'string' // [1]
this.value = 'string' // [5]
this['method']() // [7]
this[PrivateSymbolForMethod](this[PrivateSymbolForValue]) // [8]
this[PrivateSymbolForLength] = 1 // [9]
this.constructor['method'](this.constructor['value']) // [10]
}
A.prototype[PrivateSymbolForMethod] = function () {} // [2]
A.prototype.method = function () {} // [6]
A.value = 'string' // [3]
A.method = function () {} // [4] Related proposal-about-private-symbol |
@thysultan @ljharb This sentences are permanent:
see "Access Control and Inheritance" this function someFunction () {
let x = 1
}
someFunction['x'] // 1 |
@thysultan |
@d8corp You're right |
What do you think about that? class X {
value = 1 // prototype value, if you use "use strict" it can be only primitive
this value = 2
private value = 3
protected value = 4
static value = 5
constructor () {
console.log(this.value) // 2
console.log(private.value) // 3
console.log(protected.value) // 4
console.log(static.value) // 5
}
} |
@d8corp |
@d8corp I think you forgot how to access things on other objects for protected and private. E.g. the compare case. class X {
private id = Symbol('unique-id-of-instance'); // does this work? I assume it doesn't..?
constructor() {
private.id = Symbol('actually-unique-id'); // okay, writing a constructor does the trick
}
isEqualTo(other) {
// how do I get the private properties of another instance of X?
return private.id == other.id;
}
}
const x = new X();
x.isEqualTo(x); // ? edit: Oh, I see. You removed that feature. Unfortunately that makes it super confusing and also a lot less valuable. The usual expectation is that visibility is by class/type, not by instance/object. Having it per instance means any method involving more than exactly one object needs to leak implementation details. |
class X {
a = 1
method () {}
this b = 2
this method1 () {}
}
const x = new X()
/*
x = {
b: 2,
method1 () {},
__proto__: {
a: 1,
method () {}
}
}
*/ This is not the final version. |
I think in most cases enough to use opened fields for that. class X {
static id = Symbol('unique-id-of-instance')
[static.id] = true
isEqualTo(other) {
return other[static.id]
}
} Or const X = (() => {
const id = new WeakSet()
return class X {
constructor () {
id.add(this)
}
isEqualTo(other) {
return id.has(other)
}
}
})() And also we can come up with something else. For example new space like private, protected and etc. This is very flexible pattern. Also we can stay the idea of |
Unfortunately, this use of static fields will not work, since static public field initializers are evaluated after computed property names (so that the initializers could instantiate the class). |
@littledan class X {
static id = Symbol('unique-id-of-instance')
[this.constructor.id] = true // the this is not available
this [this.constructor.id] = true // the this is available
isEqualTo(other) {
return other[static.id]
}
} |
For why this proposal focuses on strongly private fields rather than symbols, see this FAQ entry. |
Prototype fieldsYou can imagine like the field is a variable which is one for each instance. const field = 'value' Also we create variables for each instance const filed1 = 'value'
const field2 = {
value: 'value'
}
function newClass () {
let field1 = field1
let field2 = field2
// you can change field1 like this.field1 = 'new value'
field1 = 'new value'
// and field1 from outside still equals 'value' but you can change an object
field2.value = 'new value'
// and field2 from outside changes also
} But the variables are inside the class and you have an access through this. class Class {
field ='value'
method () {
return this.field
}
} The prototype fields are something between static and public fields. Just you need to understand the prototype fields are single variables for all instances, they are like static fields. Public fieldsclass Class {
public field = 'value'
} This is the fields which inside newClass function, and you can cover prototype fields. const field = 'value'
function newClass () {
let field = 'value'
field = 'new value'
// ...
} But it is senselessly. // what you see
class Class {
const field = 'value'
let field = 'new value'
}
// what you write
class Class {
field = 'value'
public field = 'new value'
}
// maybe it's better for compatibility
class Class {
const field = 'value'
field = 'new value'
}
// or maybe better to use const for immutable prototype fields Static fieldsStatic fields are as usual but you need to understand that the fields can be changed. You can use 'static' inside methods to observe the changes. class X {
static field = 'value'
method () {
return static.field
}
}
class Y extends X {
static field = 'new value'
}
const y = new Y()
y.method() // returns 'new value' Private fieldsPrototype and public fields are available anywhere, if you need private space you can use private fields. They are available only inside body of the class. class Class {
private field = 'value'
method () {
return private.field
}
} I agree with @thysultan, you can use private like a function to get private fields of argument for this class because we have similar symptoms for 'super'. class Class {
private id = Symbol('Class id')
instanceOf (obj) {
return private(obj).id === private.id
}
} Protected fieldsclass Class {
protected field = 'value'
method () {
return protected.field
}
} This is like private but the fields available for extended classes Composite of fieldsIt will be nice if i have a possible to use type of field once for a few. class User {
exp = 0
ballance = 0
public
tasks = new Map(),
friends = new Map()
private
id = Symbol('user id'),
achievements = new Map()
get achievements () {
return new Map(private.achievements)
}
get level () {
return static.getLevel(this.exp)
}
isItMe (obj) {
static.speak(private(obj).id === private.id ? 'yes' : 'no')
}
static
getLevel (exp) {
return Math.round(Math.sqrt(exp + 1))
},
speak (text) {
console.log(text)
}
} |
This is a very nice syntax proposal. I like this a lot. It stays true to JavaScript: semantic wording. F.e. I love that
|
@littledan Can you re-open this? The suggestions here don't strictly dictate anything about implementation, you could easily argue that this can be implemented with Symbols. The syntax is similar to my |
If we switch to It's better to take the safer route in this regard, like many, especially @rdking, have pointed out many times. |
When I mentioned above,
By "to some extent" I was referring to the fact that @devsnek pointed out the following way to gain access to protected members. I replied in the following comments with some ideas on how to prevent it. The main point is, if I can basically achieve it in JavaScript, the JS engine itself could certainly achieve it because it has vast space in which to hide internals that are inaccessible from JavaScript. |
I think it'll be great
Or something like that.
I often use "this.constructor", it'll be nice to use "static" for that.
p.s. the sharp looks ugly
The text was updated successfully, but these errors were encountered: