代码之家  ›  专栏  ›  技术社区  ›  Michael Warner

js类中的只读属性,但仍可以在类中修改[重复]

  •  0
  • Michael Warner  · 技术社区  · 5 年前

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

    这里有一个例子。 我怎样才能阻止 instance.property ?

    class Something {
      constructor(){
        this.property = "test";
      }
    }
    
    var instance = new Something();
    console.log(instance.property); //=> "test"
    
    0 回复  |  直到 9 年前
        1
  •  117
  •   Alister    5 年前

    私有字段正在 ECMA standard . 你可以从今天开始使用 babel 7 和第三阶段预设。见 babel REPL example .

    class Something {
      #property;
    
      constructor(){
        this.#property = "test";
      }
    }
    
    const instance = new Something();
    console.log(instance.property); //=> undefined
    
        2
  •  250
  •   kmiklas    6 年前

    简而言之,不,es6类不支持私有属性。

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

    小精灵

    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; }
    }
    
        3
  •  177
  •   twhb    7 年前

    关于@loganfsmyth的回答:

    javascript中唯一真正私有的数据仍然是作用域变量。不能以与公共属性相同的方式在内部访问属性的方式拥有私有属性,但可以使用范围变量存储私有数据。

    作用域变量

    这里的方法是使用构造函数的作用域(私有)来存储私有数据。要使方法能够访问这个私有数据,它们也必须在构造函数中创建,这意味着您要用每个实例重新创建它们。这是一个性能和内存的惩罚,但有些人认为这个惩罚是可以接受的。对于不需要访问私有数据的方法,可以像往常一样将它们添加到原型中,从而避免惩罚。

    例子:

    function Person(name) {
      let age = 20; // this is private
      this.name = name; // this is public
    
      this.greet = function () {
        // here we can access both name and age
        console.log(`name: ${this.name}, age: ${age}`);
      };
    }
    
    let joe = new Person('Joe');
    joe.greet();
    
    // here we can access name but not age
    

    瞄准镜织布工

    weakmap可以用来避免前面方法的性能和内存损失。weakmap将数据与对象(这里是实例)关联起来,这样只能使用该weakmap访问数据。因此,我们使用作用域变量方法创建一个私有weakmap,然后使用该weakmap检索与 this . 这比scoped variables方法更快,因为所有实例都可以共享一个weakmap,所以不需要重新创建方法就可以让它们访问自己的weakmap。

    例子:

    let Person = (function () {
      let privateProps = new WeakMap();
    
      class Person {
        constructor(name) {
          this.name = name; // this is public
          privateProps.set(this, {age: 20}); // this is private
        }
    
        greet() {
          // Here we can access both name and age
          console.log(`name: ${this.name}, age: ${privateProps.get(this).age}`);
        }
      }
    
      return Person;
    })();
    
    let joe = new Person('Joe');
    joe.greet();
    
    // here we can access joe's name but not age
    

    本例使用一个对象将一个编织器用于多个私有属性;您也可以使用多个编织器并像 age.set(this, 20) ,或者编写一个小包装并以另一种方式使用它,比如 privateProps.set(this, 'age', 0) .

    从理论上讲,这种方法的隐私可能会被篡改全球 WeakMap 对象。也就是说,所有的javascript都可能被损坏的全局变量破坏。我们的代码已经建立在这样的假设之上了。

    (这种方法也可以用 Map 但是 弱映射 更好是因为 地图 除非你非常小心,否则会造成内存泄漏,而这两种情况在其他方面并无不同。)

    半答案:范围符号

    符号是一种可以用作属性名的基元值类型。可以使用作用域变量方法创建私有符号,然后将私有数据存储在 this[mySymbol] .

    此方法的隐私可能会被使用 Object.getOwnPropertySymbols ,但有点尴尬。

    例子:

    let Person = (function () {
      let ageKey = Symbol();
    
      class Person {
        constructor(name) {
          this.name = name; // this is public
          this[ageKey] = 20; // this is intended to be private
        }
    
        greet() {
          // Here we can access both name and age
          console.log(`name: ${this.name}, age: ${this[ageKey]}`);
        }
      }
    
      return Person;
    })();
    
    let joe = new Person('Joe');
    joe.greet();
    
    // Here we can access joe's name and, with a little effort, age. ageKey is
    // not in scope, but we can obtain it by listing all Symbol properties on
    // joe with `Object.getOwnPropertySymbols(joe)`.
    

    半答案:下划线

    旧的默认设置是,只使用带有下划线前缀的公共属性。尽管在任何方面都不是私有属性,但这种约定非常流行,它可以很好地进行通信,读者应该将该属性视为私有属性,这通常可以完成任务。作为这个失误的交换,我们得到了一个更容易阅读,更容易打字,更快的方法。

    例子:

    class Person {
      constructor(name) {
        this.name = name; // this is public
        this._age = 20; // this is intended to be private
      }
    
      greet() {
        // Here we can access both name and age
        console.log(`name: ${this.name}, age: ${this._age}`);
      }
    }
    
    let joe = new Person('Joe');
    joe.greet();
    
    // Here we can access both joe's name and age. But we know we aren't
    // supposed to access his age, which just might stop us.
    

    结论

    截至2017年年底,私人房产仍然没有完美的方式。各种方法各有利弊。作用域变量是真正的私有变量;作用域weakmaps是非常私有的,比作用域变量更实用;作用域符号是相当私有的,也相当实用;下划线通常足够私有,也非常实用。

        4
  •  114
  •   Maor Refaeli    6 年前

    更新:A proposal with nicer syntax 正在路上。欢迎捐款。


    是的,对象中有-用于作用域访问- ES6 introduces Symbol s .

    符号是唯一的,除了反射之外,你不能从外部获得一个(如爪哇/ C中的私人),但是任何有权访问内部符号的人都可以使用它来进行密钥访问:

    var property = Symbol();
    class Something {
        constructor(){
            this[property] = "test";
        }
    }
    
    var instance = new Something();
    
    console.log(instance.property); //=> undefined, can only access with access to the Symbol
    
        5
  •  30
  •   Community ƒernando Valle    7 年前

    答案是“不”。但是,您可以创建对属性的私有访问,如下所示:

    (在早期版本的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 )

        6
  •  27
  •   loganfsmyth    9 年前

    在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));
      }
    }
    
        7
  •  22
  •   Community ƒernando Valle    7 年前

    为了以后供其他旁观者参考,我听说现在的建议是 WeakMaps 保存私人数据。

    下面是一个更清楚的工作示例:

    function storePrivateProperties(a, b, c, d) {
      let privateData = new WeakMap;
      // unique object as key, weak map can only accept object as key, when key is no longer referened, garbage collector claims the key-value 
      let keyA = {}, keyB = {}, keyC = {}, keyD = {};
    
      privateData.set(keyA, a);
      privateData.set(keyB, b);
      privateData.set(keyC, c);
      privateData.set(keyD, d);
    
      return {
        logPrivateKey(key) {
          switch(key) {
          case "a":
            console.log(privateData.get(keyA));
            break;
          case "b":
            console.log(privateData.get(keyB));
            break;
          case "c":
            console.log(privateData.get(keyC));
            break;
          case "d":
            console.log(privateData.set(keyD));
            break;
          default:
            console.log(`There is no value for ${key}`)
          }
        }
      }
    }
    
        8
  •  12
  •   Bergi    10 年前

    取决于 whom you ask -)

    private 属性修饰符包含在 Maximally minimal classes proposal 似乎已经进入了 current draft .

    但是,可能有 support for private names ,这确实允许私有属性,而且它们可能也可以用于类定义中。

        9
  •  9
  •   efidiles    8 年前

    完成@d13和@johnny oshika和@danyalaytekin的评论:

    我想在@johnny oshika提供的示例中,我们可以使用普通函数而不是箭头函数,然后 .bind 当前对象加上 _privates 对象作为curried参数:

    JS

    function _greet(_privates) {
      return 'Hello ' + _privates.message;
    }
    
    function _updateMessage(_privates, newMessage) {
      _privates.message = newMessage;
    }
    
    export default class Something {
      constructor(message) {
        const _privates = {
          message
        };
    
        this.say = _greet.bind(this, _privates);
        this.updateMessage = _updateMessage.bind(this, _privates);
      }
    }
    

    MIN JS

    import Something from './something.js';
    
    const something = new Something('Sunny day!');
    
    const message1 = something.say();
    something.updateMessage('Cloudy day!');
    const message2 = something.say();
    
    console.log(message1 === 'Hello Sunny day!');  // true
    console.log(message2 === 'Hello Cloudy day!');  // true
    
    // the followings are not public
    console.log(something._greet === undefined);  // true
    console.log(something._privates === undefined);  // true
    console.log(something._updateMessage === undefined);  // true
    
    // another instance which doesn't share the _privates
    const something2 = new Something('another Sunny day!');
    
    const message3 = something2.say();
    
    console.log(message3 === 'Hello another Sunny day!'); // true
    

    我能想到的好处是:

    • 我们可以有私人方法( _greet _updateMessage 像私人方法一样行事,只要我们不 export 参考文献)
    • 尽管它们不在原型上,但上述方法将节省内存,因为实例在类之外创建一次(而不是在构造函数中定义它们)
    • 因为我们在一个模块里,所以不会泄露任何全局信息
    • 我们也可以使用绑定的 私立者 对象

    我可以想到一些缺点:

    可以在以下位置找到正在运行的代码段: http://www.webpackbin.com/NJgI5J8lZ

        10
  •  9
  •   Johnny Oshika    6 年前

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

    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
    
        11
  •  8
  •   Nikita Kurtin    9 年前

    是-可以创建封装的属性 ,但它没有使用访问修饰符(public private)完成,至少没有使用es6。

    下面是一个简单的使用ES6的示例:

    1使用创建类 class 单词

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

    3若要允许对这些变量进行某些访问控制(setters 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
    
        12
  •  7
  •   Lucio Paiva    7 年前

    对“私人”的不同看法

    我决定采用一种更实用的方法,如果您的ide支持jsdoc(例如webtorm),那么这种方法就可以很好地解决es6中目前无法提供私有可见性的问题。我们的想法是使用 @private tag . 在开发过程中,ide将阻止您从类之外访问任何私有成员。对我来说工作得很好,它对于隐藏内部方法非常有用,因此auto-complete特性向我展示了类真正要公开的内容。下面是一个例子:

    auto-complete showing just public stuff

        13
  •  6
  •   kevlened    7 年前

    弱映射

    • IE11支持(符号不支持)
    • 硬私有(使用符号的道具是软私有的,因为 Object.getOwnPropertySymbols )
    • 看起来很干净(不像闭包需要构造函数中的所有道具和方法)

    首先,定义一个函数来包装weakmap:

    function Private() {
      const map = new WeakMap();
      return obj => {
        let props = map.get(obj);
        if (!props) {
          props = {};
          map.set(obj, props);
        }
        return props;
      };
    }
    

    然后,在类之外构造一个引用:

    const p = new Private();
    
    class Person {
      constructor(name, age) {
        this.name = name;
        p(this).age = age; // it's easy to set a private variable
      }
    
      getAge() {
        return p(this).age; // and get a private variable
      }
    }
    

    注: IE11不支持,但在示例中看起来更清晰。

        14
  •  4
  •   JSInitiate    8 年前

    我相信在构造函数中使用闭包可以“两全其美”。有两种变体:

    所有数据成员都是私有的

    function myFunc() {
       console.log('Value of x: ' + this.x);
       this.myPrivateFunc();
    }
    
    function myPrivateFunc() {
       console.log('Enhanced value of x: ' + (this.x + 1));
    }
    
    class Test {
       constructor() {
    
          let internal = {
             x : 2,
          };
          
          internal.myPrivateFunc = myPrivateFunc.bind(internal);
          
          this.myFunc = myFunc.bind(internal);
       }
    };

    有些成员是私人的

    注:这是公认的丑陋。如果您知道更好的解决方案,请编辑此答复。

    function myFunc(priv, pub) {
       pub.y = 3; // The Test object now gets a member 'y' with value 3.
       console.log('Value of x: ' + priv.x);
       this.myPrivateFunc();
    }
    
    function myPrivateFunc() {
       pub.z = 5; // The Test object now gets a member 'z' with value 3.
       console.log('Enhanced value of x: ' + (priv.x + 1));
    }
    
    class Test {
       constructor() {
          
          let self = this;
    
          let internal = {
             x : 2,
          };
          
          internal.myPrivateFunc = myPrivateFunc.bind(null, internal, self);
          
          this.myFunc = myFunc.bind(null, internal, self);
       }
    };
        15
  •  4
  •   Francisco Neto    7 年前

    事实上,使用符号和代理是可能的。在类作用域中使用符号并在代理中设置两个陷阱:一个用于类原型,以便reflect.ownkeys(实例)或object.getownpropertysymbols不会泄漏符号,另一个用于构造函数本身,因此当 new ClassName(attrs) 调用时,将截获返回的实例,并阻止自己的属性符号。 代码如下:

    const Human = (function() {
      const pet = Symbol();
      const greet = Symbol();
    
      const Human = privatizeSymbolsInFn(function(name) {
        this.name = name; // public
        this[pet] = 'dog'; // private 
      });
    
      Human.prototype = privatizeSymbolsInObj({
        [greet]() { // private
          return 'Hi there!';
        },
        revealSecrets() {
          console.log(this[greet]() + ` The pet is a ${this[pet]}`);
        }
      });
    
      return Human;
    })();
    
    const bob = new Human('Bob');
    
    console.assert(bob instanceof Human);
    console.assert(Reflect.ownKeys(bob).length === 1) // only ['name']
    console.assert(Reflect.ownKeys(Human.prototype).length === 1 ) // only ['revealSecrets']
    
    
    // Setting up the traps inside proxies:
    function privatizeSymbolsInObj(target) { 
      return new Proxy(target, { ownKeys: Object.getOwnPropertyNames });
    }
    
    function privatizeSymbolsInFn(Class) {
      function construct(TargetClass, argsList) {
        const instance = new TargetClass(...argsList);
        return privatizeSymbolsInObj(instance);
      }
      return new Proxy(Class, { construct });
    }

    Reflect.ownKeys() 工作方式如下: Object.getOwnPropertyNames(myObj).concat(Object.getOwnPropertySymbols(myObj)) 所以我们需要一个陷阱。

        16
  •  4
  •   Robin F.    7 年前

    就我个人而言,我喜欢 bind operator :: 然后将它与前面提到的解决方案@d13结合起来,但是现在在使用 export 关键字,并将私有函数放入模块中。

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

    私家侦探

    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
    

    如有意见,将不胜感激。

        17
  •  4
  •   NanoWizard    7 年前

    我想 Benjamin's answer 在语言本机支持显式私有变量之前,可能是大多数情况下的最佳选择。

    但是,如果出于某种原因,您需要阻止 Object.getOwnPropertySymbols() ,我考虑使用的方法是附加一个唯一的、不可配置的、不可枚举的、不可写的属性,该属性可以用作构造时每个对象的属性标识符(例如 Symbol ,如果您还没有其他独特的属性,如 id )然后使用该标识符保存每个对象的“私有”变量的映射。

    const privateVars = {};
    
    class Something {
        constructor(){
            Object.defineProperty(this, '_sym', {
                configurable: false,
                enumerable: false,
                writable: false,
                value: Symbol()
            });
    
            var myPrivateVars = {
                privateProperty: "I'm hidden"
            };
    
            privateVars[this._sym] = myPrivateVars;
    
            this.property = "I'm public";
        }
    
        getPrivateProperty() {
            return privateVars[this._sym].privateProperty;
        }
    
        // A clean up method of some kind is necessary since the
        // variables won't be cleaned up from memory automatically
        // when the object is garbage collected
        destroy() {
            delete privateVars[this._sym];
        }
    }
    
    var instance = new Something();
    console.log(instance.property); //=> "I'm public"
    console.log(instance.privateProperty); //=> undefined
    console.log(instance.getPrivateProperty()); //=> "I'm hidden"
    

    与使用 WeakMap faster access time 如果性能成为一个问题。

        18
  •  4
  •   MarkM    7 年前

    我是在寻找“班级专用数据”的最佳实践时发现这篇文章的。有人提到一些模式会有性能问题。

    我根据在线书籍“Exploring ES6”中的4种主要模式整理了几个JSPerf测试:

    http://exploringjs.com/es6/ch_classes.html#sec_private-data-for-classes

    测试可在以下位置找到:

    https://jsperf.com/private-data-for-classes

    在chrome 63.0.3239/macosx10.11.6中,性能最好的模式是“通过构造函数环境的私有数据”和“通过命名约定的私有数据”。对我来说,Safari在Weakmap的表现很好,但Chrome的表现不太好。

    我不知道对内存的影响,但是“构造函数环境”的模式(有些人警告说这是一个性能问题)的性能非常好。

    四种基本模式是:

    通过构造函数环境的私有数据

    class Countdown {
        constructor(counter, action) {
            Object.assign(this, {
                dec() {
                    if (counter < 1) return;
                    counter--;
                    if (counter === 0) {
                        action();
                    }
                }
            });
        }
    }
    const c = new Countdown(2, () => {});
    c.dec();
    c.dec();
    

    通过构造函数环境的私有数据2

    class Countdown {
        constructor(counter, action) {
            this.dec = function dec() {
                if (counter < 1) return;
                counter--;
                if (counter === 0) {
                    action();
                }
            }
        }
    }
    const c = new Countdown(2, () => {});
    c.dec();
    c.dec();
    

    通过命名约定的私有数据

    class Countdown {
        constructor(counter, action) {
            this._counter = counter;
            this._action = action;
        }
        dec() {
            if (this._counter < 1) return;
            this._counter--;
            if (this._counter === 0) {
                this._action();
            }
        }
    }
    const c = new Countdown(2, () => {});
    c.dec();
    c.dec();
    

    通过weakmaps的私有数据

    const _counter = new WeakMap();
    const _action = new WeakMap();
    class Countdown {
        constructor(counter, action) {
            _counter.set(this, counter);
            _action.set(this, action);
        }
        dec() {
            let counter = _counter.get(this);
            if (counter < 1) return;
            counter--;
            _counter.set(this, counter);
            if (counter === 0) {
                _action.get(this)();
            }
        }
    }
    const c = new Countdown(2, () => {});
    c.dec();
    c.dec();
    

    通过符号的私有数据

    const _counter = Symbol('counter');
    const _action = Symbol('action');
    
    class Countdown {
        constructor(counter, action) {
            this[_counter] = counter;
            this[_action] = action;
        }
        dec() {
            if (this[_counter] < 1) return;
            this[_counter]--;
            if (this[_counter] === 0) {
                this[_action]();
            }
        }
    }
    const c = new Countdown(2, () => {});
    c.dec();
    c.dec();
    
        19
  •  3
  •   Ilya Zarembsky    8 年前
    class Something {
      constructor(){
        var _property = "test";
        Object.defineProperty(this, "property", {
            get: function(){ return _property}
        });
      }
    }
    
    var instance = new Something();
    console.log(instance.property); //=> "test"
    instance.property = "can read from outside, but can't write";
    console.log(instance.property); //=> "test"
    
        20
  •  3
  •   Michael F    7 年前

    连打字本都做不到。从他们 documentation :

    当成员标记为private时,不能从其包含类的外部访问它。例如:

    class Animal {
        private name: string;
        constructor(theName: string) { this.name = theName; }
    }
    
    new Animal("Cat").name; // Error: 'name' is private;
    

    但是他们的 playground 这给出:

    var Animal = (function () {
        function Animal(theName) {
            this.name = theName;
        }
        return Animal;
    }());
    console.log(new Animal("Cat").name);
    

    所以他们的“私有”关键字是无效的。

        21
  •  3
  •   Paul Whipp    7 年前

    很晚才来参加派对,但我在搜索时碰到了操作问题,所以… 是的,可以通过在闭包中包装类声明来拥有私有属性

    有一个例子说明了我在 this codepen . 在下面的代码片段中,subscribable类有两个“private”函数 process processCallbacks . 任何属性都可以以这种方式添加,并且通过使用闭包将它们保持为私有。如果关注点被很好地分离,并且javascript不需要在闭包灵活地完成工作时通过添加更多语法而变得臃肿,那么imo隐私是一个罕见的需求。

    const Subscribable = (function(){
    
      const process = (self, eventName, args) => {
        self.processing.set(eventName, setTimeout(() => processCallbacks(self, eventName, args)))};
    
      const processCallbacks = (self, eventName, args) => {
        if (self.callingBack.get(eventName).length > 0){
          const [nextCallback, ...callingBack] = self.callingBack.get(eventName);
          self.callingBack.set(eventName, callingBack);
          process(self, eventName, args);
          nextCallback(...args)}
        else {
          delete self.processing.delete(eventName)}};
    
      return class {
        constructor(){
          this.callingBack = new Map();
          this.processing = new Map();
          this.toCallbacks = new Map()}
    
        subscribe(eventName, callback){
          const callbacks = this.unsubscribe(eventName, callback);
          this.toCallbacks.set(eventName,  [...callbacks, callback]);
          return () => this.unsubscribe(eventName, callback)}  // callable to unsubscribe for convenience
    
        unsubscribe(eventName, callback){
          let callbacks = this.toCallbacks.get(eventName) || [];
          callbacks = callbacks.filter(subscribedCallback => subscribedCallback !== callback);
          if (callbacks.length > 0) {
            this.toCallbacks.set(eventName, callbacks)}
          else {
            this.toCallbacks.delete(eventName)}
          return callbacks}
    
        emit(eventName, ...args){
          this.callingBack.set(eventName, this.toCallbacks.get(eventName) || []);
          if (!this.processing.has(eventName)){
            process(this, eventName, args)}}}})();
    

    我喜欢这种方法,因为它能很好地分离关注点,并将事情真正保密。唯一的缺点是需要使用“self”(或类似的东西)在私有内容中引用“this”。

        22
  •  3
  •   Serg    6 年前

    哦,这么多奇异的解决方案!我通常不关心隐私,所以我用 “伪隐私” 正如它的 said here . 但如果真的在意(如果有一些特殊要求),我会在这个例子中使用类似的东西:

    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
    

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

    function Job(name){
      var impl = new jobImpl(name);
      this.do = time => impl.do(time),
      this.stop = time => impl.stop(time)
    }
    
        23
  •  2
  •   kofifus    7 年前

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

        24
  •  2
  •   1-14x0r super-xp    7 年前

    是的,完全可以,而且很容易。这是通过在构造函数中返回原型对象图来公开私有变量和函数来完成的。这不是什么新鲜事,但需要一点js foo来理解它的优雅。这种方式不使用全局作用域或编织器。它是语言中的一种反映形式。取决于如何利用它;可以强制异常中断调用堆栈,也可以将异常作为 undefined . 这是下面的演示,可以阅读更多关于这些功能的信息 here

    class Clazz {
      constructor() {
        var _level = 1
    
        function _private(x) {
          return _level * x;
        }
        return {
          level: _level,
          public: this.private,
          public2: function(x) {
            return _private(x);
          },
          public3: function(x) {
            return _private(x) * this.public(x);
          },
        };
      }
    
      private(x) {
        return x * x;
      }
    }
    
    var clazz = new Clazz();
    
    console.log(clazz._level); //undefined
    console.log(clazz._private); // undefined
    console.log(clazz.level); // 1
    console.log(clazz.public(1)); //2
    console.log(clazz.public2(2)); //2
    console.log(clazz.public3(3)); //27
    console.log(clazz.private(0)); //error
        25
  •  2
  •   Nikola Andreev    6 年前

    我找到了一个非常简单的解决方案 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
    
        26
  •  2
  •   Yami Teru    6 年前

    我用这个模式,它一直对我有效

    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
        27
  •  2
  •   Paweł    6 年前

    实际上它 可能的。
    1。首先,创建类并在构造函数中返回 _public 功能。
    2。在被叫 公众 函数传递 this 参考 (访问所有私有方法和道具) ,以及来自 constructor (这将被传递 new Names() )
    三.在 公众 功能范围还有 Names 类的访问权限 (u此)私人 姓名

    class Names {
      constructor() {
        this.privateProperty = 'John';
        return _public(this, arguments);
      }
      privateMethod() { }
    }
    
    const names = new Names(1,2,3);
    console.log(names.somePublicMethod); //[Function]
    console.log(names.publicProperty); //'Jasmine'
    console.log(names.privateMethod); //undefined
    console.log(names.privateProperty); //undefind
    
    function _public(_this, _arguments) {
      class Names {
        constructor() {
          this.publicProperty = 'Jasmine';
          _this.privateProperty; //"John";
          _this.privateMethod; //[Function]
        }
    
        somePublicMethod() {
          _this.privateProperty; //"John";
          _this.privateMethod; //[Function]
        }
    
      }
      return new Names(..._arguments);
    }
    
        28
  •  2
  •   João Henrique    6 年前

    你可以试试这个 https://www.npmjs.com/package/private-members

    此包将按实例保存成员。

    const pvt = require('private-members');
    const _ = pvt();
    
    let Exemplo = (function () {    
        function Exemplo() {
            _(this).msg = "Minha Mensagem";
        }
    
        _().mensagem = function() {
            return _(this).msg;
        }
    
        Exemplo.prototype.showMsg = function () {
            let msg = _(this).mensagem();
            console.log(msg);
        };
    
        return Exemplo;
    })();
    
    module.exports = Exemplo;
    
        29
  •  1
  •   guitarino    7 年前

    大多数答案要么说这是不可能的,要么要求你使用一个编织工或符号,这是ES6的功能,可能需要多填充。不过,还有别的办法!看看这个:

    // 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

    我叫这个方法 存取器模式 . 基本的想法是我们有一个 关闭 A 钥匙 在闭包中,我们创建一个 私有对象 (在构造函数中)只有在 钥匙 .

    如果你感兴趣,你可以在 my article . 使用此方法,可以创建在闭包外部无法访问的每对象属性。因此,您可以在构造函数或原型中使用它们,但不能在其他任何地方使用它们。我没见过这种方法在任何地方使用,但我认为它非常强大。

        30
  •  1
  •   Jayesbe    7 年前

    另一种类似于最后两篇文章的方式

    class Example {
      constructor(foo) {
    
        // privates
        const self = this;
        this.foo = foo;
    
        // public interface
        return self.public;
      }
    
      public = {
        // empty data
        nodata: { data: [] },
        // noop
        noop: () => {},
      }
    
      // everything else private
      bar = 10
    }
    
    const test = new Example('FOO');
    console.log(test.foo); // undefined
    console.log(test.noop); // { data: [] }
    console.log(test.bar); // undefined