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

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

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

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

当前回答

就我个人而言,我喜欢绑定操作符::的建议,然后将其与@d13提到的解决方案结合起来,但现在坚持使用@d13的答案,在这里您为类使用export关键字,并将私有函数放在模块中。

还有一个棘手的解决方案,这里没有提到,下面是更实用的方法,将允许它在类中拥有所有的私有道具/方法。

Private.js

export const get = state => key => state[key];
export const set = state => (key,value) => { state[key] = value; }

. js

import { get, set } from './utils/Private'
export default class Test {
  constructor(initialState = {}) {
    const _set = this.set = set(initialState);
    const _get = this.get = get(initialState);

    this.set('privateMethod', () => _get('propValue'));
  }

  showProp() {
    return this.get('privateMethod')();
  }
}

let one = new Test({ propValue: 5});
let two = new Test({ propValue: 8});
two.showProp(); // 8
one.showProp(); // 5

请对此提出意见。

其他回答

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

答案是“不”。但是你可以像这样创建对属性的私有访问:

使用模块。模块中的所有内容都是私有的,除非使用export关键字将其设为公有。 在模块内部,使用函数闭包:http://www.kirupa.com/html5/closures_in_javascript.htm

(可以使用符号来确保隐私的建议在ES6规范的早期版本中是正确的,但现在不再是这样:https://mail.mozilla.org/pipermail/es-discuss/2014-January/035604.html和https://stackoverflow.com/a/22280202/1282216。有关符号和隐私的详细讨论,请参阅:https://curiosity-driven.org/private-properties-in-javascript)

这是一个干净简单的“类”解决方案,具有私有和公共接口,并支持组合

我有一个解决办法,它很简单…虽然性能不是问题…但它确实有效,而且效果很好。

诀窍在于,在私有属性和函数被建立和标准化/采用之前,需要另一种解决方案,这是另一种解决方案…

class ClassPrivateProperties {
    constructor(instance) {
        const $this = instance;
        let properties = {};
        this.prop = (key, value = undefined) => {
            if (!value) {
                return properties[key];
            } else {
                properties[key] = value;
            }
        };
        this.clear = instance => {
            if ($this === instance) {
                properties = {};
                return true;
            } else {
                return false;
            }
        }
    }
}

这是一个示例用法,可以是什么(如果你使用上面的感觉自由,使它更好)

class Test {
    constructor() {
        this._privateProps = new ClassPrivateProperties(this);
    }
    property(key, value = undefined) {
        if (!value) {
            return this._privateProps.prop(key);
        } else {
            this._privateProps.prop(key, value);
        }
    }
    clear() { return this._privateProps.clear(this); }
}
const test = new test;
test.property('myKey','some value here');
console.log(test.property('myKey'));

就像我提到的,这个问题不是最好的,但它工作,使属性真正的私有。

使用ES6模块(最初由@d13提出)对我来说效果很好。它不能完美地模拟私有属性,但至少您可以确信,应该是私有的属性不会泄漏到类之外。这里有一个例子:

something.js

let _message = null;
const _greet = name => {
  console.log('Hello ' + name);
};

export default class Something {
  constructor(message) {
    _message = message;
  }

  say() {
    console.log(_message);
    _greet('Bob');
  }
};

然后消费代码可以是这样的:

import Something from './something.js';

const something = new Something('Sunny day!');
something.say();
something._message; // undefined
something._greet(); // exception

更新(重要):

正如@DanyalAytekin在评论中概述的那样,这些私有属性是静态的,因此在全球范围内。它们在处理单例对象时工作得很好,但在处理瞬态对象时必须小心。扩展上面的例子:

import Something from './something.js';
import Something2 from './something.js';

const a = new Something('a');
a.say(); // a

const b = new Something('b');
b.say(); // b

const c = new Something2('c');
c.say(); // c

a.say(); // c
b.say(); // c
c.say(); // c