函数重载和函数重写
以下是Darren Jones撰写的新书《 JavaScript:Ninja的新手》(第二版)的摘录。 这是JavaScript的终极入门指南。 SitePoint Premium成员可以使用其成员身份进行访问,或者您可以在世界各地的商店中购买副本。
JavaScript的动态特性意味着函数不仅可以调用自身,还可以定义自身,甚至重新定义自身。 这是通过将匿名函数分配给与该函数同名的变量来完成的 。
考虑以下功能:
function party(){
console.log('Wow this is amazing!');
party = function(){
console.log('Been there, got the T-Shirt');
}
}
这会将消息记录在控制台中,然后重新定义自身以在控制台中记录其他消息。 一旦调用了该函数,就好像是这样定义的:
function party() {
console.log('Been there, got the T-Shirt');
}
第一次调用该函数后,它将记录以下消息:“到那里,得到了T恤”:
party();
<< 'Wow this is amazing!'
party();
<< 'Been there, got the T-Shirt'
party();
<< 'Been there, got the T-Shirt'
如果该函数也分配给另一个变量,则此变量将保留原始函数定义,并且不会被重写。 这是因为原始函数被分配给一个变量,然后在函数内,与该函数同名的变量被分配给另一个函数。 如果我们创建一个名为beachParty
的变量,该变量在首次调用并重新定义之前已分配给party()
函数,您可以看到一个示例:
function party(){
console.log('Wow this is amazing!');
party = function(){
console.log('Been there, got the T-Shirt');
}
}
const beachParty = party; // note that the party function has not been invoked
beachParty(); // the party() function has now been redefined, even though it hasn't been called explicitly
<< 'Wow this is amazing!'
party();
<< 'Been there, got the T-Shirt'
beachParty(); // but this function hasn't been redefined
<< 'Wow this is amazing!'
beachParty(); // no matter how many times this is called it will remain the same
<< 'Wow this is amazing!'
财产损失
注意:如果以前在函数上设置了任何属性,则当函数重新定义自身时,这些属性将丢失。 在前面的示例中,我们可以设置music
属性,然后在调用并重新定义函数后,该属性将不再存在:
function party() {
console.log('Wow this is amazing!');
party = function(){
console.log('Been there, got the T-Shirt');
}
}
party.music = 'Classical Jazz'; // set a property of the function
party();
<< "Wow this is amazing!"
party.music; // function has now been redefined, so the property doesn't exist
<< undefined
这称为惰性定义模式 ,通常在首次调用某些初始化代码时使用。 这意味着可以在第一次调用时完成初始化,然后可以将函数重新定义为您希望在以后的每次调用中使用的函数。
初始化时间分支
可以在上一章中讨论的功能检测中使用该技术,以创建可重写自身的函数,称为初始化时间分支 。 这使功能可以在浏览器中更有效地工作,并且避免在每次调用功能时对其进行检查。
让我们以我们的虚拟unicorn
对象为例,该对象尚未在所有浏览器中得到完全支持。 在上一章中,我们研究了如何使用特征检测来检查是否支持此功能。 现在我们可以更进一步:我们可以根据是否支持某些方法来定义一个函数。 这意味着我们只需要在第一次调用该函数时检查支持:
function ride(){
if (window.unicorn) {
ride = function(){
// some code that uses the brand new and sparkly unicorn methods
return 'Riding on a unicorn is the best!';
}
} else {
ride = function(){
// some code that uses the older pony methods
return 'Riding on a pony is still pretty good';
}
}
return ride();
}
在检查window.unicorn
对象是否存在之后(通过检查是否真实),我们根据结果重写了ride()
函数。 在函数的最后,我们再次调用它,以便现在调用重写的函数,并返回相关值。 需要注意的一件事是,函数第一次被调用两次,尽管它在以后每次调用时变得更加高效。 让我们看一下它是如何工作的:
ride(); // the function rewrites itself, then calls itself
<< 'Riding on a pony is still pretty good'
调用该函数后,将根据浏览器的功能对其进行重写。 我们可以通过检查函数而不检查它来检查它:
ride
<< function ride() {
return 'Riding on a pony is still pretty good';
}
这对于在首次调用函数时进行初始化,针对所使用的浏览器进行优化是有用的模式。
递归函数
递归函数是一种在满足特定条件之前会自行调用的函数 。 当涉及迭代过程时,这是一个有用的工具。 一个常见的示例是计算数字阶乘的函数:
function factorial(n) {
if (n === 0) {
return 1;
} else {
return n * factorial(n - 1);
}
}
如果提供0
作为参数(0阶乘为1),则此函数将返回1
,否则它将通过调用自身的结果将参数乘以一个较小的参数来乘以该参数。 该函数将继续调用自身,直到最终参数为0
并返回1
。 这将导致1、2、3和所有数字的乘积直到原始参数。
来自数学世界的另一个例子是Collatz猜想 。 这个问题很容易解决,但到目前为止尚未解决。 它涉及采用任何正整数并遵循以下规则:
如果数字是偶数,则将其除以2
如果数字是奇数,则将其乘以三并加一
![](https://i-blog.csdnimg.cn/blog_migrate/9a596003f34a66fd377c57f9907620d8.png)
免费学习PHP!
全面介绍PHP和MySQL,从而实现服务器端编程的飞跃。
原价$ 11.95 您的完全免费
例如,如果我们以数字18开头,则将具有以下顺序:
18,9,28,14,7,22,11,34,17,52,26,13,40,20,10,5,16,8,4,2,1,4,2,1,…
如您所见,序列最后被卡在一个循环中,循环通过“ 4,2,1”。 Collatz猜想指出,每个正整数都会创建一个在此循环中完成的序列。 已经对所有最大5×2 numbers的数字进行了验证,但是没有证据表明所有大于此整数的整数都将继续为真。 为了测试这个猜想,我们可以编写一个函数,该函数使用递归来不断调用该函数直到其值达到1
(因为我们希望我们的函数避免最后被卡在递归循环中!):
function collatz(n, sequence=[n]) {
if (n === 1){
return `Sequence took ${sequence.length} steps. It was ${sequence}`;
}
if (n%2 === 0) {
n = n/2;
} else {
n = 3*n + 1;
}
return collatz(n,[...sequence,n]);
}
此函数将数字作为参数,以及另一个称为sequence
参数,该参数具有包含第一个参数的数组的默认值。 第二个参数仅在函数递归调用自身时使用。
函数执行的第一件事是测试以查看n
的值是否为1。如果存在,函数将返回一条消息,说明已执行了多少步骤。 如果尚未达到1,则检查n
的值是否为偶数(在这种情况下,将其除以2)或奇数,在这种情况下,其值乘以3,然后加1。然后函数调用自身,提供n
的新值和新序列作为参数。 通过将旧序列和n
的值放置在新数组中并将散布运算符应用于旧序列来构造新序列。
让我们看看数字18发生了什么:
collatz(18);
<< 'Sequence took 21 steps. It was 18,9,28,14,7,22,11,34,17,52,26,13,40,20,10,5,16,8,4,2,1'
如您所见,它需要21步,但最终以1结束。
可以使用该函数,看看是否可以找到一个大于5×2⁶⁰且不以1结尾的值-如果这样做,您将会出名!
翻译自: https://www.sitepoint.com/javascript-functions-that-define-and-rewrite-themselves/
函数重载和函数重写