【问题标题】:Can anyone explain what is happening in the code below that is using JS and memoization technique谁能解释下面使用 JS 和 memoization 技术的代码中发生了什么
【发布时间】:2023-03-13 14:32:02
【问题描述】:

如果有人能以简单的方式逐步解释这里发生的事情,那将非常有帮助。我知道memoize() 正在缓存函数,但我需要更好地理解。谢谢!

var memoize = function (f) {
  var cache = {};
  return function () {
    var str = JSON.stringify(arguments);
    cache[str] = cache[str] || f.apply(f, arguments);
    return cache[str];
  };
};

var mUser = memoize(function(x){
  return function() {
    return x;
  };
});

var x = mUser(1);
var y = mUser(2);
console.log(x());  //1
console.log(y());  //2

编辑:我保留原件作为记录。但是发布修改后的代码和我对它的理解。如果我是对是错,我需要意见和解释。

var memoize = function (injected) {
  var cache = {};
  return function closure_with_access_to_cache () {
    var str = JSON.stringify(arguments);
    cache[str] = cache[str] || injected.apply(injected, arguments);
    console.log(cache);
    return cache[str];
  };
};

var memoizeUser = memoize (function injected(a) {
  return function closure_with_access_to_a () {
    return a;
  };
});

memoizeUser();

让我们试着回溯一下。

首先,当memoizeUser(); 语句被执行时,memoizeUser 代表什么或者首先调用哪个函数?

var memoizeUser = ... 是一个函数表达式,表示它没有被提升。

所以,memoize 被调用。

但是,var memoize = ... 也是一个函数表达式。仔细看,它是一个闭包closure_with_access_to_cache,并在调用时接收传递给memoizeUser 的参数。

在这个closure_with_access_to_cache中,第一次cache是空的,所以injected.apply(injected, arguments)被执行并得到另一个闭包closure_with_access_to_a作为返回值。该值存储到cache,然后返回。所以,memoizeUser 实际上变成了closure_with_access_to_a,其中a 等于传递给memoizeUser 的值。

让我们看看一些通话和日志。

console.log(memoizeUser());
{ '{}': [Function: closure_with_access_to_a] }
[Function: closure_with_access_to_a]

缓存键是空对象,因为没有任何东西作为参数传递给memoizeUser()。 memoizeUser() 返回函数 closure_with_access_to_a 记录。

console.log(memoizeUser()());
{ '{}': [Function: closure_with_access_to_a] }
undefined

memoizeUser() 返回函数 closure_with_access_to_a,它被调用并记录为 undefined,它是 a 的值,因为没有任何东西传递给 memoizeUser。

memoizeUser(1);
{ '{"0":1}': [Function: closure_with_access_to_a] }

除了 a 的值为 1 之外,与上面类似。

console.log(memoizeUser(1)());
{ '{"0":1}': [Function: closure_with_access_to_a] }
1

除了a 的值为 1 之外,与上面类似。

【问题讨论】:

  • 可以看看 Map 和其他参数,它使东西更加防弹......
  • @Jonasw 感谢您的建议!

标签: javascript memoization


【解决方案1】:

所以本质上,它的作用是确保任何给定的函数只会针对您传递给它的任何给定参数集执行一次。

memoize 函数在为给定函数执行时,将返回一个在上下文中具有该缓存的新函数。它所做的第一件事是创建参数对象的 JSON 字符串表示形式,以用作该特定结果的唯一键。

然后它使用空合并运算符将该缓存值设置为自身(如果它已经存在)或应用了这些参数的注入函数的结果。

如果你真正命名你正在使用的所有函数,这会更有意义:

function memoize(injectedFunction) {
  var cache = {};
  return function memoizedFunction() {
    // 'arguments' here is the arguments object for memoizedFunction.
    var cacheKey= JSON.stringify(arguments);

    // This is a logical OR which is null coalescing in JS. If cache[str] 
    // is null or undefined, the statement proceeds to call injectedFunction.apply. 
    // If it's not, then it returns whatever is stored there.
    cache[cacheKey] = cache[cacheKey] || injectedFunction.apply(injectedFunction, arguments);  
    return cache[cacheKey];
  };
};

所以本质上,考虑注入一个简单的加法函数:

function add(a, b) { return a + b; }

var memoizedAdd = memoize(add);

console.log(memoizedAdd(1, 2));

此时,memoizedAdd 使用参数 1、2 运行。这将创建一个 "{\"0\": 1, \"1\": 2}" 的 cacheKey,然后设置 cache[cacheKey] = add.apply(add, [1 ,2])(实际上,当然在其中它认为 'add' 为 'injectedFunction'。

下次你运行 memoizeAdd(1,2) 时,你会从缓存中得到与 3 相同的结果,而无需再次运行 add() 函数。

希望一切都有意义。

【讨论】:

  • @Paul 感谢您花费的时间和精力。我编辑了我的问题并添加了一些想法以供进一步分析:) 我猜它有点棘手的是 memoizeUser 内部的关闭
【解决方案2】:

memoize() 正在缓存函数是什么意思?这意味着函数不会被执行,而是返回一个缓存的结果。 (纯)函数的结果总是基于它的参数。如果您调用具有相同参数的函数,它将返回相同的结果。这就是为什么我们可以将结果存储在 cache 对象中,其中参数(或 str 的字符串化表示)作为键,函数的结果作为值:

cache[str] = f.apply(f, arguments);

apply 用于 to apply function f 到我们提供的参数数组,在这种情况下,我们的 f 函数被调用的所有参数。

cache[str] = cache[str] || f.apply(f, arguments);

是为cache[str] 设置默认值的简写方式。如果cache[str]没有设置,那么函数f会被调用并存储结果。

你的例子中被记忆的函数也很有趣。它接受一个参数并返回一个函数。 x 和 y 是函数。您可以通过添加空参数列表() 来调用它们。这就是为什么最后几行阅读

console.log(x());  //1
console.log(y());  //2

在 JavaScript 中,如果你在另一个函数中使用 function 关键字,你就是在创建一个闭包。

由于闭包,局部变量(局部变量x)在从被调用函数返回后仍然可以访问。

当函数 mUser 以 1 作为参数调用时,局部变量 x 等于 1。返回的函数在所谓的闭包中捕获了这种事态。闭包是一种将局部变量的值与函数一起存储的方法。

【讨论】:

  • “重要的是要注意 memoize 函数返回一个可以不带参数调用的函数。这就是最后几行读取的原因”,你在这一点上是不正确的。 x 可以不带参数调用的原因是因为在创建 mUser 时传递给 memoize 的函数,而不是因为它被 memoized。
  • @Paul 完全正确。感谢您的输入。我已经更新了我的答案。你能再看一遍吗?
  • @snwclone 感谢您的回答!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多