为什么原型中具有私有属性的类实例的对象在访问私有成员时会抛出异常?

问题描述 投票:0回答:1

考虑这段代码:

class Klass {
  #priv = 42

  get pub() {
    return this.#priv
  }
}

// these two correctly return 42
console.log(new Klass().pub);
console.log((new class extends Klass {}).pub)

// but this one throws "TypeError: Cannot read private member 
// #priv from an object whose class did not declare it"
console.log(Object.create(new Klass()).pub) // throws 

// even though
console.log(Object.create(new Klass()) instanceof Klass) // true
console.log(Object.getPrototypeOf(Object.create(new Klass())).pub) // 42

我认为既然我在原型链中有一个

Klass
的真实实例,那么访问
Object.create(new Klass()).pub
就不会抛出异常。

这是设计使然吗?如果是的话,为什么要这样做呢? 另外,使用通用函数来克隆行为类似于

Klass
的任意类实例的正确方法是什么?

背景:

我在用 vitest 测试某些东西时遇到了这个问题。我的代码看起来像这样:

import { it, expect } from 'vitest';

class Klass {
    #priv = 42;

    get pub() {
        return this.#priv;
    }
}

it('works', () => {
    expect(new Klass()).toMatchObject({
        make_this_test_fail: 'yup',
        pub: 42,
    });
});

我的测试显然失败了,但没有给我很好的差异显示该属性

make_this_test_fail
在它显示的实例上没有找到

 FAIL  test/the.test.js [ test/the.test.js ]
TypeError: Cannot read private member #priv from an object whose class did not declare it
 ❯ Klass.get pub [as pub] test/the.test.js:7:15
      5| 
      6|  get pub() {
      7|   return this.#priv;
       |               ^
      8|  }
      9| }

我将其跟踪到 this 行,其中

actual
来自
deepClone
,它使用
Object.create
here 创建任意类的实例。

javascript oop inheritance private
1个回答
0
投票

这是设计使然吗?如果是的话为什么要这样做?

是的,这是设计使然:私有字段不会沿原型链继承,它们仅存在于构造期间创建它们的特定实例上。这使得它们从外部完全无法检测到,您不能通过例如以下方式弄乱私有字段:交换对象的原型,并且您不能欺骗方法在以前不存在的对象上创建私有字段(通过分配,在示例中考虑 setter)。

私有字段是“硬私有”,非常类似于内置对象的内部槽。它们也不会被代理转发。私有字段的心智模型就像一个

WeakMap
存储每个实例的值。

(也就是说,不是每个人都同意这是一个好的设计。)

此外,使用通用函数来克隆行为类似于

Klass
的任意类实例的正确方法是什么?

你无法编写这样的通用函数。任何类,无论是否使用私有字段,都可以具有不可克隆的私有状态。实现这项工作的唯一方法是为每个类提供一个

clone
方法,以适当的方式克隆自身。如果您想避免冲突,请使用方法名称的符号。

© www.soinside.com 2019 - 2024. All rights reserved.