函数可以动态定义,也可以分配给变量。如果创建了一个新函数并且将其分配给保存了另外函数的同一个变量,那么就以一个新函数覆盖了旧函数。在某种程度上,回收了旧函数指针以指向一个新函数。而这一切发生在旧函数体的内部。在这种情况下,该函数以一个新的实现覆盖并重新定义了自身。这可能听起来比实际上更复杂,下面让我们看一下简单的例子:
let scareMe = function () {
alert('Boo!')
scareMe = function () {
alert('Double boo!')
}
}
//使用自定义函数
scareMe(); //输出Boo!
scareMe(); //输出Double boo!
当您的函数有一些初始化准备工作,并且仅需要执行一次,那么这种模式就非常有用。因为并没有任何理由去执行本可以避免的重复工作,即该函数的一些部分可能并不再需要。在这种情况下,自定义函数可以更新自身的实现。使用此模式可以显著地帮助您提升应用程序的性能,这是由于重新定义的函数仅执行了更少的工作。
注意:这种模式的另一个名称是"惰性函数定义"(lazy function definition)。因为函数直到第一次使用时才被正确地定义,并且其具有向后惰性,执行了更少的工作。
该模式的其中一个缺点在于,当它重定义自身时已经添加到原始函数的任何属性都会丢失。此外,如果该函数使用了不同的名称,比如分配给不同的变量或者以对象的方法来使用,那么重定义部分将永远不会发生,并且将会执行原始函数体。
下面让我们来看一个例子,该例中scareMe()函数将以第一类对象(first-class object)使用的方式来使用:
- 添加一个新的属性
- 函数对象被分配给一个新的变量
- 该函数也以一个方法的形式使用
考虑下面的代码片段:
//1. 添加一个新的属性
scareMe.property = 'properly';
//2. 赋值给另一个不同名称的变量
let prank = scareMe;
//3. 作为一个方法使用
let spooky = {
boo: scareMe
};
prank(); //输出Boo!
prank(); //输出Boo!
console.log(prank.property); //输出'properly'
spooky.boo(); //输出Boo!
spooky.boo(); //输出Boo!
console.log(spooky.boo.property); //输出'properly'
scareMe(); //输出Double boo!
scareMe(); //输出Double boo!
console.log(scareMe.property); //输出undefined
正如您所看到的,当将该函数分配给一个新的变量时,如预期的那样,函数的自定义(self-definition)并没有发生。每次当调用prank()时,它都通知'Boo!'消息,同时它还覆盖了全局scareMe()函数,但是prank()自身保持了可见旧函数,其中还包括属性。当该函数以spooky对象的boo()方法使用时,也发生了同样的情况。所有这些调用不断地重写全局scareMe()指针,以至于当它最终被调用时,它才第一次具有更新函数主体并通知'Double boo!'消息的权利。此外,它也不能访问scareMe.property属性。
参考资料:《JavaScript 模式》 Stoyan Stefanov 著 陈新 译
网友评论