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

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

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

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

当前回答

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

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

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

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

其他回答

哦,这么多奇异的解决方案!我通常不关心隐私,所以我使用“伪隐私”,正如这里所说的。但如果确实关心(如果有一些特殊的要求),我会在这个例子中使用:

class jobImpl{
  // public
  constructor(name){
    this.name = name;
  }
  // public
  do(time){
    console.log(`${this.name} started at ${time}`);
    this.prepare();
    this.execute();
  }
  //public
  stop(time){
    this.finish();
    console.log(`${this.name} finished at ${time}`);
  }
  // private
  prepare(){ console.log('prepare..'); }
  // private
  execute(){ console.log('execute..'); }
  // private
  finish(){ console.log('finish..'); }
}

function Job(name){
  var impl = new jobImpl(name);
  return {
    do: time => impl.do(time),
    stop: time => impl.stop(time)
  };
}

// Test:
// create class "Job"
var j = new Job("Digging a ditch");
// call public members..
j.do("08:00am");
j.stop("06:00pm");

// try to call private members or fields..
console.log(j.name); // undefined
j.execute(); // error

函数(构造函数)的另一种可能实现:

function Job(name){
  var impl = new jobImpl(name);
  this.do = time => impl.do(time),
  this.stop = time => impl.stop(time)
}

是的-你可以创建封装的属性,但它没有与访问修饰符(公共|私有)至少不是与ES6。

下面是一个简单的例子,如何用ES6完成:

用类词创建类

2在构造函数内部使用let OR const保留字声明块作用域变量->因为它们是块作用域,所以不能从外部访问(封装)

为了允许对这些变量进行一些访问控制(setter |getter),你可以在它的构造函数中使用:this.methodName=function(){}语法声明实例方法

"use strict";
    class Something{
        constructor(){
            //private property
            let property="test";
            //private final (immutable) property
            const property2="test2";
            //public getter
            this.getProperty2=function(){
                return property2;
            }
            //public getter
            this.getProperty=function(){
                return property;
            }
            //public setter
            this.setProperty=function(prop){
                property=prop;
            }
        }
    }

现在让我们检查一下:

var s=new Something();
    console.log(typeof s.property);//undefined 
    s.setProperty("another");//set to encapsulated `property`
    console.log(s.getProperty());//get encapsulated `property` value
    console.log(s.getProperty2());//get encapsulated immutable `property2` value

在JS中获得真正隐私的唯一方法是通过作用域,因此不可能有一个属性是this的成员,只能在组件内部访问。在ES6中存储真正私有数据的最佳方法是使用WeakMap。

const privateProp1 = new WeakMap();
const privateProp2 = new WeakMap();

class SomeClass {
  constructor() {
    privateProp1.set(this, "I am Private1");
    privateProp2.set(this, "I am Private2");

    this.publicVar = "I am public";
    this.publicMethod = () => {
      console.log(privateProp1.get(this), privateProp2.get(this))
    };        
  }

  printPrivate() {
    console.log(privateProp1.get(this));
  }
}

显然,这可能是一个缓慢的,肯定是丑陋的,但它确实提供了隐私。

请记住,即使这样也不是完美的,因为Javascript是动态的。有人仍然可以这样做

var oldSet = WeakMap.prototype.set;
WeakMap.prototype.set = function(key, value){
    // Store 'this', 'key', and 'value'
    return oldSet.call(this, key, value);
};

为了在值被存储时捕获它们,所以如果你想格外小心的话,你需要捕获一个对.set和.get的本地引用来显式使用,而不是依赖于可覆盖的原型。

const {set: WMSet, get: WMGet} = WeakMap.prototype;

const privateProp1 = new WeakMap();
const privateProp2 = new WeakMap();

class SomeClass {
  constructor() {
    WMSet.call(privateProp1, this, "I am Private1");
    WMSet.call(privateProp2, this, "I am Private2");

    this.publicVar = "I am public";
    this.publicMethod = () => {
      console.log(WMGet.call(privateProp1, this), WMGet.call(privateProp2, this))
    };        
  }

  printPrivate() {
    console.log(WMGet.call(privateProp1, this));
  }
}

我找到了一个非常简单的解决方案,只需使用Object.freeze()。当然,问题是以后你不能向对象添加任何东西。

class Cat {
    constructor(name ,age) {
        this.name = name
        this.age = age
        Object.freeze(this)
    }
}

let cat = new Cat('Garfield', 5)
cat.age = 6 // doesn't work, even throws an error in strict mode

不同的“私人”方式

Instead of fighting against the fact that private visibility is currently unavailable in ES6, I decided to take a more practical approach that does just fine if your IDE supports JSDoc (e.g., Webstorm). The idea is to use the @private tag. As far as development goes, the IDE will prevent you from accessing any private member from outside its class. Works pretty well for me and it's been really useful for hiding internal methods so the auto-complete feature shows me just what the class really meant to expose. Here's an example: