【问题标题】:Access Modifiers (Private, Protected) in ES6ES6 中的访问修饰符(私有、受保护)
【发布时间】:2016-04-03 17:21:11
【问题描述】:

注意:我已经完成了以下关于Symbols、WeekMaps 和地图的 SO 问题和 7 个答案(截至目前),请在投票前阅读完整问题:Private properties in JavaScript ES6 classes
文章:https://esdiscuss.org/topic/es7-property-initializers

下面是我的Simple Class,其中包含私有、公共和受保护的属性和方法。

  'use strict';
  class MyClass {
    constructor () {
      this.publicVar = 'This is Public Variable';
      this.privateVar = 'This is Private Variable';
      this.protectedVar = 'This is Protected Variable';
    } // Public Constructor Method.
    
    publicMethod () {
      console.log('   Accessing this.publicVar: ', this.publicVar);
      console.log('   Accessing this.privateVar: ', this.privateVar);
      console.log('   Accessing this.protectedVar: ', this.protectedVar);
      return 'Its Public Method'
    } // Public Method.

    privateMethod () {return 'Its Private Method'} // Private Method.
    protectedMethod () {return 'Its Protected Method'} // Protected Method.

    foo () {
      this.publicMethod();
      this.privateMethod();
      this.protectedMethod();
    } // Public Method
  } // end class

我正在实例化对象并调用按预期工作的公共方法。

let MyObject = new MyClass;
MyObject.foo(); // Works fine.
console.log( MyObject.publicVar ); // Works
console.log( MyObject.publicMethod() ); // Works

按预期工作。

现在我的问题。我知道 ES6 规范中很少有像 Symbol 这样的东西,目前在 ES6 类上获得受保护和私有变量/方法的解决方法是什么。

console.log( MyObject.privateVar ); // Works
console.log( MyObject.privateMethod() ); // Works

我希望这个属性和方法只在它自己的类中可见。

console.log( MyObject.protectedVar ); // Works
console.log( MyObject.protectedMethod() ); // Works

我希望这个属性和方法在它自己的类和扩展它的类中可见。

赞赏解决方法/实现此行为的更好解决方案

【问题讨论】:

  • 如果您已经阅读过 Symbols 和 WeakMap,您还需要了解什么?
  • 我已经完成了这些,但希望该示例也可以作为限制和扩展类范围的答案
  • ES6 类没有故意私有/受保护:see here WeakMap/Symbols 更像是一种将它们添加到 ES6 类中的解决方法,但 ES6 类更多地是一种定义方法的方式,而不是用 Java 和 C# 等语言复制“经典”OO 类

标签: ecmascript-6 access-modifiers


【解决方案1】:

私有财产

在 ES6(及之前)中,所有私有属性实现都依赖于closure

人们 have been doing it 甚至在 JavaScript 有版本之前。 WeakMap 只是一种变体,它消除了对每个新对象的新范围和新函数的需求,但以访问速度为代价。

Symbol 是一种 ES6 变体,它对常见操作(例如简单的属性访问或for in)隐藏属性。

var MyClass;
( () => {
  // Define a scoped symbol for private property A.
  const PropA = Symbol( 'A' );
  // Define the class once we have all symbols
  MyClass = class {
    someFunction () {
      return "I can read " + this[ PropA ]; // Access private property
    }
  }
  MyClass.prototype[ PropA ] = 'Private property or method';
})();

// function in the closure can access the private property.
var myObject = new MyClass();
alert( myObject.someFunction() );

// But we cannot "recreate" the Symbol externally.
alert( myObject[ Symbol( 'A' ) ] ); // undefined

// However if someone *really* must access it...
var symbols = Object.getOwnPropertySymbols( myObject.__proto__ );
alert( myObject[ symbols[ 0 ] ] );

如上所示,Object.getOwnPropertySymbols() 可以解决它。 尽管它存在,但我总是选择符号而不是 WeakMap。 代码更干净,更简单,gc 工作更少,而且(我认为)更高效。

我个人也避免使用classObject.create 要简单得多。但这超出了范围。


受保护的属性

受保护的属性,就其本质而言,需要执行函数知道调用代码的对象,判断是否应该被授予访问权限。

这在JS中是不可能的,不是因为ES6有no real class,而是因为调用者contextsimply unavailable

由于 JavaScript 的 various special natures,在可预见的未来,受保护的属性将仍然不可能。

