如何使用 Traceur 在 ES6 类中实现私有方法 [重复]

Posted

技术标签:

【中文标题】如何使用 Traceur 在 ES6 类中实现私有方法 [重复]【英文标题】:How to implement private method in ES6 class with Traceur [duplicate] 【发布时间】:2015-03-07 02:17:14 【问题描述】:

我现在使用 Traceur Compiler 来获得 ES6 功能的优势。

我想从 ES5 中实现这些东西:

function Animal() 
    var self = this,
        sayHi;

    sayHi  = function() 
        self.hi();
    ;

    this.hi = function() /* ... */

目前 traceur 不支持 privatepublic 关键字 (from harmony)。并且 ES6 类语法不允许在类主体中使用简单的var(或let)语句。

我找到的唯一方法是在类声明之前模拟私有。比如:

var sayHi = function() 
    // ... do stuff
;

class Animal 
...

总比没有好,但正如预期的那样,如果没有 apply-ing 或 bind-ing 每次都不能将正确的 this 传递给私有方法。

那么,有没有可能在兼容 Traceur 编译器的 ES6 类中使用私有数据?

【问题讨论】:

你考虑过6to5吗?我更喜欢它而不是跟踪器。我没有用过这个特别的东西,但是check out this snippet @Sampsa 这是一个很好的工具,但我在你的 sn-p 中找不到任何关于双冒号(::) 语法的信息。是来自规范还是草案? 实际上,这个问题并不完全重复,因为这个问题是关于私有方法的,而引用的问题是关于私有属性/字段的。 现在有一个私钥是#。见:github.com/tc39/proposal-class-fields 【参考方案1】:

当前ECMAScript 6 specification 中没有privatepublicprotected 关键字。

所以 Traceur 不支持privatepublic。 6to5(目前称为“Babel”)实现this proposal 用于实验目的(另请参阅this discussion)。但毕竟只是提议。

所以现在你可以通过WeakMap 模拟私有属性(参见here)。另一种选择是Symbol - 但它不提供真正的隐私,因为可以通过Object.getOwnPropertySymbols 轻松访问该属性。

恕我直言,此时最好的解决方案是使用伪隐私。如果你经常在你的方法中使用applycall,那么这个方法是非常特定于对象的。所以值得在你的类中用下划线前缀声明它:

class Animal 

    _sayHi() 
        // do stuff
    

【讨论】:

真的,许多语言中的字段隐私伪隐私。以 Java 为例——您始终可以使用反射获取对象的“私有”字段的值。从这个意义上说,隐私实际上只是一种设计模式——它为消费者提供了一个易于理解、工具/编译器可执行的接口,用于指示不应调用的方法。前面带有下划线的方法是这样一个接口的一个完全合理的实现,即使它只是一个约定而不是语言规范。 对于任何类型的函数迭代都不是很好,特别是如果人们选择不同的方式来标记哪些函数是私有的。 _idid_p_idprivateIds_id ....函数迭代示例,Promise.promisifyAll(SomeClass.prototype) "不要使用 _ underbar 作为名称的第一个或最后一个字符。它有时旨在表示隐私,但它实际上并不提供隐私。如果隐私是重要的是,使用闭包。避免表现出缺乏能力的约定。 Code Conventions for the javascript Programming Language - 道格拉斯·克罗克福德 @CarlosAraya 这是在 ES6 之前编写的。他基本上是在说“不要使用通用约定来编写类,因为类在 JS 中不是一个东西。”除了现在它们是一个东西,所以现在使用它们的约定是有意义的。 这是大家都讨厌的匈牙利符号吗?【参考方案2】:

你总是可以使用普通函数:

function myPrivateFunction() 
  console.log("My property: " + this.prop);


class MyClass() 
  constructor() 
    this.prop = "myProp";
    myPrivateFunction.bind(this)();
  


new MyClass(); // 'My property: myProp'

【讨论】:

Lambdas 自动绑定this。因为他们根本没有this(即你不能绑定一个lambda)。来源:blog.getify.com/arrow-this MDN 将 this 称为“词法绑定到 this”。见:developer.mozilla.org/en/docs/Web/JavaScript/Reference/… 第二种方法的一个缺点是 myPrivateFunction 是一个实例函数。它不在原型链上,所以如果你要实例化它的倍数,你就会膨胀 MyClass() 。但好点,它似乎确实有效。 您的第二个代码示例不支持多个实例; myPrivateFunction 只有一个实例。 不工作的例子。虽然可以为 'myPrivateFunction.call(this)' 工作【参考方案3】:

