为什么外部函数的属性继承给 JavaScript 函数的私有变量

why does the property of the external function inherit to the private variable of the javascript function

提问人:Bas Botman 提问时间:1/3/2023 更新时间:1/3/2023 访问量:56

问:

尝试使用函数式编程创建具有外部函数的对象,以减少内存使用。

其功能是

//increment no of test cases
function incrNoOfTestCases(inputObj){
    let hits = inputObj.hits;
    console.log(`function says: ${hits}`)
    return {...inputObj, hits: (hits || 0) + 1};
}

creator 函数是

const test = function(testDecription){
    let state = {hits:0};
    state.getState = ()=> testDecription;
    state.incHits = () => state = incrNoOfTestCases(state);
    state.getHits = () => state.hits || 0;
    return state;
}

当我进行以下测试时,我可以通过为函数分配属性来更改命中。

test1.incHits().hits=10;  //mutable!!
console.log(test1.getHits());  //gives 10
console.log(test1.incHits().hits);    //gives function says: 10 and then 11
test1.hits=20; //immutable
console.log(test1.getHits());  //gives 10

我尝试了各种替代方案,最后想出了声明函数以增加创建器函数中的测试用例。我正在寻找一个解释为什么该属性是可变的,而不是工作案例。

在第一个版本中,该函数是

function incrNoOfTestCases(inputObj){
    return {...inputObj, hits: (inputObj.hits || 0) + 1};
}

在这种情况下,我还希望 inputObj.hits 不会被 incrNoOfTestCases.hits 可变,但也不是。

似乎 JavaScript 在执行函数之前首先将 incrNoOfTestCases.hits 分配给 state。这是正确的吗?你能解释一下为什么吗?

JavaScript的 对象 函数式编程 可变

评论

0赞 T.J. Crowder 1/3/2023
在您的第三个代码块中,什么是?test1
0赞 Pointy 1/3/2023
如何以及何时调用?test()
1赞 T.J. Crowder 1/3/2023
假设是调用的结果,那么可以肯定的是,返回的对象是可变的。为什么不呢?您的代码没有执行任何操作来阻止修改 (返回) 返回的对象的属性。“看来 JavaScript 首先分配了......”如果你想知道事情发生的顺序,最好的办法是使用调试器(更多:ericlippert.com/2014/03/05/how-to-debug-small-programs)。test1testincHintshitsincrNoOfTestCasesincHits
1赞 Bergi 1/3/2023
"创建一个具有外部函数的对象以减少内存使用量“ - 好吧,您的函数无法做到这一点,它肯定会在每次调用时创建新函数,并将它们直接放在对象上。test
0赞 Bas Botman 1/5/2023
谢谢大家的评论。非常感谢。忘记包含 const test1 = test('test 1 ...');作为我测试的第一行,对不起克劳德......我现在意识到,状态是由创建者函数返回的,因此 test1 从那时起与任何属性一样,因此是可变的。但是 incHits 是 test1 的一个属性,作为一个函数是一个对象,它可以有自己的属性,但是 test1.incHits().hits =/= test1.hits。我希望 incrNoOfTestCases 使用 test1.hits iso test1.incHits().hits。

答:

2赞 geoffrey 1/3/2023 #1

此代码没有任何功能。在函数式编程中,您不希望小逻辑单元独立处理其状态。那是OOP。如果更改值,则使用闭包与使用类相同。

这更实用,尽管它可能不像您想要的那样工作。

const Test = (description, hits = 0) => ({
    getState: () => description,
    incHits: () => Test(description, hits + 1),
    getHits: () => hits
})

const test1 = Test('description')
const test2 = test1.incHits(); // incHits returns a new instance of Test
console.log(test2.getHits())

这本来可以做同样的事情

class Test {
   constructor(description, hits = 0) {
      this.description = description;
      this.hits = hits;
   }
   static of (description) { return new Test(description) }
   getState () { return this.description}
   incHits () { return new Test(this.description, this.hits + 1); }
   getHits () { return this.hits }
}

const test1 = Test.of('description');
const test2 = test1.incHits();

另一种方法

const Test = (description, hits = 0) => ({ description, hits, type: 'Test' });
export const getState = ({ description }) => description;
export const incHits = ({ description, hits }) => Test(description, hits + 1);
export const getHits = ({ hits }) => hits;
export const of = (description) => Test(description);
import * from './Test'
const test1 = Test.of('description');
const test2 = Test.incHits(test1);

评论

0赞 Bas Botman 1/5/2023
谢谢 Geoffrey,你让我进行了更多的研究,在你发表评论后再次深入研究 crockford.com/javascript/private.html 和他的书。我缺乏关于公共/私人和制作物品的方法的知识。最后,Douglas 使用一个返回对象的函数来创建一个对象(参见 JavaScript 的优点,第 5 章)。对我来说,它看起来很优雅,并且充分利用了 JavaScript。
0赞 geoffrey 1/5/2023
你学习这些东西是非常好的,但要意识到它已经有 20 年的历史了。类语法要简单得多。私人成员是自我记录的,在 Typescript 中,您甚至可以获得关键字(尽管它们只能保护您免受自己的伤害)。如果您不喜欢该关键字,请考虑使用静态方法(如“factory”函数,该函数将对象创建委托给构造函数,并仅传递参数。privateprotectednewTest.of
0赞 geoffrey 1/5/2023
在我的回答中列出的 3 种构造方式中,第一种是性能最差的,因为每次创建 的实例时都会创建新函数。它的可读性也不是很强,因为“方法”似乎有自由变量,它们是从闭包中获得的。一个类(在 JS 中)很明显,因为你必须提到,并且第 3 个示例中的函数采用参数。Testthis
0赞 geoffrey 1/5/2023
无论如何,请注意,在函数式编程中,对象不保持状态:它们只是数据袋。