是否可以在ES6类中创建私有属性?

举个例子。 如何阻止访问instance.property?

class Something {
  constructor(){
    this.property = "test";
  }
}

var instance = new Something();
console.log(instance.property); //=> "test"

当前回答

我用这个模式,它总是对我有效

class Test { constructor(data) { class Public { constructor(prv) { // public function (must be in constructor on order to access "prv" variable) connectToDb(ip) { prv._db(ip, prv._err); } } // public function w/o access to "prv" variable log() { console.log("I'm logging"); } } // private variables this._data = data; this._err = function(ip) { console.log("could not connect to "+ip); } } // private function _db(ip, err) { if(!!ip) { console.log("connected to "+ip+", sending data '"+this.data+"'"); return true; } else err(ip); } } var test = new Test(10), ip = "185.167.210.49"; test.connectToDb(ip); // true test.log(); // I'm logging test._err(ip); // undefined test._db(ip, function() { console.log("You have got hacked!"); }); // undefined

其他回答

除了给出的答案之外,您还可以使用代理来创建“私有属性”,使代理只对公共代码可用。实例只对构造函数、绑定方法和作为接收者的Proxy本身可用。

这比使用符号和弱映射有一些优势。

符号是可枚举的,可以用代理捕获。 当实例被代理为实例时,WeakMaps失败!== new Proxy(instance)

WeakMap失败。

const map = new WeakMap()

const instance = new SomeClass()
map.set(instance, 'foo')
// somewhere along the way in 3rd party code
const proxy = new Proxy(instance, {})
assert(map.set(instance) === map.get(proxy)) // fail
const proxy2 = new Proxy(proxy, {})
// more headache

使用代理用私有道具验证来装饰实例

getProxy = (instance) => new Proxy(instance, {

    get: (target, name, receiver) => {
        console.log('get', { target, name, receiver })
        if (name[0] === '_') throw new Error('Cannot access private property ' + name)
        return Reflect.get(target, name, receiver) 
    },
    set: (target, name, value, receiver) => {
        console.log('set', { target, name, value, receiver })
        if (name[0] === '_') throw new Error('Cannot set private property ' + name)
        return Reflect.set(target, name, value, receiver) 
    }
    
})


class PublicClass {

    constructor() {
        Object.defineProperty(this, '_privateProp', { enumerable: false, writable: true, configurable: false })
        return getProxy(this) // can be moved out as a decorator
    }

    getPrivatePropFail() {
        return this._privateProp // fail
    }

    getPrivateProp = () => {
        return this._privateProp // ok
    }

    setPrivateProp = (value) => {
        return this._privateProp = value // ok
    }

}


pub = new PublicClass()

try {
    console.log('get pub._privateProp', pub._privateProp)
} catch(e) {
    console.error(e) 
}
try {
    console.log('set pub._privateProp', pub._privateProp = 'you fail')
} catch(e) {
    console.error(e) 
}
pub.setPrivateProp('you ok')
console.log('pub.getPrivateProp()', pub.getPrivateProp())
console.log('pub', Object.keys(pub))

这种方法的优点

私有属性访问验证被装饰在实例上(可选)。 私有属性可以在控制台、调试器和测试环境中检查,属性简单(没有符号或映射) 您可以控制验证和错误处理

的缺点

代理增加了开销和抽象级别 调试将显示包装对象的Proxy() 访问私有道具的方法需要是箭头函数 当无意中暴露实例时,可能会泄漏私有道具。添加一个方法getSelf = () => this

注:

考虑到开销,这种方法可以用于属性封装和调试的清晰度超过开销的场景。例如,当从存储中填充模型时。如。setjson (json)将确保没有私有道具被破坏。

通过使用WeakMap和Proxy来确保“私有”属性不可见,同时允许在每个作用域上使用相同实例访问WeakMap,可以进一步调整此方法以提供更好的封装。然而,这牺牲了可读性和调试。

正如我们所知,ES6类不支持私有属性。

下面是我使用的方法(可能会有帮助)。基本上,我是在工厂内部包装一个类。

function Animal(name) {
    const privateData = 'NO experiments on animals have been done!';

    class Animal {
        constructor(_name) {
            this.name = _name;
        }
        getName() {
            return this.name
        }
        getDisclamer() {
            return `${privateData} Including ${this.name}`
        }
    }
    return new Animal(name)
}

我是一个初学者,所以很高兴听到这是一个坏的方法。

更新:看别人的回答,这个已经过时了。

简单的回答,不,ES6类不支持私有属性。

但是你可以模仿这种行为,不将新属性附加到对象,而是将它们保存在类构造函数中,并使用getter和setter来获取隐藏的属性。注意,在类的每个新实例上重新定义getter和setter。

ES6

class Person {
    constructor(name) {
        var _name = name
        this.setName = function(name) { _name = name; }
        this.getName = function() { return _name; }
    }
}

ES5

function Person(name) {
    var _name = name
    this.setName = function(name) { _name = name; }
    this.getName = function() { return _name; }
}

这取决于你问谁:-)

最大最小类提案中没有包含私有属性修饰符,这似乎已经进入了当前的草案。

但是,可能会支持私有名称,它允许私有属性—而且它们可能也可以用于类定义中。

使用WeakMap可以在类中拥有私有方法。

根据MDN网络文档:

WeakMap对象是键/值对的集合,其中键仅为对象,值可以是任意值。 键中的对象引用是弱保存的,这意味着如果不再有对该对象的其他引用,则它们是垃圾收集(GC)的目标。

这是一个创建带有私有成员_items的Queue数据结构的例子,该成员_items包含一个数组。

const _items = new WeakMap();

class Queue {    
    constructor() {
        _items.set(this, []);
    }

    enqueue( item) {
        _items.get(this).push(item);
    }    

    get count() {
        return _items.get(this).length;        
    }

    peek() {
        const anArray = _items.get(this);
        if( anArray.length == 0)
            throw new Error('There are no items in array!');

        if( anArray.length > 0)
            return anArray[0];
    }

    dequeue() {        
        const anArray = _items.get(this);
        if( anArray.length == 0)
            throw new Error('There are no items in array!');

        if( anArray.length > 0)
            return anArray.splice(0, 1)[0];
    }    
}

使用的例子:

const c = new Queue();
c.enqueue("one");
c.enqueue("two");
c.enqueue("three");
c.enqueue("four");
c.enqueue("five");
console.log(c);

私有成员_items是隐藏的,不能在Queue对象的属性或方法中看到:

然而,Queue对象中的私有成员_items可以通过以下方式访问:

const anArray = _items.get(this);