如何使用 Traceur 在 ES6 class 中实现私有方法

How to implement private method in ES6 class with Traceur

我现在使用 Traceur 编译器来利用 ES6 的特性。

我想从 ES5 实现这些东西:

function Animal() {
    var self = this,
        sayHi;

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

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

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

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

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

class Animal {
...

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

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

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

所以Traceur不支持privatepublic。 6to5(目前称为"Babel")实现了this proposal for experimental purpose (see also this discussion)。但毕竟只是提议

所以现在您可以通过 WeakMap 模拟私有属性(参见 here)。另一种选择是 Symbol - 但它不提供实际隐私,因为可以通过 Object.getOwnPropertySymbols.

轻松访问 属性

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

class Animal {

    _sayHi() {
        // do stuff
    }
}

您始终可以使用正常功能:

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

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

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

正如 alexpods 所说,在 ES6 中没有专门的方法来做到这一点。然而,对于那些感兴趣的人,还有一个关于 bind operator 的提议,它启用了这种语法:

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

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

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

尽管目前无法将方法或 属性 声明为私有,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

您可以使用符号

var say = Symbol()

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

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

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

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

在 ES6 中:

var say = Symbol()

class Cat {

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

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

}

希望对您有所帮助。 :)

我。在 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"

我想出了我认为更好的解决方案,允许:

  • 不需要'this._'、that/self、weakmaps、符号等。清晰明了'class'代码

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

  • 完全不使用 'this' 这意味着清晰的代码更不容易出错

  • public 接口清晰并作为私有方法的代理与实现分离

  • 允许轻松组合

用这个你可以做到:

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 了解代码和更详细的示例,包括构造函数和组合

正如Marcelo Lazaroni已经说过的,

Although currently there is no way to declare a method or property as private, ES6 modules are not in the global namespace. Therefore, anything that you declare in your module and do not export will not be available to any other part of your program, but will still be available to your module during run time.

但是他的示例没有显示私有方法如何访问 class 实例的成员。 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;

具体采用哪种方式完全取决于个人喜好。

你考虑过使用工厂函数吗? 它们通常是 Javascript 中的 much better alternative to classes or constructor functions。 这是它如何工作的示例:

function car () {

    var privateVariable = 4

    function privateFunction () {}

    return {

        color: 'red',

        drive: function (miles) {},

        stop: function() {}

        ....

    }

}

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