关于javascript:如何使用Traceur在ES6类中实现私有方法

How to implement private method in ES6 class with Traceur

本问题已经有最佳答案,请猛点这里访问。

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

我想从ES5实现这些东西:

1
2
3
4
5
6
7
8
9
10
function Animal() {
    var self = this,
        sayHi;

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

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

目前,traceur不支持privatepublic关键字(来自和谐)。 ES6类语法不允许在类体中使用简单的var(或let)语句。

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

1
2
3
4
5
6
var sayHi = function() {
    // ... do stuff
};

class Animal {
...

没有什么比通过预期更好的了,你不能每次都没有apply -ing或bind的情况下将正确的this传递给私有方法。

那么,是否有可能在ES6类中使用与traceur编译器兼容的私有数据?


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

所以Traceur不支持privatepublic。 6to5(目前它被称为"Babel")实现了这个提议用于实验目的(另见本讨论)。但毕竟这只是提案。

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

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

1
2
3
4
5
6
class Animal {

    _sayHi() {
        // do stuff
    }
}


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

1
2
3
4
5
6
7
8
9
10
11
12
function myPrivateFunction() {
  console.log("My property:" + this.prop);
}

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

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


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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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;

在另一个文件中

1
2
3
4
5
6
7
8
9
10
11
12
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


你可以使用Symbol

1
2
3
4
5
6
7
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中:

1
2
3
4
5
6
7
8
9
10
11
12
13
var say = Symbol()

class Cat {

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

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

}


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

I.声明vars,IIFE内部的函数(立即调用函数表达式),这些函数只能在匿名函数中使用。 (当您需要更改ES6的代码时,不使用'var'就可以使用"let,const"关键字。)

1
2
3
4
5
6
7
8
9
10
11
12
let Name = (function() {
  const _privateHello = function() {
  }
  class Name {
    constructor() {
    }
    publicMethod() {
      _privateHello()
    }
  }
  return Name;
})();

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

将删除实例时,将删除WeakMap中的存储变量。看看这篇文章。 (管理ES6类的私有数据)

1
2
3
let Name = (function() {
  const _privateName = new WeakMap();
})();

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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"


正如alexpods所说,在ES6中没有专门的方法来做到这一点。但是,对于那些感兴趣的人,还有一个绑定运算符的提议,它启用了这种语法:

1
2
3
4
5
6
7
8
9
10
11
12
function privateMethod() {
  return `Hello ${this.name}`;
}

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

再一次,这只是一个提案。你的旅费可能会改变。


你考虑过使用工厂功能吗?
它们通常是Javascript中类或构造函数的更好的替代品。
以下是它的工作原理示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function car () {

    var privateVariable = 4

    function privateFunction () {}

    return {

        color: 'red',

        drive: function (miles) {},

        stop: function() {}

        ....

    }

}

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


正如马塞洛·拉扎罗尼已经说过的那样,

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.

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

1
2
3
4
5
6
7
8
9
10
11
12
13
function myPrivateFunction(myClass) {
  console.log("My property:" + myClass.prop);
}

class MyClass() {
  constructor() {
    this.prop ="myProp";
  }
  testMethod() {
    myPrivateFunction(this);
  }
}
module.exports = MyClass;

你这样做的方式实际上取决于个人偏好。


我想出了一个更好的解决方案,允许:

  • 不需要'this._',那个/ self,weakmaps,符号等。清晰明了的"类"代码

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

  • 根本不使用'this',这意味着清晰的代码不易出错

  • 公共接口是明确的,并与实现分离,作为私有方法的代理

  • 可以轻松组合

你可以这样做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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"]
1
<script src="https://cdn.rawgit.com/kofifus/New/7987670c/new.js">

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