[更新] 三年后,由于模块的广泛支持,可以模拟受保护属性的大多数好处,请参阅下面 Twifty 的答案。 它们仍然是公开的,但您需要额外访问它们,这意味着很难意外访问或覆盖它们。 [/更新]

或者...


包属性

某些语言具有半保护属性,有时称为“包私有”,其中方法/属性可供同一模块/包中的成员访问。

ES6 可以通过闭包来实现它。 它与上面的私有属性代码完全相同——只是与多个原型共享范围及其符号。

但这是不切实际的,因为这需要将整个模块定义在相同的封闭范围内,即在单个文件中。 但它仍然是一种选择。

【讨论】:

    【解决方案2】:

    我迟到了,但可以在 javascript 中模拟私有和受保护的方法。

    私有方法/属性

    使用众所周知的符号方法

    const someMethod = Symbol()
    const someProperty = Symbol()
    
    export default class Parent {
      constructor () {
        this[someProperty] = 'and a private property'
      }
    
      [someMethod] () {
        console.log('this is a private method')
        console.log(this[someProperty])
      }
    
      callPrivateMethod () {
        this[someMethod]()
      }
    }
    

    受保护的方法/属性

    就其性质而言,受保护的成员对派生类是可见的。他们还必须模仿super.method 模式。

    symbols.js

    export default {
       protectedMethod: Symbol()
    }
    

    父.js

    import symbols from './symbols'
    
    const someMethod = Symbol()
    const someProperty = Symbol()
    
    export default class Parent {
      constructor () {
        this[someProperty] = 'and a private property'
      }
    
      [someMethod] () {
        console.log('this is a private method')
        console.log(this[someProperty])
      }
    
      [symbols.protectedMethod] () {
        console.log('I am the parent')
      }
    
      callPrivateMethod () {
        this[someMethod]()
      }
    }
    

    child.js

    import Parent from './parent'
    import symbols from './symbols'
    
    export default class Child {
      [symbols.protectedMethod] () {
        console.log('I am the child')
        super[symbols.protectedMethod]()
      }
    
      callProtectedMethod () {
        this[symbols.protectedMethod]()
      }
    }
    

    【讨论】:

    • 这是不受保护的,这是公开的,因为每个人都可以访问“符号”,还是我错过了什么?
    • @BenjaminVanRyseghem 要访问您需要使用在 parent.js 中创建的原始符号的字段,您不能只创建另一个具有相同名称的符号并期望它也提供访问权限。这是模拟保护,因为每种语言都有绕过的方法。这里使用符号的目的是防止在对象上意外使用相同的字段名称。
    • >要访问您需要使用在 parent.js 中创建的原始符号的字段,您也可以import symbols from './symbols'
    • @BenjaminVanRyseghem,正是我的观点。如果您的代码打算写入受保护/私有字段,那么您必须导入符号。假设您发布了一个模块“字符串”,其中包含一个名为“_sum”的预期私有字段,任何用户都可以扩展或添加一个名为“_sum”的冲突字段。他们必须调查源头才能找到错误。使用符号可以防止这种情况。保护私有/受保护成员免受意外写入。这不是为了向用户隐藏字段,它只不过是一个安全网。没有任何语言可以隐藏这些成员。
    【解决方案3】:

    还可以使用符号作为“键”,通过看门人和条件行为启用具有受保护继承的类版本。

    例如,在此 BST 中,root 是私有的,add/insert 返回 true。不希望任何直接节点访问。

    但是,AVL 子类可以同时使用这两者,也可以使用一些实用函数。

    该键允许将类的自以为是的表达作为独立的或基础的。甚至可以使用装饰器或受保护的访问映射(而不是通过看门人吸气剂)完成 DRY-er。

    class BinarySearchTree {
      #root;
    
      constructor(sym) {
        this.#root = null;
        this.#access = sym;
      }
    
      #protectionCheck = () =>
        this.#access === PROTECTED ||
        (() => {
          throw new Error("Ah, ah, ah. You didn't say the magic word.");
        })();
    
      __root() {
        this.#protectionCheck();
        return this.#root;
      }
    
      add(val) {
        ...
        return this.#access === PROTECTED ? node : true;
      }
    }
    
    class AVL extends BinarySearchTree {
      constructor() {
        super(PROTECTED);
      }
    
      get #root() {
        return super.__root();
      }
    
      add = (value) => {
        const node = super.add(value);
        this.#balanceUpstream(node);
        return true;
      };
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-05-14
      • 2013-03-05
      • 1970-01-01
      • 2013-04-03
      • 2019-05-07
      相关资源
      最近更新 更多