缓存John Resig的《学习高级JavaScript》中函数的返回结果

3

我有几个关于John Resig在http://ejohn.org/apps/learn/#19中练习#19的函数的问题。

  1. 第二个最后一行getElements.cache = {};的目的是什么?它是否将return results存储在数组中?

  2. 如果我的猜测(1)正确,那么它只能捕获return results,因为在函数的“else”部分中,getElements.cache[name] = results;吗?

  3. 当我在教程的控制台中使用代码时,我从“else”部分中删除了getElements.cache[name] = results这一行,但仍然得到了与存在该行相同的结果--也就是说,它告诉我有76个元素。因此,如果不需要该结果,getElements.cache[name] = results这一行的目的是什么?

  4. 在函数的“else”部分中,getElements.cache[name] = results;这一行的顺序是否具有重要意义?在代码的“if”部分中,它说results = getElements.cache[name]

  5. 最后,cache是JavaScript中预定义的函数吗?我在文档中找不到它。

function getElements( name ) { 
  var results; 

  if ( getElements.cache[name] ) { 
    results = getElements.cache[name]; 
  } else { 
    results = document.getElementsByTagName(name); 
    getElements.cache[name] = results; 
  } 

  return results; 
} 
getElements.cache = {}; 

log( "Elements found: ", getElements("pre").length );
3个回答

4

a) 倒数第二行确定了对象getElements上的属性“cache”是一个对象,实际上是初始化该属性。

b) 那就是缓存,将getElements视为一个对象,缓存是保存结果的哈希表。

c) 是的,你仍然会得到相同的结果,因为缓存只是缓存,不会以任何方式改变答案,它只可能加快速度。

d) 是的,一个存储结果到缓存中,另一个从缓存中提取结果。

e) 不,这是为“getElements”定义的自定义属性,可以定义任意数量的具有不同名称的属性。


非常感谢。关于(d),哪一个将结果存储到缓存中?哪一个是从缓存中取出结果的?关于(e),这个自定义属性是在“幕后”定义的吗?即我没有看到任何创建此属性的代码。 - mjmitche
使用“result = getElements.cache[name]”的if语句正在从缓存中复制该值。关于(e),请参见(a)。那就是定义。 - Myles
1
为什么初始化该属性并不是必须的?(来自a)如果您不这样做,就无法使用getElements.cache[name],因为该函数尚未具有cache属性。但我认为可以通过仅使用getElements[name]来省略缓存属性。 - Gnijuohz
@Gnijuohz 这是一个好问题。我当时不确定自己在想什么,但确实需要初始化才能使其成为数组。我已经相应地编辑了帖子。 - Myles

2

1.- 第二个最后一行的getElements.cache = {};有什么作用?它是在数组中存储返回结果吗?

它只是初始化对象。

2.- 如果我在(1)中的猜测是正确的,那么它是否仅捕获返回结果,因为在函数的“else”部分中,getElements.cache[name] = results;

是的,它使用缓存方法来避免访问DOM。

3.- 当我在教程的控制台中使用代码时,我从“else”部分中删除了getElements.cache[name] = results一行,但仍然得到了相同的结果,就像当它在那里时-换句话说,它告诉我有76个元素。因此,如果不需要获取结果,getElements.cache[name] = results这条线的目的是什么?

从本质上讲,它并不是必要的,因为它只是使函数返回更快,因为访问对象比遍历DOM更快。

4.- 在函数的“else”部分中,getElements.cache[name] = results;的顺序与代码的if部分相反,那里说results = getElements.cache[name]

在else部分中,首先,results变量被赋予实际结果的值,然后结果被缓存。然后,在then部分中,results变量只是被赋予了缓存值。不需要重新缓存它。

5.- 最后,cache是JavaScript中预定义的函数吗?我在文档中找不到它。

它不是预定义的。它被用作此特定函数的属性(在JavaScript中,函数是一级对象,并且可以在属性中存储值。

这是带有注释的代码:

function getElements( name ) { 
  var results; 

  if (getElements.cache[name]) { 
    results = getElements.cache[name]; // Use the cached value
  } else { 
    results = document.getElementsByTagName(name); // Get the desired value
    getElements.cache[name] = results; // cache the result
  } 

  return results; 
} 

getElements.cache = {}; // Initialize the cache

谢谢。关于第三点,如果移除了 getElements.cache[name] = results,期望的值将会被存储在变量 results 中。当您提到“遍历 Dom”时,您是指需要从变量中提取所需的值吗? - mjmitche
“document.getElementsByTagName(name);” 这行代码从 DOM(文档对象模型,即 HTML 的内部表示)中获取信息,而从中获取信息比仅读取对象属性更加耗费资源。 - Marcelo
好的,非常感谢,但这引出了另一个问题。但是既然代码中有“document.getElementsByTagName(name)”,那么这个函数不是同时在“访问对象”(使用缓存)和“遍历DOM”(使用getElementByID)吗?如果缓存的目的是使其比遍历Dom更便宜,为什么函数两者都做呢?这不是更加昂贵吗?难道最便宜的事情不是省略document. getElementsByTag(name)吗? - mjmitche
它只会在第一次执行时进行操作。它将从DOM中获取信息并将其存储在缓存中(属性分配不昂贵)。第二次,它将仅查找该值是否已经在缓存中,并直接从缓存中读取。我想你可以尝试对其进行分析,创建一个函数,使其调用带有和不带缓存的100K次函数,并查看是否有差异。理论上应该有差异;) - Marcelo
好的,现在清楚了。我没有意识到缓存的(显而易见的)目的是为了多次调用。非常感谢您的帮助。 - mjmitche

2

一种替代的缓存函数

// Were going to get some things.
var getSomeThings = function() {
    // Calling something expense to get the things.
    var things = somethingExpensive();
    // overwriting this function with a function that returns things
    getSomeThings = function() {
         return things;
    }
    // call the new function that returns things and return things.
    return getSomeThings();
};

第一次调用此函数时,您需要计算并返回结果。第二次、第三次等调用时,您只需返回结果即可。
另一个示例允许您使用不同的参数进行缓存,但思路类似。这意味着您缓存了许多东西而不仅仅是一个。
【编辑】
此代码创建了一个名为“getSomeThings”的变量,并将其分配给一个函数。当您调用该函数时,它可以访问“getSomeThings”本身作为变量,并使用新函数设置它。
例如:
function getSomethingExpensive() {
     ...
     return 42;
}
var b = function() {
     b = getSomethingExpensive();
}    
b();
alert(b);

非常感谢。您能否稍微详细解释一下它如何在第二次和第三次调用中跳过计算部分?函数 getSomeThings = function() { return things; } 实际上如何“覆盖” somethingExpensive() 函数? - mjmitche
@mjmitche 请看编辑。你不是在覆盖 somethingExpensive,而是在覆盖 getSomeThings 本身,因此它永远不会再次调用 somethingExpensive。 - Raynos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接