重定义函数

函数可以被动态定义,也可以被赋值给变量。如果将你定义的函数赋值给已经存在的函数变量的话,则新函数会覆盖旧函数。这样做的结果是,旧函数的引用被丢弃掉,变量中所存储的引用值替换成了新的函数。这样看起来这个变量指代的函数逻辑就发生了变化,或者说函数进行了“重新定义”或“重写”。听起来很麻烦,但实际上并不复杂,来看一个例子:

  1. var scareMe = function () {
  2. alert("Boo!");
  3. scareMe = function () {
  4. alert("Double boo!");
  5. };
  6. };
  7. // 使用重定义函数
  8. scareMe(); // Boo!
  9. scareMe(); // Double boo!

当函数中包含一些初始化操作,并希望这些初始化操作只执行一次,那么这种模式是非常合适的,因为我们要避免重复执行不需要的代码。在这个场景中,函数执行一次后就被重写为另外一个函数了。

使用这种模式可以帮助提高应用的执行效率,因为重新定义的函数执行的代码量更少。

这种模式的另外一个名字是“函数的懒惰定义”,因为直到函数执行一次后才重新定义,可以说它是“某个时间点之后才存在”,简称“懒惰定义”。

这种模式有一个明显的缺陷,就是之前给原函数添加的功能在重定义之后都丢失了。同时,如果这个函数被重定义为不同的名字,被赋值给不同的变量,或者是作为对象的方法使用,那么重定义的部分并不会生效,原来的函数依然会被执行。

让我们来看一个例子,scareMe()函数在这里作为一等对象来使用:

  1. 给他增加了一个属性
  2. 函数对象被赋值给一个新变量
  3. 函数还被作为方法来调用

看一下这段代码:

  1. // 1. 添加一个新属性
  2. scareMe.property = "properly";
  3. // 2. 被赋值给一个不同名的变量
  4. var prank = scareMe;
  5. // 3. 作为方法使用
  6. var spooky = {
  7. boo: scareMe
  8. };
  9. // 使用新名字调用
  10. prank(); // "Boo!"
  11. prank(); // "Boo!"
  12. console.log(prank.property); // "properly"
  13. // 作为方法调用
  14. spooky.boo(); // "Boo!"
  15. spooky.boo(); // "Boo!"
  16. console.log(spooky.boo.property); // "properly"
  17. // 使用重定义函数
  18. scareMe(); // Double boo!
  19. scareMe(); // Double boo!
  20. console.log(scareMe.property); // undefined

从结果来看,当重定义函数被赋值给一个新的变量的时候,这段使用重定义函数的代码的执行结果与我们期望的结果可能并不一样。每当prank()被调用的时候,它都弹出“Boo!”。同时它也重写了scareMe()函数,但是prank()自己仍然能够使用之前的定义,包括属性property。在这个函数被作为spooky对象的boo()方法调用的时候,结果也一样。所有的这些调用,在第一次的时候就已经修改了全局的scareMe()的指向,所以当它最终被调用的时候,它的函数体已经被修改为弹出“Double boo”,也就不能获取到新添加的属性scareMe.property