如何给class的方法设置私有变量?

will
  • 6
江苏

注意, 问题不是如何定义私有方法, 而是如何给class的方法定义私有变量。私有方法是仅类内部可以访问, 而一个方法的私有变量是仅该方法内部可以访问, 这是2个不同的东西, 请不要混淆

let Test=function(){}
Test.prototype.method=(()=>{
    let private=1
    return function(){}
})()

上面是传统的构造函数加原型的方式实现的类, 可以用立即执行函数给method方法设置私有变量private。
如果改成用class实现这个类, 如何在test方法中设置私有变量呢?尝试过如下代码, 报语法错误了。

let Test=class{
    method(()=>{
        let private=1
        return function(){}
    })()
}
回复
阅读 975
6 个回答
Mannix
  • 1.2k
广东
✓ 已被采纳

你的第一个例子

let Test=function(){}
Test.prototype.method=(()=>{
    let private=1
    return function(){}
})()

这样写的话,这里的 private 是所有 Test 实例的 method 共享的(类似静态变量)

let Test = function() { }
Test.prototype.method = (() => {
  let a = 1
  return function() { return a++ }
})()

let test1 = new Test()
console.log(test1.method()); // 1
console.log(test1.method()); // 2
console.log(new Test().method()) // 3

这种的话没法在 class 内部定义(除非用 static + prototype,见文末补充),可以这样写

const method = (() => {
  let a = 1
  return function() { return a++ }
})()

class Test {
  method = method
}
// 等同于
// class Test {
//   constructor() {
//     this.method = method
//   }
// }

let test1 = new Test()
console.log(test1.method()); // 1
console.log(test1.method()); // 2
console.log(new Test().method()) // 3

如果是要每个实例的 method 都有自己的变量,则可以在构造函数中定义

function Test() {
  this.method = (() => {
    let a = 1
    return function() { return a++ }
  })()
}

let test1 = new Test()
console.log(test1.method()); // 1
console.log(test1.method()); // 2
console.log(new Test().method()) // 1

对应的 class 写法

class Test {
  method = (() => {
    let a = 1
    return function() { return a++ }
  })()
}
// 等同于
// class Test {
//   constructor() {
//     this.method = (() => {
//       let a = 1
//       return function() { return a++ }
//     })()
//   }
// }

let test1 = new Test()
console.log(test1.method()); // 1
console.log(test1.method()); // 2
console.log(new Test().method()) // 1

如果要定义一个仅供 method 方法内部使用的函数,且:

  • method 定义在原型上,而不是实例上
  • 可访问 # 私有属性

那么可以在 static 块中定义

class Test1 {
  #a = 1
  static {
    this.prototype.method = ((privateFn) => function() {
      return privateFn(this) + this.#a
    })((thiz) => 'call privateFn ' + thiz.#a++)
  }
}
  1. 你应该先去了解 class 关键字的用法,而不是在这里瞎试。见 MDN Classes in JavaScript
  2. 过去的 ES 标准没有私有变量,ES2022 后增加了 # 前缀,用来标记私有变量。我觉得很丑,当然这见仁见智。见 MDN
  3. TypeScript 提供另一种写法,比较推荐,可以看看。

新语法用带 # 号前缀的属性/方法名,表示私有。语法简单得多

class Test {
    #private = 1;
    method() {
        console.log(this.#private);
    }
}

不用新语法也可以通过 Symbol 来实现。参阅:JavaScript 私有成员 - SegmentFault 思否

const priv = Symbol();
class Test {
    [priv] = 1;
    method() { console.log(this[priv]); }
}
const method = (() => {
    let x = 0;
    return () => x++
})();
export class Test{
    method = method;
}

但是感觉怪怪的。

看了楼上的一些评论我觉得你可能是过于执着了。

class 与 prototype 本来就是关于继承的不同范式,没必要强行捏合在一起,就好像用 Java 的风格去写 Self 一样,很难说美好。

如果是不支持原型的语言中,要实现原型的效果要怎么做呢,大约是传入一个原型对象

class Test {
  Test::Test(Proto* prototype) : prototype_(prototype) {}
  void foo() { prototype_->foo(); }
  Proto* prototype_;
};

但是 js 本身就支持的东西,何必纠结,需要原型的效果,那我直接操作原型可不可以呢。

class Test{}
Test.prototype.foo=(()=>{
    let n=1
    return function() {return ++n}
})()
7826
  • 3
香港新手上路,请多包涵

这样应该是可以实现你的需求的,看起来不够美观优雅就是了

class Foo {
  get bar () {
    let p = 1
    return () => {
      p = 2
      console.log(p)
    }
  }

  baz () {
    this.bar()
  }
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
宣传栏