正如 alexpods 所说,在 ES6 中没有专门的方法可以做到这一点。但是,对于那些感兴趣的人,还有一个关于bind operator 的提案,它支持这种语法:

function privateMethod() 
  return `Hello $this.name`;


export class Animal 
  constructor(name) 
    this.name = name;
  
  publicMethod() 
    this::privateMethod();
  

再一次,这只是一个建议。您的里程可能会有所不同。

【讨论】:

【参考方案4】:

虽然目前无法将方法或属性声明为私有,但ES6 模块不在全局命名空间中。因此,您在模块中声明但未导出的任何内容都将不可用于程序的任何其他部分,但在运行时仍可用于您的模块。因此,您拥有私有属性和方法:)

这是一个例子 (在test.js 文件中)

function tryMe1(a) 
  console.log(a + 2);


var tryMe2 = 1234;

class myModule 
  tryMe3(a) 
    console.log(a + 100);
  

  getTryMe1(a) 
    tryMe1(a);
  

  getTryMe2() 
    return tryMe2;
  


// Exports just myModule class. Not anything outside of it.
export default myModule; 

在另一个文件中

import MyModule from './test';

let bar = new MyModule();

tryMe1(1); // ReferenceError: tryMe1 is not defined
tryMe2; // ReferenceError: tryMe2 is not defined
bar.tryMe1(1); // TypeError: bar.tryMe1 is not a function
bar.tryMe2; // undefined

bar.tryMe3(1); // 101
bar.getTryMe1(1); // 3
bar.getTryMe2(); // 1234

【讨论】:

如果你想在私有方法中使用类上下文(this),你应该在类中使用tryMe1(1).bind(this)。但如果你使用箭头函数,这将失败。 @JacopKane 但是你必须将bind(this) 的结果分配给this 的属性(破坏隐私),或者每次调用bind(this),这可能是性能问题——更不用说bound function will be slower than a normal function了。 @John 完全同意,尽管没有上下文,私有方法不是很有用,因为它根本不是没有属性访问的“方法”。它正在成为一个单独的实用功能。我猜唯一的另一种方法是将对上下文的引用作为参数传递。只是大声思考。 猜猜如果我创建超过 1 个 myModule 实例并添加一些 setter method 会发生什么情况,这将更改在所有实例之间共享的 tryMe2 变量。跨度> @Sergey - 什么??【参考方案5】:

你可以使用符号

var say = Symbol()

function Cat()
  this[say]() // call private methos


Cat.prototype[say] = function() alert('im a private') 

附: alexpods 不正确。他得到保护而不是私有,因为继承是名称冲突

其实你可以用var say = String(Math.random())代替Symbol

在 ES6 中:

var say = Symbol()

class Cat 

  constructor()
    this[say]() // call private
  

  [say]()
    alert('im private')
  


【讨论】:

export var say = Symbol();Cat[say]()也可以访问[say]()方法 @novaline 私有变量不是防止黑客攻击,而是防止意外用子类覆盖属性。【参考方案6】:

我希望这会有所帮助。 :)

我。在IIFE(Immediately-invoked function expression) 中声明变量、函数,这些函数只能在匿名函数中使用。 (当您需要更改 ES6 的代码时,最好使用“let, const”关键字而不使用 'var'。)

let Name = (function() 
  const _privateHello = function() 
  
  class Name 
    constructor() 
    
    publicMethod() 
      _privateHello()
    
  
  return Name;
)();

二。 WeakMap 对象可以很好地解决内存泄漏问题。

当实例被移除时,WeakMap 中存储的变量也会被移除。检查这篇文章。 (Managing the private data of ES6 classes)

let Name = (function() 
  const _privateName = new WeakMap();
)();

三。让我们把所有东西放在一起。

let Name = (function() 
  const _privateName = new WeakMap();
  const _privateHello = function(fullName) 
    console.log("Hello, " + fullName);
  

  class Name 
    constructor(firstName, lastName) 
      _privateName.set(this, firstName: firstName, lastName: lastName);
    
    static printName(name) 
      let privateName = _privateName.get(name);
      let _fullname = privateName.firstName + " " + privateName.lastName;
      _privateHello(_fullname);
    
    printName() 
      let privateName = _privateName.get(this);
      let _fullname = privateName.firstName + " " + privateName.lastName;
      _privateHello(_fullname);
    
  

  return Name;
)();

