gpt4 book ai didi

javascript - 为 Node CommonJS 模块创建不可变类

转载 作者:太空宇宙 更新时间:2023-11-04 02:55:19 25 4
gpt4 key购买 nike

我正在 Node 项目中创建一个基本类,并且我想使用 Jest 来测试它。我收到一个错误,暗示在测试中使用“严格”模式,我想避免/修复该错误

<小时/>~/lib/LogRecord.js

module.exports = class LogRecord {
constructor(level, message, timestamp) {
this.level = level;
this.message = message;
this.timestamp = timestamp ? timestamp : Date.now();
}

get level() {
return this.level;
}

get message() {
return this.message;
}

get timestamp() {
return this.timestamp;
}
}

我正在用这个测试它:

let LogRecord = require('../lib/logRecord');

describe('Test LogRecord functionality', () => {
test('LogRecord constructor', () => {
let timestamp = Date.now();
let logRecord = new LogRecord('INFO', 'Test Message', timestamp);
expect(logRecord.level).toBe('INFO');
expect(logRecord.message).toBe('Test Message');
expect(logRecord.timestamp).toBe(timestamp);
});
test('LogRecord is read-only', () => {
let timestamp = Date.now();
let logRecord = new LogRecord('INFO', 'Test Message', timestamp);

logRecord.level = 'WARN'
logRecord.message = 'New Message'
logRecord.timestamp = Date.now();

expect(logRecord.level).toBe('INFO');
expect(logRecord.message).toBe('Test Message');
expect(logRecord.timestamp).toBe(timestamp);
});
});

当我运行 npm test 时,我在两个 LogRecord 测试中都收到以下错误:

Test LogRecord functionality › LogRecord constructor

TypeError: Cannot set property level of #<LogRecord> which has only a getter

1 | module.exports = class LogRecord {
2 | constructor(level, message, timestamp) {
> 3 | this.level = level;
| ^
4 | this.message = message;
5 | this.timestamp = timestamp ? timestamp : Date.now();
6 | }

at new LogRecord (lib/logRecord.js:3:9)
at Object.test (test/logRecord.test.js:6:25)

编辑 - worker 类(Class)

const data = new WeakMap();

let levelKey = {id:'level'};
let messageKey = {id:'message'};
let timestampKey = {id:'timestamp'};

module.exports = class LogRecord {
constructor(level, message, timestamp) {
data.set(levelKey, level);
data.set(messageKey, message);
data.set(timestampKey, timestamp ? timestamp : Date.now());
}

get level () {
return data.get(levelKey)
}

get message () {
return data.get(messageKey)
}

get timestamp () {
return data.get(timestampKey)
}
}

最佳答案

测试的目的是确保您的代码按照您的想法运行。考虑下面的类:

class Foo {
constructor (bar) {
this._bar = bar;
}

get bar () {
return this._bar;
}
}

此处 bar只读,无法设置 bar 属性:

let foo = new Foo('a foo');
foo.bar; // 'a foo'
foo.bar = 'am not'; // TypeError!

模块问题并不真正相关:因为注释中链接的 Logar 类主体总是严格模式,无论如何。

因此,如果您希望某个属性是只读的,则无需担心写入它。工作流程可能如下所示:

  1. 编写空类 Foo class Foo {} 并构造一个实例 foo = new Foo()
  2. 编写测试来检查 bar,但由于我们有一个空类而失败
  3. 添加构造函数参数和 getter
  4. 检查测试现在是否通过
  5. 添加测试以确保尝试设置栏会引发预期错误*

如果您不需要只读属性,您可以添加一个 setter :

class Foo {
constructor (bar) {
this._bar = bar;
}

get bar () {
return this._bar;
}

set bar (value) {
this._bar = value;
}
}

在这种情况下,您需要添加一个测试来设置 bar 并读取更改后的值。

* 你可能想知道为什么这个测试在这里,当这种行为由规范保证时,我认为测试是必要的,因为有人可以(对调用者透明地)将类重构为老式构造函数并创建错误向量:

// post refactor Foo
const Foo = function Foo(bar) {
this.bar = bar; // danger! now writable!
};

希望此类事情能够被知识渊博的审阅者发现,但无论如何我都会编写测试。

更新

如果您想要的是在构造函数中设置的保证只读属性,这里有一个方法:

const data = new WeakMap();

module.exports = class Foo () {
constructor (bar) {
data.set(this, bar);
}

get bar () {
return data.get(this);
}
};

因为 data 未导出,所以外部代码无法更改它。尝试设置实例的 bar 属性将会抛出异常。这比仅使用 getter 和 setter 定义下划线属性要复杂一些,但如果这是您想要的,那么...我知道这种模式,因为我已经使用过它。

更新2

您只能为每个模块创建一个weakmap,而不是为每个类或实例创建一个weakmap。弱映射存储一个唯一的数据条目,该数据条目是针对各个实例的(即this):

const data = new WeakMap();

module.exports = {
Foo: class Foo () {
constructor (bar) {
data.set(this, bar);
}

get bar () {
return data.get(this);
}
},

Bar: class Bar () {
constructor (prop1, prop2) {

// for multiple props we'll store an object...
data.set(this, { prop2, prop1 });
}

get prop1 () {
// ...and retrieve it and access it's props to return
return data.get(this).prop1;
}

get prop2 () {
return data.get(this).prop2;
}
}
};

请注意,使用 getter 设置 props 但没有 setter 仍然会抛出...

// in someotherfile.js
const { Foo } = require('path/to/file/with/foo.js');
const foo = new Foo('imma foo');
foo.bar; // 'imma foo'
foo.bar = 'no not really'; // TypeError!

// you can still set random properties that don't have a getter:
foo.baz = 'I do not throw';
foo.baz; // 'I do not throw'

关于javascript - 为 Node CommonJS 模块创建不可变类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51613186/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com