var aMan = new Name("JH", "Son");
aMan.printName(); // "Hello, JH Son"
Name.printName(aMan); // "Hello, JH Son"

【讨论】:

【参考方案7】:

我想出了一个我认为更好的解决方案:

不需要“this._”、that/self、weakmaps、symbols 等。清晰直接的“类”代码

私有变量和方法是真正私有的并且具有正确的“this”绑定

根本不使用“this”,这意味着代码清晰且不易出错

公共接口清晰,作为私有方法的代理与实现分离

可以轻松合成

你可以这样做:

function Counter() 
  // public interface
  const proxy = 
    advance,  // advance counter and get new value
    reset,    // reset value
    value     // get value
  
	
  // private variables and methods
  let count=0;
    
  function advance() 
    return ++count;
  
    	
  function reset(newCount) 
    count=(newCount || 0);
  
    	
  function value() 
    return count;
  
    
  return proxy;

    	
let counter=Counter.New();
console.log(counter instanceof Counter); // true
counter.reset(100);
console.log('Counter next = '+counter.advance()); // 101
console.log(Object.getOwnPropertyNames(counter)); // ["advance", "reset", "value"]
<script src="https://cdn.rawgit.com/kofifus/New/7987670c/new.js"></script>

请参阅New 获取代码和更详细的示例,包括构造函数和组合

【讨论】:

这种模式一直是合乎逻辑的。您不仅可以获得提升和私有,还可以通过简单地返回对象中的每个函数来将函数转换为闭包并公开公共方法。此外,您可以通过绑定到方法来利用this 范围,如果您不想调用new,只需将其作为IIFE 执行。真正是编写代码的好方法。【参考方案8】:

正如Marcelo Lazaroni 已经说过的,

虽然目前没有办法将方法或属性声明为私有,但 ES6 模块不在全局命名空间中。因此,您在模块中声明但未导出的任何内容都将不可用于程序的任何其他部分,但在运行时仍可用于您的模块。

但他的示例没有显示私有方法如何访问类实例的成员。 Max 向我们展示了一些很好的例子,说明如何通过绑定或在构造函数中使用 lambda 方法来访问实例成员,但我想添加一种更简单的方法:将实例作为参数传递给私有方法。这样做会导致 Max 的 MyClass 看起来像这样:

function myPrivateFunction(myClass) 
  console.log("My property: " + myClass.prop);


class MyClass() 
  constructor() 
    this.prop = "myProp";
  
  testMethod() 
    myPrivateFunction(this);
  

module.exports = MyClass;

您采用哪种方式实际上取决于个人喜好。

【讨论】:

【参考方案9】:

您是否考虑过使用工厂函数? 它们通常是 Javascript 中的 much better alternative to classes or constructor functions。 以下是其工作原理的示例:

function car () 

    var privateVariable = 4

    function privateFunction () 

    return 

        color: 'red',

        drive: function (miles) ,

        stop: function() 

        ....

    


感谢闭包,您可以访问返回对象内的所有私有函数和变量,但不能从外部访问它们。

【讨论】:

对我来说,这是最糟糕的解决方案。您不是在这里创建一个实际的 ES6 类,只是返回一个 POJO。此外,每次调用工厂时都会创建您的私有闭包,从而导致更高的内存占用和更慢的性能。 重点是避免创建类。事实上,许多 js 开发人员认为在 JavaScript 中创建类是一种反模式。对于语言的性质以及关键字“this”在 js 中引入的风险。就性能而言,除非您一次创建成千上万个这样的对象,否则它并不重要;在这种情况下,即使使用类,您也可能会遇到性能问题。 嗯。但是,这个答案与问题有什么关系呢? 使用工厂函数是使用有助于创建私有/公共方法的类的有效替代方法。如果未来的读者找不到使用令人满意的类的解决方案,我认为知道他们有解决问题的替代方案对他们来说很有价值。

以上是关于如何使用 Traceur 在 ES6 类中实现私有方法 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

常见的ES6转码(编译)工具——Babel转码器Traceur转码器

Javascript 实现的类属性私有化

在 Typescript 中实现接口时如何定义私有属性?

通过traceur.js来编写ES6

如何使用 ES6 模块模拟单元测试的依赖关系

ES6框架的搭建